KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > coyote > http11 > Http11AprProcessor


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 package org.apache.coyote.http11;
19
20 import java.io.ByteArrayInputStream JavaDoc;
21 import java.io.IOException JavaDoc;
22 import java.io.InterruptedIOException JavaDoc;
23 import java.util.StringTokenizer JavaDoc;
24 import java.util.regex.Pattern JavaDoc;
25 import java.util.regex.PatternSyntaxException JavaDoc;
26 import java.security.cert.CertificateFactory JavaDoc;
27 import java.security.cert.X509Certificate JavaDoc;
28
29 import org.apache.coyote.ActionCode;
30 import org.apache.coyote.ActionHook;
31 import org.apache.coyote.Adapter;
32 import org.apache.coyote.Request;
33 import org.apache.coyote.RequestInfo;
34 import org.apache.coyote.Response;
35 import org.apache.coyote.http11.filters.ChunkedInputFilter;
36 import org.apache.coyote.http11.filters.ChunkedOutputFilter;
37 import org.apache.coyote.http11.filters.GzipOutputFilter;
38 import org.apache.coyote.http11.filters.IdentityInputFilter;
39 import org.apache.coyote.http11.filters.IdentityOutputFilter;
40 import org.apache.coyote.http11.filters.SavedRequestInputFilter;
41 import org.apache.coyote.http11.filters.VoidInputFilter;
42 import org.apache.coyote.http11.filters.VoidOutputFilter;
43 import org.apache.coyote.http11.filters.BufferedInputFilter;
44 import org.apache.tomcat.jni.Address;
45 import org.apache.tomcat.jni.SSL;
46 import org.apache.tomcat.jni.SSLSocket;
47 import org.apache.tomcat.jni.Sockaddr;
48 import org.apache.tomcat.jni.Socket;
49 import org.apache.tomcat.util.buf.Ascii;
50 import org.apache.tomcat.util.buf.ByteChunk;
51 import org.apache.tomcat.util.buf.HexUtils;
52 import org.apache.tomcat.util.buf.MessageBytes;
53 import org.apache.tomcat.util.http.FastHttpDateFormat;
54 import org.apache.tomcat.util.http.MimeHeaders;
55 import org.apache.tomcat.util.net.AprEndpoint;
56 import org.apache.tomcat.util.net.SocketStatus;
57 import org.apache.tomcat.util.net.AprEndpoint.Handler.SocketState;
58 import org.apache.tomcat.util.res.StringManager;
59
60
61 /**
62  * Processes HTTP requests.
63  *
64  * @author Remy Maucherat
65  */

