Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/coyote/http11/Http11Processor.java


1   /*
2    *  Copyright 1999-2004 The Apache Software Foundation
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  
17  package org.apache.coyote.http11;
18  
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.io.InterruptedIOException;
22  import java.io.OutputStream;
23  import java.net.InetAddress;
24  import java.net.Socket;
25  import java.util.StringTokenizer;
26  import java.util.regex.Pattern;
27  import java.util.regex.PatternSyntaxException;
28  import java.security.AccessController;
29  import java.security.PrivilegedAction;
30  
31  import org.apache.coyote.ActionCode;
32  import org.apache.coyote.ActionHook;
33  import org.apache.coyote.Adapter;
34  import org.apache.coyote.Processor;
35  import org.apache.coyote.Request;
36  import org.apache.coyote.RequestInfo;
37  import org.apache.coyote.Response;
38  import org.apache.coyote.http11.filters.ChunkedInputFilter;
39  import org.apache.coyote.http11.filters.ChunkedOutputFilter;
40  import org.apache.coyote.http11.filters.GzipOutputFilter;
41  import org.apache.coyote.http11.filters.IdentityInputFilter;
42  import org.apache.coyote.http11.filters.IdentityOutputFilter;
43  import org.apache.coyote.http11.filters.SavedRequestInputFilter;
44  import org.apache.coyote.http11.filters.VoidInputFilter;
45  import org.apache.coyote.http11.filters.VoidOutputFilter;
46  import org.apache.coyote.http11.filters.BufferedInputFilter;
47  import org.apache.tomcat.util.buf.Ascii;
48  import org.apache.tomcat.util.buf.ByteChunk;
49  import org.apache.tomcat.util.buf.HexUtils;
50  import org.apache.tomcat.util.buf.MessageBytes;
51  import org.apache.tomcat.util.http.FastHttpDateFormat;
52  import org.apache.tomcat.util.http.MimeHeaders;
53  import org.apache.tomcat.util.net.PoolTcpEndpoint;
54  import org.apache.tomcat.util.net.SSLSupport;
55  import org.apache.tomcat.util.res.StringManager;
56  import org.apache.tomcat.util.threads.ThreadPool;
57  import org.apache.tomcat.util.threads.ThreadWithAttributes;
58  
59  
60  /**
61   * Processes HTTP requests.
62   *
63   * @author Remy Maucherat
64   */
65  public class Http11Processor implements Processor, ActionHook {
66  
67  
68      /**
69       * Logger.
70       */
71      protected static org.apache.commons.logging.Log log
72          = org.apache.commons.logging.LogFactory.getLog(Http11Processor.class);
73  
74      /**
75       * The string manager for this package.
76       */
77      protected static StringManager sm =
78          StringManager.getManager(Constants.Package);
79  
80  
81      // ----------------------------------------------------------- Constructors
82  
83  
84      /**
85       * Default constructor.
86       */
87      public Http11Processor() {
88          this(Constants.DEFAULT_HTTP_HEADER_BUFFER_SIZE);
89      }
90  
91  
92      public Http11Processor(int headerBufferSize) {
93  
94          request = new Request();
95          inputBuffer = new InternalInputBuffer(request, headerBufferSize);
96          request.setInputBuffer(inputBuffer);
97  
98          response = new Response();
99          response.setHook(this);
100         outputBuffer = new InternalOutputBuffer(response, headerBufferSize);
101         response.setOutputBuffer(outputBuffer);
102         request.setResponse(response);
103 
104         initializeFilters();
105 
106         // Cause loading of HexUtils
107         int foo = HexUtils.DEC[0];
108 
109     }
110 
111 
112     // ----------------------------------------------------- Instance Variables
113 
114 
115     /**
116      * Associated adapter.
117      */
118     protected Adapter adapter = null;
119 
120 
121     /**
122      * Request object.
123      */
124     protected Request request = null;
125 
126 
127     /**
128      * Response object.
129      */
130     protected Response response = null;
131 
132 
133     /**
134      * Input.
135      */
136     protected InternalInputBuffer inputBuffer = null;
137 
138 
139     /**
140      * Output.
141      */
142     protected InternalOutputBuffer outputBuffer = null;
143 
144 
145     /**
146      * State flag.
147      */
148     protected boolean started = false;
149 
150 
151     /**
152      * Error flag.
153      */
154     protected boolean error = false;
155 
156 
157     /**
158      * Keep-alive.
159      */
160     protected boolean keepAlive = true;
161 
162 
163     /**
164      * HTTP/1.1 flag.
165      */
166     protected boolean http11 = true;
167 
168 
169     /**
170      * HTTP/0.9 flag.
171      */
172     protected boolean http09 = false;
173 
174 
175     /**
176      * Content delimitator for the request (if false, the connection will
177      * be closed at the end of the request).
178      */
179     protected boolean contentDelimitation = true;
180 
181 
182     /**
183      * Is there an expectation ?
184      */
185     protected boolean expectation = false;
186 
187 
188     /**
189      * List of restricted user agents.
190      */
191     protected Pattern[] restrictedUserAgents = null;
192 
193 
194     /**
195      * Maximum number of Keep-Alive requests to honor.
196      */
197     protected int maxKeepAliveRequests = -1;
198 
199 
200     /**
201      * SSL information.
202      */
203     protected SSLSupport sslSupport;
204 
205 
206     /**
207      * Socket associated with the current connection.
208      */
209     protected Socket socket;
210 
211 
212     /**
213      * Remote Address associated with the current connection.
214      */
215     protected String remoteAddr = null;
216 
217 
218     /**
219      * Remote Host associated with the current connection.
220      */
221     protected String remoteHost = null;
222 
223 
224     /**
225      * Local Host associated with the current connection.
226      */
227     protected String localName = null;
228 
229 
230 
231     /**
232      * Local port to which the socket is connected
233      */
234     protected int localPort = -1;
235 
236 
237     /**
238      * Remote port to which the socket is connected
239      */
240     protected int remotePort = -1;
241 
242 
243     /**
244      * The local Host address.
245      */
246     protected String localAddr = null;
247 
248 
249     /**
250      * Maximum timeout on uploads. 5 minutes as in Apache HTTPD server.
251      */
252     protected int timeout = 300000;
253 
254 
255     /**
256      * Flag to disable setting a different time-out on uploads.
257      */
258     protected boolean disableUploadTimeout = false;
259 
260 
261     /**
262      * Allowed compression level.
263      */
264     protected int compressionLevel = 0;
265 
266 
267     /**
268      * Minimum contentsize to make compression.
269      */
270     protected int compressionMinSize = 2048;
271 
272 
273     /**
274      * Socket buffering.
275      */
276     protected int socketBuffer = -1;
277 
278 
279     /**
280      * Max saved post size.
281      */
282     protected int maxSavePostSize = 4 * 1024;
283 
284 
285     /**
286      * List of user agents to not use gzip with
287      */
288     protected Pattern noCompressionUserAgents[] = null;
289 
290     /**
291      * List of MIMES which could be gzipped
292      */
293     protected String[] compressableMimeTypes =
294     { "text/html", "text/xml", "text/plain" };
295 
296 
297     /**
298      * Host name (used to avoid useless B2C conversion on the host name).
299      */
300     protected char[] hostNameC = new char[0];
301 
302 
303     /**
304      * Associated thread pool.
305      */
306     protected ThreadPool threadPool;
307 
308 
309     /**
310      * Associated endpoint.
311      */
312     protected PoolTcpEndpoint endpoint;
313 
314 
315     /**
316      * Allow a customized the server header for the tin-foil hat folks.
317      */
318     protected String server = null;
319 
320 
321     // ------------------------------------------------------------- Properties
322 
323 
324     /**
325      * Return compression level.
326      */
327     public String getCompression() {
328         switch (compressionLevel) {
329         case 0:
330             return "off";
331         case 1:
332             return "on";
333         case 2:
334             return "force";
335         }
336         return "off";
337     }
338 
339 
340     /**
341      * Set compression level.
342      */
343     public void setCompression(String compression) {
344         if (compression.equals("on")) {
345             this.compressionLevel = 1;
346         } else if (compression.equals("force")) {
347             this.compressionLevel = 2;
348         } else if (compression.equals("off")) {
349             this.compressionLevel = 0;
350         } else {
351             try {
352                 // Try to parse compression as an int, which would give the
353                 // minimum compression size
354                 compressionMinSize = Integer.parseInt(compression);
355                 this.compressionLevel = 1;
356             } catch (Exception e) {
357                 this.compressionLevel = 0;
358             }
359         }
360     }
361 
362     /**
363      * Set Minimum size to trigger compression.
364      */
365     public void setCompressionMinSize(int compressionMinSize) {
366         this.compressionMinSize = compressionMinSize;
367     }
368 
369 
370     public void setThreadPool(ThreadPool threadPool) {
371         this.threadPool = threadPool;
372     }
373 
374     
375     public void setEndpoint(PoolTcpEndpoint endpoint) {
376         this.endpoint = endpoint;
377     }
378 
379 
380     /**
381      * Add user-agent for which gzip compression didn't works
382      * The user agent String given will be exactly matched
383      * to the user-agent header submitted by the client.
384      *
385      * @param userAgent user-agent string
386      */
387     public void addNoCompressionUserAgent(String userAgent) {
388         try {
389             Pattern nRule = Pattern.compile(userAgent);
390             noCompressionUserAgents =
391                 addREArray(noCompressionUserAgents, nRule);
392         } catch (PatternSyntaxException pse) {
393             log.error(sm.getString("http11processor.regexp.error", userAgent), pse);
394         }
395     }
396 
397 
398     /**
399      * Set no compression user agent list (this method is best when used with
400      * a large number of connectors, where it would be better to have all of
401      * them referenced a single array).
402      */
403     public void setNoCompressionUserAgents(Pattern[] noCompressionUserAgents) {
404         this.noCompressionUserAgents = noCompressionUserAgents;
405     }
406 
407 
408     /**
409      * Set no compression user agent list.
410      * List contains users agents separated by ',' :
411      *
412      * ie: "gorilla,desesplorer,tigrus"
413      */
414     public void setNoCompressionUserAgents(String noCompressionUserAgents) {
415         if (noCompressionUserAgents != null) {
416             StringTokenizer st = new StringTokenizer(noCompressionUserAgents, ",");
417 
418             while (st.hasMoreTokens()) {
419                 addNoCompressionUserAgent(st.nextToken().trim());
420             }
421         }
422     }
423 
424     /**
425      * Add a mime-type which will be compressable
426      * The mime-type String will be exactly matched
427      * in the response mime-type header .
428      *
429      * @param mimeType mime-type string
430      */
431     public void addCompressableMimeType(String mimeType) {
432         compressableMimeTypes =
433             addStringArray(compressableMimeTypes, mimeType);
434     }
435 
436 
437     /**
438      * Set compressable mime-type list (this method is best when used with
439      * a large number of connectors, where it would be better to have all of
440      * them referenced a single array).
441      */
442     public void setCompressableMimeTypes(String[] compressableMimeTypes) {
443         this.compressableMimeTypes = compressableMimeTypes;
444     }
445 
446 
447     /**
448      * Set compressable mime-type list
449      * List contains users agents separated by ',' :
450      *
451      * ie: "text/html,text/xml,text/plain"
452      */
453     public void setCompressableMimeTypes(String compressableMimeTypes) {
454         if (compressableMimeTypes != null) {
455             StringTokenizer st = new StringTokenizer(compressableMimeTypes, ",");
456 
457             while (st.hasMoreTokens()) {
458                 addCompressableMimeType(st.nextToken().trim());
459             }
460         }
461     }
462 
463 
464     /**
465      * Return the list of restricted user agents.
466      */
467     public String[] findCompressableMimeTypes() {
468         return (compressableMimeTypes);
469     }
470 
471 
472 
473     // --------------------------------------------------------- Public Methods
474 
475 
476     /**
477      * Add input or output filter.
478      *
479      * @param className class name of the filter
480      */
481     protected void addFilter(String className) {
482         try {
483             Class clazz = Class.forName(className);
484             Object obj = clazz.newInstance();
485             if (obj instanceof InputFilter) {
486                 inputBuffer.addFilter((InputFilter) obj);
487             } else if (obj instanceof OutputFilter) {
488                 outputBuffer.addFilter((OutputFilter) obj);
489             } else {
490                 log.warn(sm.getString("http11processor.filter.unknown", className));
491             }
492         } catch (Exception e) {
493             log.error(sm.getString("http11processor.filter.error", className), e);
494         }
495     }
496 
497 
498     /**
499      * General use method
500      *
501      * @param sArray the StringArray
502      * @param value string
503      */
504     private String[] addStringArray(String sArray[], String value) {
505         String[] result = null;
506         if (sArray == null) {
507             result = new String[1];
508             result[0] = value;
509         }
510         else {
511             result = new String[sArray.length + 1];
512             for (int i = 0; i < sArray.length; i++)
513                 result[i] = sArray[i];
514             result[sArray.length] = value;
515         }
516         return result;
517     }
518 
519 
520     /**
521      * General use method
522      *
523      * @param rArray the REArray
524      * @param value Obj
525      */
526     private Pattern[] addREArray(Pattern rArray[], Pattern value) {
527         Pattern[] result = null;
528         if (rArray == null) {
529             result = new Pattern[1];
530             result[0] = value;
531         }
532         else {
533             result = new Pattern[rArray.length + 1];
534             for (int i = 0; i < rArray.length; i++)
535                 result[i] = rArray[i];
536             result[rArray.length] = value;
537         }
538         return result;
539     }
540 
541 
542     /**
543      * General use method
544      *
545      * @param sArray the StringArray
546      * @param value string
547      */
548     private boolean inStringArray(String sArray[], String value) {
549         for (int i = 0; i < sArray.length; i++) {
550             if (sArray[i].equals(value)) {
551                 return true;
552             }
553         }
554         return false;
555     }
556 
557 
558     /**
559      * Checks if any entry in the string array starts with the specified value
560      *
561      * @param sArray the StringArray
562      * @param value string
563      */
564     private boolean startsWithStringArray(String sArray[], String value) {
565         if (value == null)
566            return false;
567         for (int i = 0; i < sArray.length; i++) {
568             if (value.startsWith(sArray[i])) {
569                 return true;
570             }
571         }
572         return false;
573     }
574 
575 
576     /**
577      * Add restricted user-agent (which will downgrade the connector
578      * to HTTP/1.0 mode). The user agent String given will be matched
579      * via regexp to the user-agent header submitted by the client.
580      *
581      * @param userAgent user-agent string
582      */
583     public void addRestrictedUserAgent(String userAgent) {
584         try {
585             Pattern nRule = Pattern.compile(userAgent);
586             restrictedUserAgents = addREArray(restrictedUserAgents, nRule);
587         } catch (PatternSyntaxException pse) {
588             log.error(sm.getString("http11processor.regexp.error", userAgent), pse);
589         }
590     }
591 
592 
593     /**
594      * Set restricted user agent list (this method is best when used with
595      * a large number of connectors, where it would be better to have all of
596      * them referenced a single array).
597      */
598     public void setRestrictedUserAgents(Pattern[] restrictedUserAgents) {
599         this.restrictedUserAgents = restrictedUserAgents;
600     }
601 
602 
603     /**
604      * Set restricted user agent list (which will downgrade the connector
605      * to HTTP/1.0 mode). List contains users agents separated by ',' :
606      *
607      * ie: "gorilla,desesplorer,tigrus"
608      */
609     public void setRestrictedUserAgents(String restrictedUserAgents) {
610         if (restrictedUserAgents != null) {
611             StringTokenizer st =
612                 new StringTokenizer(restrictedUserAgents, ",");
613             while (st.hasMoreTokens()) {
614                 addRestrictedUserAgent(st.nextToken().trim());
615             }
616         }
617     }
618 
619 
620     /**
621      * Return the list of restricted user agents.
622      */
623     public String[] findRestrictedUserAgents() {
624         String[] sarr = new String [restrictedUserAgents.length];
625 
626         for (int i = 0; i < restrictedUserAgents.length; i++)
627             sarr[i] = restrictedUserAgents[i].toString();
628 
629         return (sarr);
630     }
631 
632 
633     /**
634      * Set the maximum number of Keep-Alive requests to honor.
635      * This is to safeguard from DoS attacks.  Setting to a negative
636      * value disables the check.
637      */
638     public void setMaxKeepAliveRequests(int mkar) {
639         maxKeepAliveRequests = mkar;
640     }
641 
642 
643     /**
644      * Return the number of Keep-Alive requests that we will honor.
645      */
646     public int getMaxKeepAliveRequests() {
647         return maxKeepAliveRequests;
648     }
649 
650 
651     /**
652      * Set the maximum size of a POST which will be buffered in SSL mode.
653      */
654     public void setMaxSavePostSize(int msps) {
655         maxSavePostSize = msps;
656     }
657 
658 
659     /**
660      * Return the maximum size of a POST which will be buffered in SSL mode.
661      */
662     public int getMaxSavePostSize() {
663         return maxSavePostSize;
664     }
665 
666 
667     /**
668      * Set the SSL information for this HTTP connection.
669      */
670     public void setSSLSupport(SSLSupport sslSupport) {
671         this.sslSupport = sslSupport;
672     }
673 
674 
675     /**
676      * Set the socket associated with this HTTP connection.
677      */
678     public void setSocket(Socket socket)
679         throws IOException {
680         this.socket = socket;
681     }
682 
683     /**
684      * Set the flag to control upload time-outs.
685      */
686     public void setDisableUploadTimeout(boolean isDisabled) {
687         disableUploadTimeout = isDisabled;
688     }
689 
690     /**
691      * Get the flag that controls upload time-outs.
692      */
693     public boolean getDisableUploadTimeout() {
694         return disableUploadTimeout;
695     }
696 
697     /**
698      * Set the socket buffer flag.
699      */
700     public void setSocketBuffer(int socketBuffer) {
701         this.socketBuffer = socketBuffer;
702         outputBuffer.setSocketBuffer(socketBuffer);
703     }
704 
705     /**
706      * Get the socket buffer flag.
707      */
708     public int getSocketBuffer() {
709         return socketBuffer;
710     }
711 
712     /**
713      * Set the upload timeout.
714      */
715     public void setTimeout( int timeouts ) {
716         timeout = timeouts ;
717     }
718 
719     /**
720      * Get the upload timeout.
721      */
722     public int getTimeout() {
723         return timeout;
724     }
725 
726 
727     /**
728      * Set the server header name.
729      */
730     public void setServer( String server ) {
731         if (server==null || server.equals("")) {
732             this.server = null;
733         } else {
734             this.server = server;
735         }
736     }
737 
738     /**
739      * Get the server header name.
740      */
741     public String getServer() {
742         return server;
743     }
744 
745 
746     /** Get the request associated with this processor.
747      *
748      * @return The request
749      */
750     public Request getRequest() {
751         return request;
752     }
753 
754     /**
755      * Process pipelined HTTP requests using the specified input and output
756      * streams.
757      *
758      * @param input stream from which the HTTP requests will be read
759      * @param output stream which will be used to output the HTTP
760      * responses
761      * @throws IOException error during an I/O operation
762      */
763     public void process(InputStream input, OutputStream output)
764         throws IOException {
765         ThreadWithAttributes thrA=
766                 (ThreadWithAttributes)Thread.currentThread();
767         RequestInfo rp = request.getRequestProcessor();
768         thrA.setCurrentStage(threadPool, "parsing http request");
769         rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
770 
771         // Set the remote address
772         remoteAddr = null;
773         remoteHost = null;
774         localAddr = null;
775         localName = null;
776         remotePort = -1;
777         localPort = -1;
778 
779         // Setting up the I/O
780         inputBuffer.setInputStream(input);
781         outputBuffer.setOutputStream(output);
782 
783         // Error flag
784         error = false;
785         keepAlive = true;
786 
787         int keepAliveLeft = maxKeepAliveRequests;
788         int soTimeout = socket.getSoTimeout();
789         int oldSoTimeout = soTimeout;
790 
791         int threadRatio = 0;
792         if (threadPool.getCurrentThreadsBusy() > 0) {
793             threadRatio = (threadPool.getCurrentThreadsBusy() * 100)
794                 / threadPool.getMaxThreads();
795         } else {
796             threadRatio = (endpoint.getCurrentThreadsBusy() * 100)
797                 / endpoint.getMaxThreads();
798         }
799         if ((threadRatio > 33) && (threadRatio <= 66)) {
800             soTimeout = soTimeout / 2;
801         } else if ((threadRatio > 66) && (threadRatio <= 90)) {
802             soTimeout = soTimeout / 3;
803             keepAliveLeft = 1;
804         } else if (threadRatio > 90) {
805             soTimeout = soTimeout / 20;
806             keepAliveLeft = 1;
807         }
808         
809         if (soTimeout != oldSoTimeout) {
810             try {
811                 socket.setSoTimeout(soTimeout);
812             } catch (Throwable t) {
813                 log.debug(sm.getString("http11processor.socket.timeout"), t);
814                 error = true;
815             }
816         }
817 
818         boolean keptAlive = false;
819 
820         while (started && !error && keepAlive) {
821 
822             // Parsing the request header
823             try {
824                 if( !disableUploadTimeout && keptAlive && soTimeout > 0 ) {
825                     socket.setSoTimeout(soTimeout);
826                 }
827                 inputBuffer.parseRequestLine();
828                 request.setStartTime(System.currentTimeMillis());
829                 thrA.setParam( threadPool, request.requestURI() );
830                 keptAlive = true;
831                 if (!disableUploadTimeout) {
832                     socket.setSoTimeout(timeout);
833                 }
834                 inputBuffer.parseHeaders();
835             } catch (IOException e) {
836                 error = true;
837                 break;
838             } catch (Throwable t) {
839                 if (log.isDebugEnabled()) {
840                     log.debug(sm.getString("http11processor.header.parse"), t);
841                 }
842                 // 400 - Bad Request
843                 response.setStatus(400);
844                 error = true;
845             }
846 
847             // Setting up filters, and parse some request headers
848             thrA.setCurrentStage(threadPool, "prepareRequest");
849             rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
850             try {
851                 prepareRequest();
852             } catch (Throwable t) {
853                 if (log.isDebugEnabled()) {
854                     log.debug(sm.getString("http11processor.request.prepare"), t);
855                 }
856                 // 400 - Internal Server Error
857                 response.setStatus(400);
858                 error = true;
859             }
860 
861             if (maxKeepAliveRequests > 0 && --keepAliveLeft == 0)
862                 keepAlive = false;
863 
864             // Process the request in the adapter
865             if (!error) {
866                 try {
867                     thrA.setCurrentStage(threadPool, "service");
868                     rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
869                     adapter.service(request, response);
870                     // Handle when the response was committed before a serious
871                     // error occurred.  Throwing a ServletException should both
872                     // set the status to 500 and set the errorException.
873                     // If we fail here, then the response is likely already
874                     // committed, so we can't try and set headers.
875                     if(keepAlive && !error) { // Avoid checking twice.
876                         error = response.getErrorException() != null ||
877                                 statusDropsConnection(response.getStatus());
878                     }
879 
880                 } catch (InterruptedIOException e) {
881                     error = true;
882                 } catch (Throwable t) {
883                     log.error(sm.getString("http11processor.request.process"), t);
884                     // 500 - Internal Server Error
885                     response.setStatus(500);
886                     error = true;
887                 }
888             }
889 
890             // Finish the handling of the request
891             try {
892                 thrA.setCurrentStage(threadPool, "endRequestIB");
893                 rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);
894                 inputBuffer.endRequest();
895             } catch (IOException e) {
896                 error = true;
897             } catch (Throwable t) {
898                 log.error(sm.getString("http11processor.request.finish"), t);
899                 // 500 - Internal Server Error
900                 response.setStatus(500);
901                 error = true;
902             }
903             try {
904                 thrA.setCurrentStage(threadPool, "endRequestOB");
905                 rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);
906                 outputBuffer.endRequest();
907             } catch (IOException e) {
908                 error = true;
909             } catch (Throwable t) {
910                 log.error(sm.getString("http11processor.response.finish"), t);
911                 error = true;
912             }
913 
914             // If there was an error, make sure the request is counted as
915             // and error, and update the statistics counter
916             if (error) {
917                 response.setStatus(500);
918             }
919             request.updateCounters();
920 
921             thrA.setCurrentStage(threadPool, "ended");
922             rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
923 
924             // Don't reset the param - we'll see it as ended. Next request
925             // will reset it
926             // thrA.setParam(null);
927             // Next request
928             inputBuffer.nextRequest();
929             outputBuffer.nextRequest();
930 
931         }
932 
933         rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
934 
935         // Recycle
936         inputBuffer.recycle();
937         outputBuffer.recycle();
938 
939         // Recycle ssl info
940         sslSupport = null;
941     }
942 
943 
944     // ----------------------------------------------------- ActionHook Methods
945 
946 
947     /**
948      * Send an action to the connector.
949      *
950      * @param actionCode Type of the action
951      * @param param Action parameter
952      */
953     public void action(ActionCode actionCode, Object param) {
954 
955         if (actionCode == ActionCode.ACTION_COMMIT) {
956             // Commit current response
957 
958             if (response.isCommitted())
959                 return;
960 
961             // Validate and write response headers
962             prepareResponse();
963             try {
964                 outputBuffer.commit();
965             } catch (IOException e) {
966                 // Set error flag
967                 error = true;
968             }
969 
970         } else if (actionCode == ActionCode.ACTION_ACK) {
971 
972             // Acknowlege request
973 
974             // Send a 100 status back if it makes sense (response not committed
975             // yet, and client specified an expectation for 100-continue)
976 
977             if ((response.isCommitted()) || !expectation)
978                 return;
979 
980             inputBuffer.setSwallowInput(true);
981             try {
982                 outputBuffer.sendAck();
983             } catch (IOException e) {
984                 // Set error flag
985                 error = true;
986             }
987 
988         } else if (actionCode == ActionCode.ACTION_CLIENT_FLUSH) {
989 
990             try {
991                 outputBuffer.flush();
992             } catch (IOException e) {
993                 // Set error flag
994                 error = true;
995                 response.setErrorException(e);
996             }
997 
998         } else if (actionCode == ActionCode.ACTION_CLOSE) {
999             // Close
1000
1001            // End the processing of the current request, and stop any further
1002            // transactions with the client
1003
1004            try {
1005                outputBuffer.endRequest();
1006            } catch (IOException e) {
1007                // Set error flag
1008                error = true;
1009            }
1010
1011        } else if (actionCode == ActionCode.ACTION_RESET) {
1012
1013            // Reset response
1014
1015            // Note: This must be called before the response is committed
1016
1017            outputBuffer.reset();
1018
1019        } else if (actionCode == ActionCode.ACTION_CUSTOM) {
1020
1021            // Do nothing
1022
1023        } else if (actionCode == ActionCode.ACTION_START) {
1024
1025            started = true;
1026
1027        } else if (actionCode == ActionCode.ACTION_STOP) {
1028
1029            started = false;
1030
1031        } else if (actionCode == ActionCode.ACTION_REQ_SSL_ATTRIBUTE ) {
1032
1033            try {
1034                if (sslSupport != null) {
1035                    Object sslO = sslSupport.getCipherSuite();
1036                    if (sslO != null)
1037                        request.setAttribute
1038                            (SSLSupport.CIPHER_SUITE_KEY, sslO);
1039                    sslO = sslSupport.getPeerCertificateChain(false);
1040                    if (sslO != null)
1041                        request.setAttribute
1042                            (SSLSupport.CERTIFICATE_KEY, sslO);
1043                    sslO = sslSupport.getKeySize();
1044                    if (sslO != null)
1045                        request.setAttribute
1046                            (SSLSupport.KEY_SIZE_KEY, sslO);
1047                    sslO = sslSupport.getSessionId();
1048                    if (sslO != null)
1049                        request.setAttribute
1050                            (SSLSupport.SESSION_ID_KEY, sslO);
1051                }
1052            } catch (Exception e) {
1053                log.warn(sm.getString("http11processor.socket.ssl"), e);
1054            }
1055
1056        } else if (actionCode == ActionCode.ACTION_REQ_HOST_ADDR_ATTRIBUTE) {
1057
1058            if ((remoteAddr == null) && (socket != null)) {
1059                InetAddress inetAddr = socket.getInetAddress();
1060                if (inetAddr != null) {
1061                    remoteAddr = inetAddr.getHostAddress();
1062                }
1063            }
1064            request.remoteAddr().setString(remoteAddr);
1065
1066        } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_NAME_ATTRIBUTE) {
1067
1068            if ((localName == null) && (socket != null)) {
1069                InetAddress inetAddr = socket.getLocalAddress();
1070                if (inetAddr != null) {
1071                    localName = inetAddr.getHostName();
1072                }
1073            }
1074            request.localName().setString(localName);
1075
1076        } else if (actionCode == ActionCode.ACTION_REQ_HOST_ATTRIBUTE) {
1077
1078            if ((remoteHost == null) && (socket != null)) {
1079                InetAddress inetAddr = socket.getInetAddress();
1080                if (inetAddr != null) {
1081                    remoteHost = inetAddr.getHostName();
1082                }
1083            }
1084            request.remoteHost().setString(remoteHost);
1085
1086        } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_ADDR_ATTRIBUTE) {
1087
1088            if (localAddr == null)
1089               localAddr = socket.getLocalAddress().getHostAddress();
1090
1091            request.localAddr().setString(localAddr);
1092
1093        } else if (actionCode == ActionCode.ACTION_REQ_REMOTEPORT_ATTRIBUTE) {
1094
1095            if ((remotePort == -1 ) && (socket !=null)) {
1096                remotePort = socket.getPort();
1097            }
1098            request.setRemotePort(remotePort);
1099
1100        } else if (actionCode == ActionCode.ACTION_REQ_LOCALPORT_ATTRIBUTE) {
1101
1102            if ((localPort == -1 ) && (socket !=null)) {
1103                localPort = socket.getLocalPort();
1104            }
1105            request.setLocalPort(localPort);
1106
1107        } else if (actionCode == ActionCode.ACTION_REQ_SSL_CERTIFICATE) {
1108            if( sslSupport != null) {
1109                /*
1110                 * Consume and buffer the request body, so that it does not
1111                 * interfere with the client's handshake messages
1112                 */
1113                InputFilter[] inputFilters = inputBuffer.getFilters();
1114                ((BufferedInputFilter) inputFilters[Constants.BUFFERED_FILTER])
1115                    .setLimit(maxSavePostSize);
1116                inputBuffer.addActiveFilter
1117                    (inputFilters[Constants.BUFFERED_FILTER]);
1118                try {
1119                    Object sslO = sslSupport.getPeerCertificateChain(true);
1120                    if( sslO != null) {
1121                        request.setAttribute
1122                            (SSLSupport.CERTIFICATE_KEY, sslO);
1123                    }
1124                } catch (Exception e) {
1125                    log.warn(sm.getString("http11processor.socket.ssl"), e);
1126                }
1127            }
1128        } else if (actionCode == ActionCode.ACTION_REQ_SET_BODY_REPLAY) {
1129            ByteChunk body = (ByteChunk) param;
1130            
1131            InputFilter savedBody = new SavedRequestInputFilter(body);
1132            savedBody.setRequest(request);
1133
1134            InternalInputBuffer internalBuffer = (InternalInputBuffer)
1135                request.getInputBuffer();
1136            internalBuffer.addActiveFilter(savedBody);
1137        }
1138
1139    }
1140
1141
1142    // ------------------------------------------------------ Connector Methods
1143
1144
1145    /**
1146     * Set the associated adapter.
1147     *
1148     * @param adapter the new adapter
1149     */
1150    public void setAdapter(Adapter adapter) {
1151        this.adapter = adapter;
1152    }
1153
1154
1155    /**
1156     * Get the associated adapter.
1157     *
1158     * @return the associated adapter
1159     */
1160    public Adapter getAdapter() {
1161        return adapter;
1162    }
1163
1164
1165    // ------------------------------------------------------ Protected Methods
1166
1167
1168    /**
1169     * After reading the request headers, we have to setup the request filters.
1170     */
1171    protected void prepareRequest() {
1172
1173        http11 = true;
1174        http09 = false;
1175        contentDelimitation = false;
1176        expectation = false;
1177        if (sslSupport != null) {
1178            request.scheme().setString("https");
1179        }
1180        MessageBytes protocolMB = request.protocol();
1181        if (protocolMB.equals(Constants.HTTP_11)) {
1182            http11 = true;
1183            protocolMB.setString(Constants.HTTP_11);
1184        } else if (protocolMB.equals(Constants.HTTP_10)) {
1185            http11 = false;
1186            keepAlive = false;
1187            protocolMB.setString(Constants.HTTP_10);
1188        } else if (protocolMB.equals("")) {
1189            // HTTP/0.9
1190            http09 = true;
1191            http11 = false;
1192            keepAlive = false;
1193        } else {
1194            // Unsupported protocol
1195            http11 = false;
1196            error = true;
1197            // Send 505; Unsupported HTTP version
1198            response.setStatus(505);
1199        }
1200
1201        MessageBytes methodMB = request.method();
1202        if (methodMB.equals(Constants.GET)) {
1203            methodMB.setString(Constants.GET);
1204        } else if (methodMB.equals(Constants.POST)) {
1205            methodMB.setString(Constants.POST);
1206        }
1207
1208        MimeHeaders headers = request.getMimeHeaders();
1209
1210        // Check connection header
1211        MessageBytes connectionValueMB = headers.getValue("connection");
1212        if (connectionValueMB != null) {
1213            ByteChunk connectionValueBC = connectionValueMB.getByteChunk();
1214            if (findBytes(connectionValueBC, Constants.CLOSE_BYTES) != -1) {
1215                keepAlive = false;
1216            } else if (findBytes(connectionValueBC,
1217                                 Constants.KEEPALIVE_BYTES) != -1) {
1218                keepAlive = true;
1219            }
1220        }
1221
1222        MessageBytes expectMB = null;
1223        if (http11)
1224            expectMB = headers.getValue("expect");
1225        if ((expectMB != null)
1226            && (expectMB.indexOfIgnoreCase("100-continue", 0) != -1)) {
1227            inputBuffer.setSwallowInput(false);
1228            expectation = true;
1229        }
1230
1231        // Check user-agent header
1232        if ((restrictedUserAgents != null) && ((http11) || (keepAlive))) {
1233            MessageBytes userAgentValueMB = headers.getValue("user-agent");
1234            // Check in the restricted list, and adjust the http11
1235            // and keepAlive flags accordingly
1236            if(userAgentValueMB != null) {
1237                String userAgentValue = userAgentValueMB.toString();
1238                for (int i = 0; i < restrictedUserAgents.length; i++) {
1239                    if (restrictedUserAgents[i].matcher(userAgentValue).matches()) {
1240                        http11 = false;
1241                        keepAlive = false;
1242                        break;
1243                    }
1244                }
1245            }
1246        }
1247
1248        // Check for a full URI (including protocol://host:port/)
1249        ByteChunk uriBC = request.requestURI().getByteChunk();
1250        if (uriBC.startsWithIgnoreCase("http", 0)) {
1251
1252            int pos = uriBC.indexOf("://", 0, 3, 4);
1253            int uriBCStart = uriBC.getStart();
1254            int slashPos = -1;
1255            if (pos != -1) {
1256                byte[] uriB = uriBC.getBytes();
1257                slashPos = uriBC.indexOf('/', pos + 3);
1258                if (slashPos == -1) {
1259                    slashPos = uriBC.getLength();
1260                    // Set URI as "/"
1261                    request.requestURI().setBytes
1262                        (uriB, uriBCStart + pos + 1, 1);
1263                } else {
1264                    request.requestURI().setBytes
1265                        (uriB, uriBCStart + slashPos,
1266                         uriBC.getLength() - slashPos);
1267                }
1268                MessageBytes hostMB = headers.setValue("host");
1269                hostMB.setBytes(uriB, uriBCStart + pos + 3,
1270                                slashPos - pos - 3);
1271            }
1272
1273        }
1274
1275        // Input filter setup
1276        InputFilter[] inputFilters = inputBuffer.getFilters();
1277
1278        // Parse transfer-encoding header
1279        MessageBytes transferEncodingValueMB = null;
1280        if (http11)
1281            transferEncodingValueMB = headers.getValue("transfer-encoding");
1282        if (transferEncodingValueMB != null) {
1283            String transferEncodingValue = transferEncodingValueMB.toString();
1284            // Parse the comma separated list. "identity" codings are ignored
1285            int startPos = 0;
1286            int commaPos = transferEncodingValue.indexOf(',');
1287            String encodingName = null;
1288            while (commaPos != -1) {
1289                encodingName = transferEncodingValue.substring
1290                    (startPos, commaPos).toLowerCase().trim();
1291                if (!addInputFilter(inputFilters, encodingName)) {
1292                    // Unsupported transfer encoding
1293                    error = true;
1294                    // 501 - Unimplemented
1295                    response.setStatus(501);
1296                }
1297                startPos = commaPos + 1;
1298                commaPos = transferEncodingValue.indexOf(',', startPos);
1299            }
1300            encodingName = transferEncodingValue.substring(startPos)
1301                .toLowerCase().trim();
1302            if (!addInputFilter(inputFilters, encodingName)) {
1303                // Unsupported transfer encoding
1304                error = true;
1305                // 501 - Unimplemented
1306                response.setStatus(501);
1307            }
1308        }
1309
1310        // Parse content-length header
1311        long contentLength = request.getContentLengthLong();
1312        if (contentLength >= 0 && !contentDelimitation) {
1313            inputBuffer.addActiveFilter
1314                (inputFilters[Constants.IDENTITY_FILTER]);
1315            contentDelimitation = true;
1316        }
1317
1318        MessageBytes valueMB = headers.getValue("host");
1319
1320        // Check host header
1321        if (http11 && (valueMB == null)) {
1322            error = true;
1323            // 400 - Bad request
1324            response.setStatus(400);
1325        }
1326
1327        parseHost(valueMB);
1328
1329        if (!contentDelimitation) {
1330            // If there's no content length and we're using keep-alive
1331            // (HTTP/1.0 with keep-alive or HTTP/1.1), assume
1332            // the client is not broken and didn't send a body
1333            if (keepAlive) {
1334                inputBuffer.addActiveFilter
1335                    (inputFilters[Constants.VOID_FILTER]);
1336                contentDelimitation = true;
1337            }
1338        }
1339
1340        if (!contentDelimitation)
1341            keepAlive = false;
1342
1343    }
1344
1345
1346    /**
1347     * Parse host.
1348     */
1349    public void parseHost(MessageBytes valueMB) {
1350
1351        if (valueMB == null || valueMB.isNull()) {
1352            // HTTP/1.0
1353            // Default is what the socket tells us. Overriden if a host is
1354            // found/parsed
1355            request.setServerPort(socket.getLocalPort());
1356            InetAddress localAddress = socket.getLocalAddress();
1357            // Setting the socket-related fields. The adapter doesn't know
1358            // about socket.
1359            request.serverName().setString(localAddress.getHostName());
1360            return;
1361        }
1362
1363        ByteChunk valueBC = valueMB.getByteChunk();
1364        byte[] valueB = valueBC.getBytes();
1365        int valueL = valueBC.getLength();
1366        int valueS = valueBC.getStart();
1367        int colonPos = -1;
1368        if (hostNameC.length < valueL) {
1369            hostNameC = new char[valueL];
1370        }
1371
1372        boolean ipv6 = (valueB[valueS] == '[');
1373        boolean bracketClosed = false;
1374        for (int i = 0; i < valueL; i++) {
1375            char b = (char) valueB[i + valueS];
1376            hostNameC[i] = b;
1377            if (b == ']') {
1378                bracketClosed = true;
1379            } else if (b == ':') {
1380                if (!ipv6 || bracketClosed) {
1381                    colonPos = i;
1382                    break;
1383                }
1384            }
1385        }
1386
1387        if (colonPos < 0) {
1388            if (sslSupport == null) {
1389                // 80 - Default HTTP port
1390                request.setServerPort(80);
1391            } else {
1392                // 443 - Default HTTPS port
1393                request.setServerPort(443);
1394            }
1395            request.serverName().setChars(hostNameC, 0, valueL);
1396        } else {
1397
1398            request.serverName().setChars(hostNameC, 0, colonPos);
1399
1400            int port = 0;
1401            int mult = 1;
1402            for (int i = valueL - 1; i > colonPos; i--) {
1403                int charValue = HexUtils.DEC[(int) valueB[i + valueS]];
1404                if (charValue == -1) {
1405                    // Invalid character
1406                    error = true;
1407                    // 400 - Bad request
1408                    response.setStatus(400);
1409                    break;
1410                }
1411                port = port + (charValue * mult);
1412                mult = 10 * mult;
1413            }
1414            request.setServerPort(port);
1415
1416        }
1417
1418    }
1419
1420
1421    /**
1422     * Check for compression
1423     */
1424    private boolean isCompressable() {
1425
1426        // Nope Compression could works in HTTP 1.0 also
1427        // cf: mod_deflate
1428
1429        // Compression only since HTTP 1.1
1430        // if (! http11)
1431        //    return false;
1432
1433        // Check if browser support gzip encoding
1434        MessageBytes acceptEncodingMB =
1435            request.getMimeHeaders().getValue("accept-encoding");
1436
1437        if ((acceptEncodingMB == null)
1438            || (acceptEncodingMB.indexOf("gzip") == -1))
1439            return false;
1440
1441        // Check if content is not allready gzipped
1442        MessageBytes contentEncodingMB =
1443            response.getMimeHeaders().getValue("Content-Encoding");
1444
1445        if ((contentEncodingMB != null)
1446            && (contentEncodingMB.indexOf("gzip") != -1))
1447            return false;
1448
1449        // If force mode, allways compress (test purposes only)
1450        if (compressionLevel == 2)
1451           return true;
1452
1453        // Check for incompatible Browser
1454        if (noCompressionUserAgents != null) {
1455            MessageBytes userAgentValueMB =
1456                request.getMimeHeaders().getValue("user-agent");
1457            if(userAgentValueMB != null) {
1458                String userAgentValue = userAgentValueMB.toString();
1459
1460                // If one Regexp rule match, disable compression
1461                for (int i = 0; i < noCompressionUserAgents.length; i++)
1462                    if (noCompressionUserAgents[i].matcher(userAgentValue).matches())
1463                        return false;
1464            }
1465        }
1466
1467        // Check if suffisant len to trig the compression
1468        long contentLength = response.getContentLengthLong();
1469        if ((contentLength == -1)
1470            || (contentLength > compressionMinSize)) {
1471            // Check for compatible MIME-TYPE
1472            if (compressableMimeTypes != null) {
1473                return (startsWithStringArray(compressableMimeTypes,
1474                                              response.getContentType()));
1475            }
1476        }
1477
1478        return false;
1479    }
1480
1481
1482    /**
1483     * When committing the response, we have to validate the set of headers, as
1484     * well as setup the response filters.
1485     */
1486    protected void prepareResponse() {
1487
1488        boolean entityBody = true;
1489        contentDelimitation = false;
1490
1491        OutputFilter[] outputFilters = outputBuffer.getFilters();
1492
1493        if (http09 == true) {
1494            // HTTP/0.9
1495            outputBuffer.addActiveFilter
1496                (outputFilters[Constant