66 public class Http11AprProcessor implements ActionHook {
67
68
69     /**
70      * Logger.
71      */

72     protected static org.apache.commons.logging.Log log
73         = org.apache.commons.logging.LogFactory.getLog(Http11AprProcessor.class);
74
75     /**
76      * The string manager for this package.
77      */

78     protected static StringManager sm =
79         StringManager.getManager(Constants.Package);
80
81
82     // ----------------------------------------------------------- Constructors
83

84
85     public Http11AprProcessor(int headerBufferSize, AprEndpoint endpoint) {
86
87         this.endpoint = endpoint;
88         
89         request = new Request();
90         int readTimeout = endpoint.getFirstReadTimeout();
91         if (readTimeout == 0) {
92             readTimeout = 100;
93         } else if (readTimeout < 0) {
94             readTimeout = -1;
95         }
96         inputBuffer = new InternalAprInputBuffer(request, headerBufferSize,
97                 readTimeout);
98         request.setInputBuffer(inputBuffer);
99
100         response = new Response();
101         response.setHook(this);
102         outputBuffer = new InternalAprOutputBuffer(response, headerBufferSize);
103         response.setOutputBuffer(outputBuffer);
104         request.setResponse(response);
105         
106         ssl = endpoint.isSSLEnabled();
107
108         initializeFilters();
109
110         // Cause loading of HexUtils
111
int foo = HexUtils.DEC[0];
112
113         // Cause loading of FastHttpDateFormat
114
FastHttpDateFormat.getCurrentDate();
115
116     }
117
118
119     // ----------------------------------------------------- Instance Variables
120

121
122     /**
123      * Associated adapter.
124      */

125     protected Adapter adapter = null;
126
127
128     /**
129      * Request object.
130      */

131     protected Request request = null;
132
133
134     /**
135      * Response object.
136      */

137     protected Response response = null;
138
139
140     /**
141      * Input.
142      */

143     protected InternalAprInputBuffer inputBuffer = null;
144
145
146     /**
147      * Output.
148      */

149     protected InternalAprOutputBuffer outputBuffer = null;
150
151
152     /**
153      * Error flag.
154      */

155     protected boolean error = false;
156
157
158     /**
159      * Keep-alive.
160      */

161     protected boolean keepAlive = true;
162
163
164     /**
165      * HTTP/1.1 flag.
166      */

167     protected boolean http11 = true;
168
169
170     /**
171      * HTTP/0.9 flag.
172      */

173     protected boolean http09 = false;
174
175
176     /**
177      * Sendfile data.
178      */

179     protected AprEndpoint.SendfileData sendfileData = null;
180
181
182     /**
183      * Comet used.
184      */

185     protected boolean comet = false;
186
187
188     /**
189      * Content delimitator for the request (if false, the connection will
190      * be closed at the end of the request).
191      */

192     protected boolean contentDelimitation = true;
193
194
195     /**
196      * Is there an expectation ?
197      */

198     protected boolean expectation = false;
199
200
201     /**
202      * List of restricted user agents.
203      */

204     protected Pattern JavaDoc[] restrictedUserAgents = null;
205
206
207     /**
208      * Maximum number of Keep-Alive requests to honor.
209      */

210     protected int maxKeepAliveRequests = -1;
211
212
213     /**
214      * SSL enabled ?
215      */

216     protected boolean ssl = false;
217     
218
219     /**
220      * Socket associated with the current connection.
221      */

222     protected long socket = 0;
223
224
225     /**
226      * Remote Address associated with the current connection.
227      */

228     protected String JavaDoc remoteAddr = null;
229
230
231     /**
232      * Remote Host associated with the current connection.
233      */

234     protected String JavaDoc remoteHost = null;
235
236
237     /**
238      * Local Host associated with the current connection.
239      */

240     protected String JavaDoc localName = null;
241
242
243
244     /**
245      * Local port to which the socket is connected
246      */

247     protected int localPort = -1;
248
249
250     /**
251      * Remote port to which the socket is connected
252      */

253     protected int remotePort = -1;
254
255
256     /**
257      * The local Host address.
258      */

259     protected String JavaDoc localAddr = null;
260
261
262     /**
263      * Maximum timeout on uploads. 5 minutes as in Apache HTTPD server.
264      */

265     protected int timeout = 300000;
266
267
268     /**
269      * Flag to disable setting a different time-out on uploads.
270      */

271     protected boolean disableUploadTimeout = false;
272
273
274     /**
275      * Allowed compression level.
276      */

277     protected int compressionLevel = 0;
278
279
280     /**
281      * Minimum contentsize to make compression.
282      */

283     protected int compressionMinSize = 2048;
284
285
286     /**
287      * Socket buffering.
288      */

289     protected int socketBuffer = -1;
290
291
292     /**
293      * Max save post size.
294      */

295     protected int maxSavePostSize = 4 * 1024;
296
297
298     /**
299      * List of user agents to not use gzip with
300      */

301     protected Pattern JavaDoc noCompressionUserAgents[] = null;
302
303     /**
304      * List of MIMES which could be gzipped
305      */

306     protected String JavaDoc[] compressableMimeTypes =
307     { "text/html", "text/xml", "text/plain" };
308
309
310     /**
311      * Host name (used to avoid useless B2C conversion on the host name).
312      */

313     protected char[] hostNameC = new char[0];
314
315
316     /**
317      * Associated endpoint.
318      */

319     protected AprEndpoint endpoint;
320
321
322     /**
323      * Allow a customized the server header for the tin-foil hat folks.
324      */

325     protected String JavaDoc server = null;
326
327
328     // ------------------------------------------------------------- Properties
329

330
331     /**
332      * Return compression level.
333      */

334     public String JavaDoc getCompression() {
335         switch (compressionLevel) {
336         case 0:
337             return "off";
338         case 1:
339             return "on";
340         case 2:
341             return "force";
342         }
343         return "off";
344     }
345
346
347     /**
348      * Set compression level.
349      */

350     public void setCompression(String JavaDoc compression) {
351         if (compression.equals("on")) {
352             this.compressionLevel = 1;
353         } else if (compression.equals("force")) {
354             this.compressionLevel = 2;
355         } else if (compression.equals("off")) {
356             this.compressionLevel = 0;
357         } else {
358             try {
359                 // Try to parse compression as an int, which would give the
360
// minimum compression size
361
compressionMinSize = Integer.parseInt(compression);
362                 this.compressionLevel = 1;
363             } catch (Exception JavaDoc e) {
364                 this.compressionLevel = 0;
365             }
366         }
367     }
368
369     /**
370      * Set Minimum size to trigger compression.
371      */

372     public void setCompressionMinSize(int compressionMinSize) {
373         this.compressionMinSize = compressionMinSize;
374     }
375
376
377     /**
378      * Add user-agent for which gzip compression didn't works
379      * The user agent String given will be exactly matched
380      * to the user-agent header submitted by the client.
381      *
382      * @param userAgent user-agent string
383      */

384     public void addNoCompressionUserAgent(String JavaDoc userAgent) {
385         try {
386             Pattern JavaDoc nRule = Pattern.compile(userAgent);
387             noCompressionUserAgents =
388                 addREArray(noCompressionUserAgents, nRule);
389         } catch (PatternSyntaxException JavaDoc pse) {
390             log.error(sm.getString("http11processor.regexp.error", userAgent), pse);
391         }
392     }
393
394
395     /**
396      * Set no compression user agent list (this method is best when used with
397      * a large number of connectors, where it would be better to have all of
398      * them referenced a single array).
399      */

400     public void setNoCompressionUserAgents(Pattern JavaDoc[] noCompressionUserAgents) {
401         this.noCompressionUserAgents = noCompressionUserAgents;
402     }
403
404
405     /**
406      * Set no compression user agent list.
407      * List contains users agents separated by ',' :
408      *
409      * ie: "gorilla,desesplorer,tigrus"
410      */

411     public void setNoCompressionUserAgents(String JavaDoc noCompressionUserAgents) {
412         if (noCompressionUserAgents != null) {
413             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(noCompressionUserAgents, ",");
414
415             while (st.hasMoreTokens()) {
416                 addNoCompressionUserAgent(st.nextToken().trim());
417             }
418         }
419     }
420
421     /**
422      * Add a mime-type which will be compressable
423      * The mime-type String will be exactly matched
424      * in the response mime-type header .
425      *
426      * @param mimeType mime-type string
427      */

428     public void addCompressableMimeType(String JavaDoc mimeType) {
429         compressableMimeTypes =
430             addStringArray(compressableMimeTypes, mimeType);
431     }
432
433
434     /**
435      * Set compressable mime-type list (this method is best when used with
436      * a large number of connectors, where it would be better to have all of
437      * them referenced a single array).
438      */

439     public void setCompressableMimeTypes(String JavaDoc[] compressableMimeTypes) {
440         this.compressableMimeTypes = compressableMimeTypes;
441     }
442
443
444     /**
445      * Set compressable mime-type list
446      * List contains users agents separated by ',' :
447      *
448      * ie: "text/html,text/xml,text/plain"
449      */

450     public void setCompressableMimeTypes(String JavaDoc compressableMimeTypes) {
451         if (compressableMimeTypes != null) {
452             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(compressableMimeTypes, ",");
453
454             while (st.hasMoreTokens()) {
455                 addCompressableMimeType(st.nextToken().trim());
456             }
457         }
458     }
459
460
461     /**
462      * Return the list of restricted user agents.
463      */

464     public String JavaDoc[] findCompressableMimeTypes() {
465         return (compressableMimeTypes);
466     }
467
468
469
470     // --------------------------------------------------------- Public Methods
471

472
473     /**
474      * Add input or output filter.
475      *
476      * @param className class name of the filter
477      */

478     protected void addFilter(String JavaDoc className) {
479         try {
480             Class JavaDoc clazz = Class.forName(className);
481             Object JavaDoc obj = clazz.newInstance();
482             if (obj instanceof InputFilter) {
483                 inputBuffer.addFilter((InputFilter) obj);
484             } else if (obj instanceof OutputFilter) {
485                 outputBuffer.addFilter((OutputFilter) obj);
486             } else {
487                 log.warn(sm.getString("http11processor.filter.unknown", className));
488             }
489         } catch (Exception JavaDoc e) {
490             log.error(sm.getString("http11processor.filter.error", className), e);
491         }
492     }
493
494
495     /**
496      * General use method
497      *
498      * @param sArray the StringArray
499      * @param value string
500      */

501     private String JavaDoc[] addStringArray(String JavaDoc sArray[], String JavaDoc value) {
502         String JavaDoc[] result = null;
503         if (sArray == null) {
504             result = new String JavaDoc[1];
505             result[0] = value;
506         }
507         else {
508             result = new String JavaDoc[sArray.length + 1];
509             for (int i = 0; i < sArray.length; i++)
510                 result[i] = sArray[i];
511             result[sArray.length] = value;
512         }
513         return result;
514     }
515
516
517     /**
518      * General use method
519      *
520      * @param rArray the REArray
521      * @param value Obj
522      */

523     private Pattern JavaDoc[] addREArray(Pattern JavaDoc rArray[], Pattern JavaDoc value) {
524         Pattern JavaDoc[] result = null;
525         if (rArray == null) {
526             result = new Pattern JavaDoc[1];
527             result[0] = value;
528         }
529         else {
530             result = new Pattern JavaDoc[rArray.length + 1];
531             for (int i = 0; i < rArray.length; i++)
532                 result[i] = rArray[i];
533             result[rArray.length] = value;
534         }
535         return result;
536     }
537
538
539     /**
540      * General use method
541      *
542      * @param sArray the StringArray
543      * @param value string
544      */

545     private boolean inStringArray(String JavaDoc sArray[], String JavaDoc value) {
546         for (int i = 0; i < sArray.length; i++) {
547             if (sArray[i].equals(value)) {
548                 return true;
549             }
550         }
551         return false;
552     }
553
554
555     /**
556      * Checks if any entry in the string array starts with the specified value
557      *
558      * @param sArray the StringArray
559      * @param value string
560      */

561     private boolean startsWithStringArray(String JavaDoc sArray[], String JavaDoc value) {
562         if (value == null)
563            return false;
564         for (int i = 0; i < sArray.length; i++) {
565             if (value.startsWith(sArray[i])) {
566                 return true;
567             }
568         }
569         return false;
570     }
571
572
573     /**
574      * Add restricted user-agent (which will downgrade the connector
575      * to HTTP/1.0 mode). The user agent String given will be matched
576      * via regexp to the user-agent header submitted by the client.
577      *
578      * @param userAgent user-agent string
579      */

580     public void addRestrictedUserAgent(String JavaDoc userAgent) {
581         try {
582             Pattern JavaDoc nRule = Pattern.compile(userAgent);
583             restrictedUserAgents = addREArray(restrictedUserAgents, nRule);
584         } catch (PatternSyntaxException JavaDoc pse) {
585             log.error(sm.getString("http11processor.regexp.error", userAgent), pse);
586         }
587     }
588
589
590     /**
591      * Set restricted user agent list (this method is best when used with
592      * a large number of connectors, where it would be better to have all of
593      * them referenced a single array).
594      */

595     public void setRestrictedUserAgents(Pattern JavaDoc[] restrictedUserAgents) {
596         this.restrictedUserAgents = restrictedUserAgents;
597     }
598
599
600     /**
601      * Set restricted user agent list (which will downgrade the connector
602      * to HTTP/1.0 mode). List contains users agents separated by ',' :
603      *
604      * ie: "gorilla,desesplorer,tigrus"
605      */

606     public void setRestrictedUserAgents(String JavaDoc restrictedUserAgents) {
607         if (restrictedUserAgents != null) {
608             StringTokenizer JavaDoc st =
609                 new StringTokenizer JavaDoc(restrictedUserAgents, ",");
610             while (st.hasMoreTokens()) {
611                 addRestrictedUserAgent(st.nextToken().trim());
612             }
613         }
614     }
615
616
617     /**
618      * Return the list of restricted user agents.
619      */

620     public String JavaDoc[] findRestrictedUserAgents() {
621         String JavaDoc[] sarr = new String JavaDoc [restrictedUserAgents.length];
622
623         for (int i = 0; i < restrictedUserAgents.length; i++)
624             sarr[i] = restrictedUserAgents[i].toString();
625
626         return (sarr);
627     }
628
629
630     /**
631      * Set the maximum number of Keep-Alive requests to honor.
632      * This is to safeguard from DoS attacks. Setting to a negative
633      * value disables the check.
634      */

635     public void setMaxKeepAliveRequests(int mkar) {
636         maxKeepAliveRequests = mkar;
637     }
638
639
640     /**
641      * Return the number of Keep-Alive requests that we will honor.
642      */

643     public int getMaxKeepAliveRequests() {
644         return maxKeepAliveRequests;
645     }
646
647
648     /**
649      * Set the maximum size of a POST which will be buffered in SSL mode.
650      */

651     public void setMaxSavePostSize(int msps) {
652         maxSavePostSize = msps;
653     }
654
655
656     /**
657      * Return the maximum size of a POST which will be buffered in SSL mode.
658      */

659     public int getMaxSavePostSize() {
660         return maxSavePostSize;
661     }
662
663
664     /**
665      * Set the flag to control upload time-outs.
666      */

667     public void setDisableUploadTimeout(boolean isDisabled) {
668         disableUploadTimeout = isDisabled;
669     }
670
671     /**
672      * Get the flag that controls upload time-outs.
673      */

674     public boolean getDisableUploadTimeout() {
675         return disableUploadTimeout;
676     }
677
678     /**
679      * Set the socket buffer flag.
680      */

681     public void setSocketBuffer(int socketBuffer) {
682         this.socketBuffer = socketBuffer;
683         outputBuffer.setSocketBuffer(socketBuffer);
684     }
685
686     /**
687      * Get the socket buffer flag.
688      */

689     public int getSocketBuffer() {
690         return socketBuffer;
691     }
692
693     /**
694      * Set the upload timeout.
695      */

696     public void setTimeout( int timeouts ) {
697         timeout = timeouts ;
698     }
699
700     /**
701      * Get the upload timeout.
702      */

703     public int getTimeout() {
704         return timeout;
705     }
706
707
708     /**
709      * Set the server header name.
710      */

711     public void setServer( String JavaDoc server ) {
712         if (server==null || server.equals("")) {
713             this.server = null;
714         } else {
715             this.server = server;
716         }
717     }
718
719     /**
720      * Get the server header name.
721      */

722     public String JavaDoc getServer() {
723         return server;
724     }
725
726
727     /** Get the request associated with this processor.
728      *
729      * @return The request
730      */

731     public Request getRequest() {
732         return request;
733     }
734
735     /**
736      * Process pipelined HTTP requests using the specified input and output
737      * streams.
738      *
739      * @throws IOException error during an I/O operation
740      */

741     public SocketState event(SocketStatus status)
742         throws IOException JavaDoc {
743         
744         RequestInfo rp = request.getRequestProcessor();
745         
746         try {
747             rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
748             error = !adapter.event(request, response, status);
749         } catch (InterruptedIOException JavaDoc e) {
750             error = true;
751         } catch (Throwable JavaDoc t) {
752             log.error(sm.getString("http11processor.request.process"), t);
753             // 500 - Internal Server Error
754
response.setStatus(500);
755             error = true;
756         }
757         
758         rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
759
760         if (error) {
761             recycle();
762             return SocketState.CLOSED;
763         } else if (!comet) {
764             endpoint.getPoller().add(socket);
765             recycle();
766             return SocketState.OPEN;
767         } else {
768             endpoint.getCometPoller().add(socket);
769             return SocketState.LONG;
770         }
771     }
772     
773     /**
774      * Process pipelined HTTP requests using the specified input and output
775      * streams.
776      *
777      * @throws IOException error during an I/O operation
778      */

779     public SocketState process(long socket)
780         throws IOException JavaDoc {
781         RequestInfo rp = request.getRequestProcessor();
782         rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
783
784         // Set the remote address
785
remoteAddr = null;
786         remoteHost = null;
787         localAddr = null;
788         localName = null;
789         remotePort = -1;
790         localPort = -1;
791
792         // Setting up the socket
793
this.socket = socket;
794         inputBuffer.setSocket(socket);
795         outputBuffer.setSocket(socket);
796
797         // Error flag
798
error = false;
799         keepAlive = true;
800
801         int keepAliveLeft = maxKeepAliveRequests;
802         long soTimeout = endpoint.getSoTimeout();
803         
804         int limit = 0;
805         if (endpoint.getFirstReadTimeout() > 0 || endpoint.getFirstReadTimeout() < -1) {
806             limit = endpoint.getMaxThreads() / 2;
807         }
808
809         boolean keptAlive = false;
810         boolean openSocket = false;
811
812         while (!error && keepAlive && !comet) {
813
814             // Parsing the request header
815
try {
816                 if( !disableUploadTimeout && keptAlive && soTimeout > 0 ) {
817                     Socket.timeoutSet(socket, soTimeout * 1000);
818                 }
819                 if (!inputBuffer.parseRequestLine
820                         (keptAlive && (endpoint.getCurrentThreadsBusy() > limit))) {
821                     // This means that no data is available right now
822
// (long keepalive), so that the processor should be recycled
823
// and the method should return true
824
openSocket = true;
825                     // Add the socket to the poller
826
endpoint.getPoller().add(socket);
827                     break;
828                 }
829                 request.setStartTime(System.currentTimeMillis());
830                 keptAlive = true;
831                 if (!disableUploadTimeout) {
832                     Socket.timeoutSet(socket, timeout * 1000);
833                 }
834                 inputBuffer.parseHeaders();
835             } catch (IOException JavaDoc e) {
836                 error = true;
837                 break;
838             } catch (Throwable JavaDoc 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
rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
849             try {
850                 prepareRequest();
851             } catch (Throwable JavaDoc t) {
852                 if (log.isDebugEnabled()) {
853                     log.debug(sm.getString("http11processor.request.prepare"), t);
854                 }
855                 // 400 - Internal Server Error
856
response.setStatus(400);
857                 error = true;
858             }
859
860             if (maxKeepAliveRequests > 0 && --keepAliveLeft == 0)
861                 keepAlive = false;
862
863             // Process the request in the adapter
864
if (!error) {
865                 try {
866                     rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
867                     adapter.service(request, response);
868                     // Handle when the response was committed before a serious
869
// error occurred. Throwing a ServletException should both
870
// set the status to 500 and set the errorException.
871
// If we fail here, then the response is likely already
872
// committed, so we can't try and set headers.
873
if(keepAlive && !error) { // Avoid checking twice.
874
error = response.getErrorException() != null ||
875                                 statusDropsConnection(response.getStatus());
876                     }
877                 } catch (InterruptedIOException JavaDoc e) {
878                     error = true;
879                 } catch (Throwable JavaDoc t) {
880                     log.error(sm.getString("http11processor.request.process"), t);
881                     // 500 - Internal Server Error
882
response.setStatus(500);
883                     error = true;
884                 }
885             }
886
887             // Finish the handling of the request
888
if (!comet) {
889                 endRequest();
890             }
891
892             // If there was an error, make sure the request is counted as
893
// and error, and update the statistics counter
894
if (error) {
895                 response.setStatus(500);
896             }
897             request.updateCounters();
898
899             // Do sendfile as needed: add socket to sendfile and end
900
if (sendfileData != null && !error) {
901                 sendfileData.socket = socket;
902                 sendfileData.keepAlive = keepAlive;
903                 if (!endpoint.getSendfile().add(sendfileData)) {
904                     openSocket = true;
905                     break;
906                 }
907             }
908             
909             rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
910
911         }
912
913         rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
914
915         if (comet) {
916             if (error) {
917                 recycle();
918                 return SocketState.CLOSED;
919             } else {
920                 return SocketState.LONG;
921             }
922         } else {
923             recycle();
924             return (openSocket) ? SocketState.OPEN : SocketState.CLOSED;
925         }
926         
927     }
928
929     
930     public void endRequest() {
931         
932         // Finish the handling of the request
933
try {
934             inputBuffer.endRequest();
935         } catch (IOException JavaDoc e) {
936             error = true;
937         } catch (Throwable JavaDoc t) {
938             log.error(sm.getString("http11processor.request.finish"), t);
939             // 500 - Internal Server Error
940
response.setStatus(500);
941             error = true;
942         }
943         try {
944             outputBuffer.endRequest();
945         } catch (IOException JavaDoc e) {
946             error = true;
947         } catch (Throwable JavaDoc t) {
948             log.error(sm.getString("http11processor.response.finish"), t);
949             error = true;
950         }
951
952         // Next request
953
inputBuffer.nextRequest();
954         outputBuffer.nextRequest();
955         
956     }
957     
958     
959     public void recycle() {
960         inputBuffer.recycle();
961         outputBuffer.recycle();
962         this.socket = 0;
963     }
964     
965
966     // ----------------------------------------------------- ActionHook Methods
967

968
969     /**
970      * Send an action to the connector.
971      *
972      * @param actionCode Type of the action
973      * @param param Action parameter
974      */

975     public void action(ActionCode actionCode, Object JavaDoc param) {
976
977         if (actionCode == ActionCode.ACTION_COMMIT) {
978             // Commit current response
979

980             if (response.isCommitted())
981                 return;
982
983             // Validate and write response headers
984
prepareResponse();
985             try {
986                 outputBuffer.commit();
987             } catch (IOException JavaDoc e) {
988                 // Set error flag
989
error = true;
990             }
991
992         } else if (actionCode == ActionCode.ACTION_ACK) {
993
994             // Acknowlege request
995

996             // Send a 100 status back if it makes sense (response not committed
997
// yet, and client specified an expectation for 100-continue)
998

999             if ((response.isCommitted()) || !expectation)
1000                return;
1001
1002            inputBuffer.setSwallowInput(true);
1003            try {
1004                outputBuffer.sendAck();
1005            } catch (IOException JavaDoc e) {
1006                // Set error flag
1007
error = true;
1008            }
1009
1010        } else if (actionCode == ActionCode.ACTION_CLIENT_FLUSH) {
1011
1012            try {
1013                outputBuffer.flush();
1014            } catch (IOException JavaDoc e) {
1015                // Set error flag
1016
error = true;
1017                response.setErrorException(e);
1018            }
1019
1020        } else if (actionCode == ActionCode.ACTION_CLOSE) {
1021            // Close
1022

1023            // End the processing of the current request, and stop any further
1024
// transactions with the client
1025

1026            comet = false;
1027            try {
1028                outputBuffer.endRequest();
1029            } catch (IOException JavaDoc e) {
1030                // Set error flag
1031
error = true;
1032            }
1033
1034        } else if (actionCode == ActionCode.ACTION_RESET) {
1035
1036            // Reset response
1037

1038            // Note: This must be called before the response is committed
1039

1040            outputBuffer.reset();
1041
1042        } else if (actionCode == ActionCode.ACTION_CUSTOM) {
1043
1044            // Do nothing
1045

1046        } else if (actionCode == ActionCode.ACTION_REQ_HOST_ADDR_ATTRIBUTE) {
1047
1048            // Get remote host address
1049
if (remoteAddr == null && (socket != 0)) {
1050                try {
1051                    long sa = Address.get(Socket.APR_REMOTE, socket);
1052                    remoteAddr = Address.getip(sa);
1053                } catch (Exception JavaDoc e) {
1054                    log.warn(sm.getString("http11processor.socket.info"), e);
1055                }
1056            }
1057            request.remoteAddr().setString(remoteAddr);
1058
1059        } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_NAME_ATTRIBUTE) {
1060
1061            // Get local host name
1062
if (localName == null && (socket != 0)) {
1063                try {
1064                    long sa = Address.get(Socket.APR_LOCAL, socket);
1065                    localName = Address.getnameinfo(sa, 0);
1066                } catch (Exception JavaDoc e) {
1067                    log.warn(sm.getString("http11processor.socket.info"), e);
1068                }
1069            }
1070            request.localName().setString(localName);
1071
1072        } else if (actionCode == ActionCode.ACTION_REQ_HOST_ATTRIBUTE) {
1073
1074            // Get remote host name
1075
if (remoteHost == null && (socket != 0)) {
1076                try {
1077                    long sa = Address.get(Socket.APR_REMOTE, socket);
1078                    remoteHost = Address.getnameinfo(sa, 0);
1079                } catch (Exception JavaDoc e) {
1080                    log.warn(sm.getString("http11processor.socket.info"), e);
1081                }
1082            }
1083            request.remoteHost().setString(remoteHost);
1084
1085        } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_ADDR_ATTRIBUTE) {
1086
1087            // Get local host address
1088
if (localAddr == null && (socket != 0)) {
1089                try {
1090                    long sa = Address.get(Socket.APR_LOCAL, socket);
1091                    localAddr = Address.getip(sa);
1092                } catch (Exception JavaDoc e) {
1093                    log.warn(sm.getString("http11processor.socket.info"), e);
1094                }
1095            }
1096
1097            request.localAddr().setString(localAddr);
1098
1099        } else if (actionCode == ActionCode.ACTION_REQ_REMOTEPORT_ATTRIBUTE) {
1100
1101            // Get remote port
1102
if (remotePort == -1 && (socket != 0)) {
1103                try {
1104                    long sa = Address.get(Socket.APR_REMOTE, socket);
1105                    Sockaddr addr = Address.getInfo(sa);
1106                    remotePort = addr.port;
1107                } catch (Exception JavaDoc e) {
1108                    log.warn(sm.getString("http11processor.socket.info"), e);
1109                }
1110            }
1111            request.setRemotePort(remotePort);
1112
1113        } else if (actionCode == ActionCode.ACTION_REQ_LOCALPORT_ATTRIBUTE) {
1114
1115            // Get local port
1116
if (localPort == -1 && (socket != 0)) {
1117                try {
1118                    long sa = Address.get(Socket.APR_LOCAL, socket);
1119                    Sockaddr addr = Address.getInfo(sa);
1120                    localPort = addr.port;
1121                } catch (Exception JavaDoc e) {
1122                    log.warn(sm.getString("http11processor.socket.info"), e);
1123                }
1124            }
1125            request.setLocalPort(localPort);
1126
1127        } else if (actionCode == ActionCode.ACTION_REQ_SSL_ATTRIBUTE ) {
1128
1129            if (ssl && (socket != 0)) {
1130                try {
1131                    // Cipher suite
1132
Object JavaDoc sslO = SSLSocket.getInfoS(socket, SSL.SSL_INFO_CIPHER);
1133                    if (sslO != null) {
1134                        request.setAttribute
1135                            (AprEndpoint.CIPHER_SUITE_KEY, sslO);
1136                    }
1137                    // Client certificate chain if present
1138
int certLength = SSLSocket.getInfoI(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN);
1139                    X509Certificate JavaDoc[] certs = null;
1140                    if (certLength > 0) {
1141                        certs = new X509Certificate JavaDoc[certLength];
1142                        for (int i = 0; i < certLength; i++) {
1143                            byte[] data = SSLSocket.getInfoB(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN + i);
1144                            CertificateFactory JavaDoc cf =
1145                                CertificateFactory.getInstance("X.509");
1146                            ByteArrayInputStream JavaDoc stream = new ByteArrayInputStream JavaDoc(data);
1147                            certs[i] = (X509Certificate JavaDoc) cf.generateCertificate(stream);
1148                        }
1149                    }
1150                    if (certs != null) {
1151                        request.setAttribute
1152                            (AprEndpoint.CERTIFICATE_KEY, certs);
1153                    }
1154                    // User key size
1155
sslO = new Integer JavaDoc(SSLSocket.getInfoI(socket, SSL.SSL_INFO_CIPHER_USEKEYSIZE));
1156                    if (sslO != null) {
1157                        request.setAttribute
1158                            (AprEndpoint.KEY_SIZE_KEY, sslO);
1159                    }
1160                    // SSL session ID
1161
sslO = SSLSocket.getInfoS(socket, SSL.SSL_INFO_SESSION_ID);
1162                    if (sslO != null) {
1163                        request.setAttribute
1164                            (AprEndpoint.SESSION_ID_KEY, sslO);
1165                    }
1166                } catch (Exception JavaDoc e) {
1167                    log.warn(sm.getString("http11processor.socket.ssl"), e);
1168                }
1169            }
1170
1171        } else if (actionCode == ActionCode.ACTION_REQ_SSL_CERTIFICATE) {
1172
1173            if (ssl && (socket != 0)) {
1174                 // Consume and buffer the request body, so that it does not
1175
// interfere with the client's handshake messages
1176
InputFilter[] inputFilters = inputBuffer.getFilters();
1177                ((BufferedInputFilter) inputFilters[Constants.BUFFERED_FILTER])
1178                    .setLimit(maxSavePostSize);
1179                inputBuffer.addActiveFilter
1180                    (inputFilters[Constants.BUFFERED_FILTER]);
1181                try {
1182                    // Renegociate certificates
1183
SSLSocket.renegotiate(socket);
1184                    // Client certificate chain if present
1185
int certLength = SSLSocket.getInfoI(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN);
1186                    X509Certificate JavaDoc[] certs = null;
1187                    if (certLength > 0) {
1188                        certs = new X509Certificate JavaDoc[certLength];
1189                        for (int i = 0; i < certLength; i++) {
1190                            byte[] data = SSLSocket.getInfoB(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN + i);
1191                            CertificateFactory JavaDoc cf =
1192                                CertificateFactory.getInstance("X.509");
1193                            ByteArrayInputStream JavaDoc stream = new ByteArrayInputStream JavaDoc(data);
1194                            certs[i] = (X509Certificate JavaDoc) cf.generateCertificate(stream);
1195                        }
1196                    }
1197                    if (certs != null) {
1198                        request.setAttribute
1199                            (AprEndpoint.CERTIFICATE_KEY, certs);
1200                    }
1201                } catch (Exception JavaDoc e) {
1202                    log.warn(sm.getString("http11processor.socket.ssl"), e);
1203                }
1204            }
1205
1206        } else if (actionCode == ActionCode.ACTION_REQ_SET_BODY_REPLAY) {
1207            ByteChunk body = (ByteChunk) param;
1208            
1209            InputFilter savedBody = new SavedRequestInputFilter(body);
1210            savedBody.setRequest(request);
1211            
1212            InternalAprInputBuffer internalBuffer = (InternalAprInputBuffer)
1213                request.getInputBuffer();
1214            internalBuffer.addActiveFilter(savedBody);
1215            
1216        } else if (actionCode == ActionCode.ACTION_COMET_BEGIN) {
1217            comet = true;
1218        } else if (actionCode == ActionCode.ACTION_COMET_END) {
1219            comet = false;
1220        }
1221
1222    }
1223
1224
1225    // ------------------------------------------------------ Connector Methods
1226

1227
1228    /**
1229     * Set the associated adapter.
1230     *
1231     * @param adapter the new adapter
1232     */

1233    public void setAdapter(Adapter adapter) {
1234        this.adapter = adapter;
1235    }
1236
1237
1238    /**
1239     * Get the associated adapter.
1240     *
1241     * @return the associated adapter
1242     */

1243    public Adapter getAdapter() {
1244        return adapter;
1245    }
1246
1247
1248    // ------------------------------------------------------ Protected Methods
1249

1250
1251    /**
1252     * After reading the request headers, we have to setup the request filters.
1253     */

1254    protected void prepareRequest() {
1255
1256        http11 = true;
1257        http09 = false;
1258        contentDelimitation = false;
1259        expectation = false;
1260        sendfileData = null;
1261        if (ssl) {
1262            request.scheme().setString("https");
1263        }
1264        MessageBytes protocolMB = request.protocol();
1265        if (protocolMB.equals(Constants.HTTP_11)) {
1266            http11 = true;
1267            protocolMB.setString(Constants.HTTP_11);
1268        } else if (protocolMB.equals(Constants.HTTP_10)) {
1269            http11 = false;
1270            keepAlive = false;
1271            protocolMB.setString(Constants.HTTP_10);
1272        } else if (protocolMB.equals("")) {
1273            // HTTP/0.9
1274
http09 = true;
1275            http11 = false;
1276            keepAlive = false;
1277        } else {
1278            // Unsupported protocol
1279
http11 = false;
1280            error = true;
1281            // Send 505; Unsupported HTTP version
1282
response.setStatus(505);
1283        }
1284
1285        MessageBytes methodMB = request.method();
1286        if (methodMB.equals(Constants.GET)) {
1287            methodMB.setString(Constants.GET);
1288        } else if (methodMB.equals(Constants.POST)) {
1289            methodMB.setString(Constants.POST);
1290        }
1291
1292        MimeHeaders headers = request.getMimeHeaders();
1293
1294        // Check connection header
1295
MessageBytes connectionValueMB = headers.getValue("connection");
1296        if (connectionValueMB != null) {
1297            ByteChunk connectionValueBC = connectionValueMB.getByteChunk();
1298            if (findBytes(connectionValueBC, Constants.CLOSE_BYTES) != -1) {
1299                keepAlive = false;
1300            } else if (findBytes(connectionValueBC,
1301                                 Constants.KEEPALIVE_BYTES) != -1) {
1302                keepAlive = true;
1303            }
1304        }
1305
1306        MessageBytes expectMB = null;
1307        if (http11)
1308            expectMB = headers.getValue("expect");
1309        if ((expectMB != null)
1310            && (expectMB.indexOfIgnoreCase("100-continue", 0) != -1)) {
1311            inputBuffer.setSwallowInput(false);
1312            expectation = true;
1313        }
1314
1315        // Check user-agent header
1316
if ((restrictedUserAgents != null) && ((http11) || (keepAlive))) {
1317            MessageBytes userAgentValueMB = headers.getValue("user-agent");
1318            // Check in the restricted list, and adjust the http11
1319
// and keepAlive flags accordingly
1320
if(userAgentValueMB != null) {
1321                String JavaDoc userAgentValue = userAgentValueMB.toString();
1322                for (int i = 0; i < restrictedUserAgents.length; i++) {
1323                    if (restrictedUserAgents[i].matcher(userAgentValue).matches()) {
1324                        http11 = false;
1325                        keepAlive = false;
1326                        break;
1327                    }
1328                }
1329            }
1330        }
1331
1332        // Check for a full URI (including protocol://host:port/)
1333
ByteChunk uriBC = request.requestURI().getByteChunk();
1334        if (uriBC.startsWithIgnoreCase("http", 0)) {
1335
1336            int pos = uriBC.indexOf("://", 0, 3, 4);
1337            int uriBCStart = uriBC.getStart();
1338            int slashPos = -1;
1339            if (pos != -1) {
1340                byte[] uriB = uriBC.getBytes();
1341                slashPos = uriBC.indexOf('/', pos + 3);
1342                if (slashPos == -1) {
1343                    slashPos = uriBC.getLength();
1344                    // Set URI as "/"
1345
request.requestURI().setBytes
1346                        (uriB, uriBCStart + pos + 1, 1);
1347                } else {
1348                    request.requestURI().setBytes
1349                        (uriB, uriBCStart + slashPos,
1350                         uriBC.getLength() - slashPos);
1351                }
1352                MessageBytes hostMB = headers.setValue("host");
1353                hostMB.setBytes(uriB, uriBCStart + pos + 3,
1354                                slashPos - pos - 3);
1355            }
1356
1357        }
1358
1359        // Input filter setup
1360
InputFilter[] inputFilters = inputBuffer.getFilters();
1361
1362        // Parse transfer-encoding header
1363
MessageBytes transferEncodingValueMB = null;
1364        if (http11)
1365            transferEncodingValueMB = headers.getValue("transfer-encoding");
1366        if (transferEncodingValueMB != null) {
1367            String JavaDoc transferEncodingValue = transferEncodingValueMB.toString();
1368            // Parse the comma separated list. "identity" codings are ignored
1369
int startPos = 0;
1370            int commaPos = transferEncodingValue.indexOf(',');
1371            String JavaDoc encodingName = null;
1372            while (commaPos != -1) {
1373                encodingName = transferEncodingValue.substring
1374                    (startPos, commaPos).toLowerCase().trim();
1375                if (!addInputFilter(inputFilters, encodingName)) {
1376                    // Unsupported transfer encoding
1377
error = true;
1378                    // 501 - Unimplemented
1379
response.setStatus(501);
1380                }
1381                startPos = commaPos + 1;
1382                commaPos = transferEncodingValue.indexOf(',', startPos);
1383            }
1384            encodingName = transferEncodingValue.substring(startPos)
1385                .toLowerCase().trim();
1386            if (!addInputFilter(inputFilters, encodingName)) {
1387                // Unsupported transfer encoding
1388
error = true;
1389                // 501 - Unimplemented
1390
response.setStatus(501);
1391            }
1392        }
1393
1394        // Parse content-length header
1395
long contentLength = request.getContentLengthLong();
1396        if (contentLength >= 0 && !contentDelimitation) {
1397            inputBuffer.addActiveFilter
1398                (inputFilters[Constants.IDENTITY_FILTER]);
1399            contentDelimitation = true;
1400        }
1401
1402        MessageBytes valueMB = headers.getValue("host");
1403
1404        // Check host header
1405
if (http11 && (valueMB == null)) {
1406            error = true;
1407            // 400 - Bad request
1408
response.setStatus(400);
1409        }
1410
1411        parseHost(valueMB);
1412
1413        if (!contentDelimitation) {
1414            // If there's no content length
1415
// (broken HTTP/1.0 or HTTP/1.1), assume
1416
// the client is not broken and didn't send a body
1417
inputBuffer.addActiveFilter
1418                    (inputFilters[Constants.VOID_FILTER]);
1419            contentDelimitation = true;
1420        }
1421
1422        // Advertise sendfile support through a request attribute
1423
if (endpoint.getUseSendfile()) {
1424            request.setAttribute("org.apache.tomcat.sendfile.support", Boolean.TRUE);
1425        }
1426        // Advertise comet support through a request attribute
1427
request.setAttribute("org.apache.tomcat.comet.support", Boolean.TRUE);
1428        
1429    }
1430
1431
1432    /**
1433     * Parse host.
1434     */

1435    public void parseHost(MessageBytes valueMB) {
1436
1437        if (valueMB == null || valueMB.isNull()) {
1438            // HTTP/1.0
1439
// Default is what the socket tells us. Overriden if a host is
1440
// found/parsed
1441
request.setServerPort(endpoint.getPort());
1442            return;
1443        }
1444
1445        ByteChunk valueBC = valueMB.getByteChunk();
1446        byte[] valueB = valueBC.getBytes();
1447        int valueL = valueBC.getLength();
1448        int valueS = valueBC.getStart();
1449        int colonPos = -1;
1450        if (hostNameC.length < valueL) {
1451            hostNameC = new char[valueL];
1452        }
1453
1454        boolean ipv6 = (valueB[valueS] == '[');
1455        boolean bracketClosed = false;
1456        for (int i = 0; i < valueL; i++) {
1457            char b = (char) valueB[i + valueS];
1458            hostNameC[i] = b;
1459            if (b == ']') {
1460                bracketClosed = true;
1461            } else if (b == ':') {
1462                if (!ipv6 || bracketClosed) {
1463                    colonPos = i;
1464                    break;
1465                }
1466            }
1467        }
1468
1469        if (colonPos < 0) {
1470            if (!ssl) {
1471                // 80 - Default HTTP port
1472
request.setServerPort(80);
1473            } else {
1474                // 443 - Default HTTPS port
1475
request.setServerPort(443);
1476            }
1477            request.serverName().setChars(hostNameC, 0, valueL);
1478        } else {
1479
1480            request.serverName().setChars(hostNameC, 0, colonPos);
1481
1482            int port = 0;
1483            int mult = 1;
1484            for (int i = valueL - 1; i > colonPos; i--) {
1485                int charValue = HexUtils.DEC[(int) valueB[i + valueS]];
1486                if (charValue == -1) {
1487                    // Invalid character
1488
error = true;
1489                    // 400 - Bad request
1490
response.setStatus(400);
1491                    break;
1492                }
1493                port = port + (charValue * mult);
1494                mult = 10 * mult;
1495            }
1496            request.setServerPort(port);
1497
1498        }
1499
1500    }
1501
1502
1503    /**
1504     * Check for compression
1505     */

1506    private boolean isCompressable() {
1507
1508        // Nope Compression could works in HTTP 1.0 also
1509
// cf: mod_deflate
1510

1511        // Compression only since HTTP 1.1
1512
// if (! http11)
1513
// return false;
1514

1515        // Check if browser support gzip encoding
1516
MessageBytes acceptEncodingMB =
1517            request.getMimeHeaders().getValue("accept-encoding");
1518
1519        if ((acceptEncodingMB == null)
1520            || (acceptEncodingMB.indexOf("gzip") == -1))
1521            return false;
1522
1523        // Check if content is not allready gzipped
1524
MessageBytes contentEncodingMB =
1525            response.getMimeHeaders().getValue("Content-Encoding");
1526
1527        if ((contentEncodingMB != null)
1528            && (contentEncodingMB.indexOf("gzip") != -1))
1529            return false;
1530
1531        // If force mode, allways compress (test purposes only)
1532
if (compressionLevel == 2)
1533           return true;
1534
1535        // Check for incompatible Browser
1536
if (noCompressionUserAgents != null) {
1537            MessageBytes userAgentValueMB =
1538                request.getMimeHeaders().getValue("user-agent");
1539            if(userAgentValueMB != null) {
1540                String JavaDoc userAgentValue = userAgentValueMB.toString();
1541
1542                // If one Regexp rule match, disable compression
1543
for (int i = 0; i < noCompressionUserAgents.length; i++)
1544                    if (noCompressionUserAgents[i].matcher(userAgentValue).matches())
1545                        return false;
1546            }
1547        }
1548
1549        // Check if suffisant len to trig the compression
1550
long contentLength = response.getContentLengthLong();
1551        if ((contentLength == -1)
1552            || (contentLength > compressionMinSize)) {
1553            // Check for compatible MIME-TYPE
1554
if (compressableMimeTypes != null) {
1555                return (startsWithStringArray(compressableMimeTypes,
1556                                              response.getContentType()));
1557            }
1558        }
1559
1560        return false;
1561    }
1562
1563
1564    /**
1565     * When committing the response, we have to validate the set of headers, as
1566     * well as setup the response filters.
1567     */

1568    protected void prepareResponse() {
1569
1570        boolean entityBody = true;
1571        contentDelimitation = false;
1572
1573        OutputFilter[] outputFilters = outputBuffer.getFilters();
1574
1575        if (http09 == true) {
1576            // HTTP/0.9
1577
outputBuffer.addActiveFilter
1578                (outputFilters[Constants.IDENTITY_FILTER]);
1579            return;
1580        }
1581
1582        int statusCode = response.getStatus();
1583        if ((statusCode == 204) || (statusCode == 205)
1584            || (statusCode == 304)) {
1585            // No entity body
1586
outputBuffer.addActiveFilter
1587                (outputFilters[Constants.VOID_FILTER]);
1588            entityBody = false;
1589            contentDelimitation = true;
1590        }
1591
1592        MessageBytes methodMB = request.method();
1593        if (methodMB.equals("HEAD")) {
1594            // No entity body
1595
outputBuffer.addActiveFilter
1596                (outputFilters[Constants.VOID_FILTER]);
1597            contentDelimitation = true;
1598        }
1599
1600        // Sendfile support
1601
if (endpoint.getUseSendfile()) {
1602            String JavaDoc fileName = (String JavaDoc) request.getAttribute("org.apache.tomcat.sendfile.filename");
1603            if (fileName != null) {
1604                // No entity body sent here
1605
outputBuffer.addActiveFilter
1606                    (outputFilters[Constants.VOID_FILTER]);
1607                contentDelimitation = true;
1608                sendfileData = new AprEndpoint.SendfileData();
1609                sendfileData.fileName = fileName;
1610                sendfileData.start =
1611                    ((Long JavaDoc) request.getAttribute("org.apache.tomcat.sendfile.start")).longValue();
1612                sendfileData.end =
1613                    ((Long JavaDoc) request.getAttribute("org.apache.tomcat.sendfile.end")).longValue();
1614            }
1615        }
1616        
1617        // Check for compression
1618
boolean useCompression = false;
1619        if (entityBody && (compressionLevel > 0) && (sendfileData == null)) {
1620            useCompression = isCompressable();
1621            // Change content-length to -1 to force chunking
1622
if (useCompression) {
1623                response.setContentLength(-1);
1624            }
1625        }
1626
1627        MimeHeaders headers = response.getMimeHeaders();
1628        if (!entityBody) {
1629            response.setContentLength(-1);
1630        } else {
1631            String JavaDoc contentType = response.getContentType();
1632            if (contentType != null) {
1633                headers.setValue("Content-Type").setString(contentType);
1634            }
1635            String JavaDoc contentLanguage = response.getContentLanguage();
1636            if (contentLanguage != null) {
1637                headers.setValue("Content-Language")
1638                    .setString(contentLanguage);
1639            }
1640        }
1641
1642        long contentLength = response.getContentLengthLong();
1643        if (contentLength != -1) {
1644            headers.setValue("Content-Length").setLong(contentLength);
1645            outputBuffer.addActiveFilter
1646                (outputFilters[Constants.IDENTITY_FILTER]);
1647            contentDelimitation = true;
1648        } else {
1649            if (entityBody && http11 && keepAlive) {
1650                outputBuffer.addActiveFilter
1651                    (outputFilters[Constants.CHUNKED_FILTER]);
1652                contentDelimitation = true;
1653                headers.addValue(Constants.TRANSFERENCODING).setString(Constants.CHUNKED);
1654            } else {
1655                outputBuffer.addActiveFilter
1656                    (outputFilters[Constants.IDENTITY_FILTER]);
1657            }
1658        }
1659
1660        if (useCompression) {
1661            outputBuffer.addActiveFilter(outputFilters[Constants.GZIP_FILTER]);
1662            headers.setValue("Content-Encoding").setString("gzip");
1663            // Make Proxies happy via Vary (from mod_deflate)
1664
headers.setValue("Vary").setString("Accept-Encoding");
1665        }
1666
1667        // Add date header
1668
headers.setValue("Date").setString(FastHttpDateFormat.getCurrentDate());
1669
1670        // FIXME: Add transfer encoding header
1671

1672        if ((entityBody) && (!contentDelimitation)) {
1673            // Mark as close the connection after the request, and add the
1674
// connection: close header
1675
keepAlive = false;
1676        }
1677
1678        // If we know that the request is bad this early, add the
1679
// Connection: close header.
1680
keepAlive = keepAlive && !statusDropsConnection(statusCode);
1681        if (!keepAlive) {
1682            headers.addValue(Constants.CONNECTION).setString(Constants.CLOSE);
1683        } else if (!http11 && !error) {
1684            headers.addValue(Constants.CONNECTION).setString(Constants.KEEPALIVE);
1685        }
1686
1687        // Build the response header
1688
outputBuffer.sendStatus();
1689
1690        // Add server header
1691
if (server != null) {
1692            headers.setValue("Server").setString(server);
1693        } else {
1694            outputBuffer.write(Constants.SERVER_BYTES);
1695        }
1696
1697        int size = headers.size();
1698        for (int i = 0; i < size; i++) {
1699            outputBuffer.sendHeader(headers.getName(i), headers.getValue(i));
1700        }
1701        outputBuffer.endHeaders();
1702
1703    }
1704
1705
1706    /**
1707     * Initialize standard input and output filters.
1708     */

1709    protected void initializeFilters() {
1710
1711        // Create and add the identity filters.
1712
inputBuffer.addFilter(new IdentityInputFilter());
1713        outputBuffer.addFilter(new IdentityOutputFilter());
1714
1715        // Create and add the chunked filters.
1716
inputBuffer.addFilter(new ChunkedInputFilter());
1717        outputBuffer.addFilter(new ChunkedOutputFilter());
1718
1719        // Create and add the void filters.
1720
inputBuffer.addFilter(new VoidInputFilter());
1721        outputBuffer.addFilter(new VoidOutputFilter());
1722
1723        // Create and add buffered input filter
1724
inputBuffer.addFilter(new BufferedInputFilter());
1725
1726        // Create and add the chunked filters.
1727
//inputBuffer.addFilter(new GzipInputFilter());
1728
outputBuffer.addFilter(new GzipOutputFilter());
1729
1730    }
1731
1732
1733    /**
1734     * Add an input filter to the current request.
1735     *
1736     * @return false if the encoding was not found (which would mean it is
1737     * unsupported)
1738     */

1739    protected boolean addInputFilter(InputFilter[] inputFilters,
1740                                     String JavaDoc encodingName) {
1741        if (encodingName.equals("identity")) {
1742            // Skip
1743
} else if (encodingName.equals("chunked")) {
1744            inputBuffer.addActiveFilter
1745                (inputFilters[Constants.CHUNKED_FILTER]);
1746            contentDelimitation = true;
1747        } else {
1748            for (int i = 2; i < inputFilters.length; i++) {
1749                if (inputFilters[i].getEncodingName()
1750                    .toString().equals(encodingName)) {
1751                    inputBuffer.addActiveFilter(inputFilters[i]);
1752                    return true;
1753                }
1754            }
1755            return false;
1756        }
1757        return true;
1758    }
1759
1760
1761    /**
1762     * Specialized utility method: find a sequence of lower case bytes inside
1763     * a ByteChunk.
1764     */

1765    protected int findBytes(ByteChunk bc, byte[] b) {
1766
1767        byte first = b[0];
1768        byte[] buff = bc.getBuffer();
1769        int start = bc.getStart();
1770        int end = bc.getEnd();
1771
1772    // Look for first char
1773
int srcEnd = b.length;
1774
1775    for (int i = start; i <= (end - srcEnd); i++) {
1776        if (Ascii.toLower(buff[i]) != first) continue;
1777        // found first char, now look for a match
1778
int myPos = i+1;
1779        for (int srcPos = 1; srcPos < srcEnd; ) {
1780                if (Ascii.toLower(buff[myPos++]) != b[srcPos++])
1781            break;
1782                if (srcPos == srcEnd) return i - start; // found it
1783
}
1784    }
1785    return -1;
1786
1787    }
1788
1789    /**
1790     * Determine if we must drop the connection because of the HTTP status
1791     * code. Use the same list of codes as Apache/httpd.
1792     */

1793    protected boolean statusDropsConnection(int status) {
1794        return status == 400 /* SC_BAD_REQUEST */ ||
1795               status == 408 /* SC_REQUEST_TIMEOUT */ ||
1796               status == 411 /* SC_LENGTH_REQUIRED */ ||
1797               status == 413 /* SC_REQUEST_ENTITY_TOO_LARGE */ ||
1798               status == 414 /* SC_REQUEST_URI_TOO_LARGE */ ||
1799               status == 500 /* SC_INTERNAL_SERVER_ERROR */ ||
1800               status == 503 /* SC_SERVICE_UNAVAILABLE */ ||
1801               status == 501 /* SC_NOT_IMPLEMENTED */;
1802    }
1803
1804}
1805
Popular Tags