KickJava   Java API By Example, From Geeks To Geeks.

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


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.IOException JavaDoc;
21 import java.io.InterruptedIOException JavaDoc;
22 import java.net.InetAddress JavaDoc;
23 import java.nio.channels.SelectionKey JavaDoc;
24 import java.util.StringTokenizer JavaDoc;
25 import java.util.regex.Pattern JavaDoc;
26 import java.util.regex.PatternSyntaxException JavaDoc;
27
28 import org.apache.coyote.ActionCode;
29 import org.apache.coyote.ActionHook;
30 import org.apache.coyote.Adapter;
31 import org.apache.coyote.Request;
32 import org.apache.coyote.RequestInfo;
33 import org.apache.coyote.Response;
34 import org.apache.coyote.http11.filters.BufferedInputFilter;
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.tomcat.util.buf.Ascii;
44 import org.apache.tomcat.util.buf.ByteChunk;
45 import org.apache.tomcat.util.buf.HexUtils;
46 import org.apache.tomcat.util.buf.MessageBytes;
47 import org.apache.tomcat.util.http.FastHttpDateFormat;
48 import org.apache.tomcat.util.http.MimeHeaders;
49 import org.apache.tomcat.util.net.NioChannel;
50 import org.apache.tomcat.util.net.NioEndpoint;
51 import org.apache.tomcat.util.net.SSLSupport;
52 import org.apache.tomcat.util.net.SocketStatus;
53 import org.apache.tomcat.util.net.NioEndpoint.Handler.SocketState;
54 import org.apache.tomcat.util.res.StringManager;
55
56
57 /**
58  * Processes HTTP requests.
59  *
60  * @author Remy Maucherat
61  * @author Filip Hanik
62  */

63 public class Http11NioProcessor implements ActionHook {
64
65
66     /**
67      * Logger.
68      */

69     protected static org.apache.commons.logging.Log log
70         = org.apache.commons.logging.LogFactory.getLog(Http11NioProcessor.class);
71
72     /**
73      * The string manager for this package.
74      */

75     protected static StringManager sm =
76         StringManager.getManager(Constants.Package);
77
78     /**
79      * SSL information.
80      */

81     protected SSLSupport sslSupport;
82
83     // ----------------------------------------------------------- Constructors
84

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

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

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

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

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

144     protected InternalNioInputBuffer inputBuffer = null;
145
146
147     /**
148      * Output.
149      */

150     protected InternalNioOutputBuffer outputBuffer = null;
151
152
153     /**
154      * Error flag.
155      */

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

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

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

174     protected boolean http09 = false;
175
176
177
178     /**
179      * Comet used.
180      */

181     protected boolean comet = false;
182     
183     /**
184      * Closed flag, a Comet async thread can
185      * signal for this Nio processor to be closed and recycled instead
186      * of waiting for a timeout.
187      * Closed by HttpServletResponse.getWriter().close()
188      */

189     protected boolean cometClose = false;
190
191     /**
192      * Content delimitator for the request (if false, the connection will
193      * be closed at the end of the request).
194      */

195     protected boolean contentDelimitation = true;
196
197
198     /**
199      * Is there an expectation ?
200      */

201     protected boolean expectation = false;
202
203
204     /**
205      * List of restricted user agents.
206      */

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

213     protected int maxKeepAliveRequests = -1;
214
215
216     /**
217      * SSL enabled ?
218      */

219     protected boolean ssl = false;
220
221
222     /**
223      * Socket associated with the current connection.
224      */

225     protected NioChannel socket = null;
226
227
228     /**
229      * Remote Address associated with the current connection.
230      */

231     protected String JavaDoc remoteAddr = null;
232
233
234     /**
235      * Remote Host associated with the current connection.
236      */

237     protected String JavaDoc remoteHost = null;
238
239
240     /**
241      * Local Host associated with the current connection.
242      */

243     protected String JavaDoc localName = null;
244
245
246
247     /**
248      * Local port to which the socket is connected
249      */

250     protected int localPort = -1;
251
252
253     /**
254      * Remote port to which the socket is connected
255      */

256     protected int remotePort = -1;
257
258
259     /**
260      * The local Host address.
261      */

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

268     protected int timeout = 300000;
269
270
271     /**
272      * Flag to disable setting a different time-out on uploads.
273      */

274     protected boolean disableUploadTimeout = false;
275
276
277     /**
278      * Allowed compression level.
279      */

280     protected int compressionLevel = 0;
281
282
283     /**
284      * Minimum contentsize to make compression.
285      */

286     protected int compressionMinSize = 2048;
287
288
289     /**
290      * Socket buffering.
291      */

292     protected int socketBuffer = -1;
293
294
295     /**
296      * Max save post size.
297      */

298     protected int maxSavePostSize = 4 * 1024;
299
300
301     /**
302      * List of user agents to not use gzip with
303      */

304     protected Pattern JavaDoc noCompressionUserAgents[] = null;
305
306     /**
307      * List of MIMES which could be gzipped
308      */

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

316     protected char[] hostNameC = new char[0];
317
318
319     /**
320      * Associated endpoint.
321      */

322     protected NioEndpoint endpoint;
323
324
325     /**
326      * Allow a customized the server header for the tin-foil hat folks.
327      */

328     protected String JavaDoc server = null;
329
330
331     // ------------------------------------------------------------- Properties
332

333
334     /**
335      * Return compression level.
336      */

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

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

375     public void setCompressionMinSize(int compressionMinSize) {
376         this.compressionMinSize = compressionMinSize;
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 JavaDoc userAgent) {
388         try {
389             Pattern JavaDoc nRule = Pattern.compile(userAgent);
390             noCompressionUserAgents =
391                 addREArray(noCompressionUserAgents, nRule);
392         } catch (PatternSyntaxException JavaDoc 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 JavaDoc[] 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 JavaDoc noCompressionUserAgents) {
415         if (noCompressionUserAgents != null) {
416             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(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 JavaDoc 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 JavaDoc[] 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 JavaDoc compressableMimeTypes) {
454         if (compressableMimeTypes != null) {
455             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(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 JavaDoc[] 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 JavaDoc className) {
482         try {
483             Class JavaDoc clazz = Class.forName(className);
484             Object JavaDoc 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 JavaDoc 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 JavaDoc[] addStringArray(String JavaDoc sArray[], String JavaDoc value) {
505         String JavaDoc[] result = null;
506         if (sArray == null) {
507             result = new String JavaDoc[1];
508             result[0] = value;
509         }
510         else {
511             result = new String JavaDoc[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 JavaDoc[] addREArray(Pattern JavaDoc rArray[], Pattern JavaDoc value) {
527         Pattern JavaDoc[] result = null;
528         if (rArray == null) {
529             result = new Pattern JavaDoc[1];
530             result[0] = value;
531         }
532         else {
533             result = new Pattern JavaDoc[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 JavaDoc sArray[], String JavaDoc 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 JavaDoc sArray[], String JavaDoc 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 JavaDoc userAgent) {
584         try {
585             Pattern JavaDoc nRule = Pattern.compile(userAgent);
586             restrictedUserAgents = addREArray(restrictedUserAgents, nRule);
587         } catch (PatternSyntaxException JavaDoc 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 JavaDoc[] 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 JavaDoc restrictedUserAgents) {
610         if (restrictedUserAgents != null) {
611             StringTokenizer JavaDoc st =
612                 new StringTokenizer JavaDoc(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 JavaDoc[] findRestrictedUserAgents() {
624         String JavaDoc[] sarr = new String JavaDoc [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 flag to control upload time-outs.
669      */

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

677     public boolean getDisableUploadTimeout() {
678         return disableUploadTimeout;
679     }
680
681     /**
682      * Set the socket buffer flag.
683      */

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

692     public int getSocketBuffer() {
693         return socketBuffer;
694     }
695
696     /**
697      * Set the upload timeout.
698      */

699     public void setTimeout( int timeouts ) {
700         timeout = timeouts ;
701     }
702
703     /**
704      * Get the upload timeout.
705      */

706     public int getTimeout() {
707         return timeout;
708     }
709
710
711     /**
712      * Set the server header name.
713      */

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

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

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

744     public SocketState event(SocketStatus status)
745         throws IOException JavaDoc {
746
747         RequestInfo rp = request.getRequestProcessor();
748
749         try {
750             rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
751             error = !adapter.event(request, response, status);
752             SelectionKey JavaDoc key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());
753             if ( key != null ) {
754                 NioEndpoint.KeyAttachment attach = (NioEndpoint.KeyAttachment) key.attachment();
755                 if ( attach!=null ) {
756                     attach.setComet(comet);
757                     Integer JavaDoc comettimeout = (Integer JavaDoc)request.getAttribute("org.apache.tomcat.comet.timeout");
758                     if ( comettimeout != null ) attach.setTimeout(comettimeout.longValue());
759                 }
760             }
761             
762         } catch (InterruptedIOException JavaDoc e) {
763             error = true;
764         } catch (Throwable JavaDoc t) {
765             log.error(sm.getString("http11processor.request.process"), t);
766             // 500 - Internal Server Error
767
response.setStatus(500);
768             error = true;
769         }
770
771         rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
772
773         if (error) {
774             recycle();
775             return SocketState.CLOSED;
776         } else if (!comet) {
777             socket.getPoller().add(socket);
778             recycle();
779             return SocketState.OPEN;
780         } else {
781             socket.getPoller().add(socket);
782             return SocketState.LONG;
783         }
784     }
785
786     /**
787      * Process pipelined HTTP requests using the specified input and output
788      * streams.
789      *
790      * @throws IOException error during an I/O operation
791      */

792     public SocketState process(NioChannel socket)
793         throws IOException JavaDoc {
794         RequestInfo rp = request.getRequestProcessor();
795         rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
796
797         // Set the remote address
798
remoteAddr = null;
799         remoteHost = null;
800         localAddr = null;
801         localName = null;
802         remotePort = -1;
803         localPort = -1;
804
805         // Setting up the socket
806
this.socket = socket;
807         inputBuffer.setSocket(socket);
808         outputBuffer.setSocket(socket);
809         inputBuffer.setSelectorPool(endpoint.getSelectorPool());
810         outputBuffer.setSelectorPool(endpoint.getSelectorPool());
811
812         // Error flag
813
error = false;
814         keepAlive = true;
815
816         int keepAliveLeft = maxKeepAliveRequests;
817         long soTimeout = endpoint.getSoTimeout();
818
819         int limit = 0;
820         if (endpoint.getFirstReadTimeout() > 0 || endpoint.getFirstReadTimeout() < -1) {
821             limit = endpoint.getMaxThreads() / 2;
822         }
823
824         boolean keptAlive = false;
825         boolean openSocket = false;
826         boolean recycle = true;
827         while (!error && keepAlive && !comet) {
828
829             // Parsing the request header
830
try {
831                 if( !disableUploadTimeout && keptAlive && soTimeout > 0 ) {
832                     socket.getIOChannel().socket().setSoTimeout((int)soTimeout);
833                     inputBuffer.readTimeout = soTimeout;
834                 }
835                 if (!inputBuffer.parseRequestLine(keptAlive && (endpoint.getCurrentThreadsBusy() > limit))) {
836                     // This means that no data is available right now
837
// (long keepalive), so that the processor should be recycled
838
// and the method should return true
839
openSocket = true;
840                     // Add the socket to the poller
841
socket.getPoller().add(socket);
842                     break;
843                 }
844                 keptAlive = true;
845                 if ( !inputBuffer.parseHeaders() ) {
846                     openSocket = true;
847                     socket.getPoller().add(socket);
848                     recycle = false;
849                     break;
850                 }
851                 request.setStartTime(System.currentTimeMillis());
852                 if (!disableUploadTimeout) { //only for body, not for request headers
853
socket.getIOChannel().socket().setSoTimeout((int)timeout);
854                     inputBuffer.readTimeout = soTimeout;
855                 }
856             } catch (IOException JavaDoc e) {
857                 error = true;
858                 break;
859             } catch (Throwable JavaDoc t) {
860                 if (log.isDebugEnabled()) {
861                     log.debug(sm.getString("http11processor.header.parse"), t);
862                 }
863                 // 400 - Bad Request
864
response.setStatus(400);
865                 error = true;
866             }
867
868             // Setting up filters, and parse some request headers
869
rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
870             try {
871                 prepareRequest();
872             } catch (Throwable JavaDoc t) {
873                 if (log.isDebugEnabled()) {
874                     log.debug(sm.getString("http11processor.request.prepare"), t);
875                 }
876                 // 400 - Internal Server Error
877
response.setStatus(400);
878                 error = true;
879             }
880
881             if (maxKeepAliveRequests > 0 && --keepAliveLeft == 0)
882                 keepAlive = false;
883
884             // Process the request in the adapter
885
if (!error) {
886                 try {
887                     rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
888                     adapter.service(request, response);
889                     // Handle when the response was committed before a serious
890
// error occurred. Throwing a ServletException should both
891
// set the status to 500 and set the errorException.
892
// If we fail here, then the response is likely already
893
// committed, so we can't try and set headers.
894
if(keepAlive && !error) { // Avoid checking twice.
895
error = response.getErrorException() != null ||
896                                 statusDropsConnection(response.getStatus());
897                     }
898                     // Comet support
899
SelectionKey JavaDoc key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());
900                     if (key != null) {
901                         NioEndpoint.KeyAttachment attach = (NioEndpoint.KeyAttachment) key.attachment();
902                         if (attach != null) {
903                             attach.setComet(comet);
904                             Integer JavaDoc comettimeout = (Integer JavaDoc) request.getAttribute("org.apache.tomcat.comet.timeout");
905                             if (comettimeout != null) attach.setTimeout(comettimeout.longValue());
906                         }
907                     }
908                 } catch (InterruptedIOException JavaDoc e) {
909                     error = true;
910                 } catch (Throwable JavaDoc t) {
911                     log.error(sm.getString("http11processor.request.process"), t);
912                     // 500 - Internal Server Error
913
response.setStatus(500);
914                     error = true;
915                 }
916             }
917
918             // Finish the handling of the request
919
if (!comet) {
920                 endRequest();
921             }
922
923             // If there was an error, make sure the request is counted as
924
// and error, and update the statistics counter
925
if (error) {
926                 response.setStatus(500);
927             }
928             request.updateCounters();
929
930             rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
931
932         }
933
934         rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
935
936         if (comet) {
937             if (error) {
938                 recycle();
939                 return SocketState.CLOSED;
940             } else {
941                 return SocketState.LONG;
942             }
943         } else {
944             if ( recycle ) recycle();
945             return (openSocket) ? SocketState.OPEN : SocketState.CLOSED;
946         }
947
948     }
949
950
951     public void endRequest() {
952
953         // Finish the handling of the request
954
try {
955             inputBuffer.endRequest();
956         } catch (IOException JavaDoc e) {
957             error = true;
958         } catch (Throwable JavaDoc t) {
959             log.error(sm.getString("http11processor.request.finish"), t);
960             // 500 - Internal Server Error
961
response.setStatus(500);
962             error = true;
963         }
964         try {
965             outputBuffer.endRequest();
966         } catch (IOException JavaDoc e) {
967             error = true;
968         } catch (Throwable JavaDoc t) {
969             log.error(sm.getString("http11processor.response.finish"), t);
970             error = true;
971         }
972
973         // Next request
974
inputBuffer.nextRequest();
975         outputBuffer.nextRequest();
976
977     }
978
979
980     public void recycle() {
981         inputBuffer.recycle();
982         outputBuffer.recycle();
983         this.socket = null;
984         this.cometClose = false;
985         this.comet = false;
986     }
987
988
989     // ----------------------------------------------------- ActionHook Methods
990

991
992     /**
993      * Send an action to the connector.
994      *
995      * @param actionCode Type of the action
996      * @param param Action parameter
997      */

998     public void action(ActionCode actionCode, Object JavaDoc param) {
999
1000        if (actionCode == ActionCode.ACTION_COMMIT) {
1001            // Commit current response
1002

1003            if (response.isCommitted())
1004                return;
1005
1006            // Validate and write response headers
1007

1008            try {
1009                prepareResponse();
1010                outputBuffer.commit();
1011            } catch (IOException JavaDoc e) {
1012                // Set error flag
1013
error = true;
1014            }
1015
1016        } else if (actionCode == ActionCode.ACTION_ACK) {
1017
1018            // Acknowlege request
1019

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

1023            if ((response.isCommitted()) || !expectation)
1024                return;
1025
1026            inputBuffer.setSwallowInput(true);
1027            try {
1028                outputBuffer.sendAck();
1029            } catch (IOException JavaDoc e) {
1030                // Set error flag
1031
error = true;
1032            }
1033
1034        } else if (actionCode == ActionCode.ACTION_CLIENT_FLUSH) {
1035
1036            try {
1037                outputBuffer.flush();
1038            } catch (IOException JavaDoc e) {
1039                // Set error flag
1040
error = true;
1041                response.setErrorException(e);
1042            }
1043
1044        } else if (actionCode == ActionCode.ACTION_CLOSE) {
1045            // Close
1046

1047            // End the processing of the current request, and stop any further
1048
// transactions with the client
1049

1050            comet = false;
1051            cometClose = true;
1052            SelectionKey JavaDoc key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());
1053            if ( key != null ) {
1054                NioEndpoint.KeyAttachment attach = (NioEndpoint.KeyAttachment) key.attachment();
1055                if ( attach!=null && attach.getComet()) {
1056                    //if this is a comet connection
1057
//then execute the connection closure at the next selector loop
1058
request.getAttributes().remove("org.apache.tomcat.comet.timeout");
1059                    attach.setError(true);
1060                    attach.setComet(false);
1061                }
1062            }
1063
1064            try {
1065                outputBuffer.endRequest();
1066            } catch (IOException JavaDoc e) {
1067                // Set error flag
1068
error = true;
1069            }
1070
1071        } else if (actionCode == ActionCode.ACTION_RESET) {
1072
1073            // Reset response
1074

1075            // Note: This must be called before the response is committed
1076

1077            outputBuffer.reset();
1078
1079        } else if (actionCode == ActionCode.ACTION_CUSTOM) {
1080
1081            // Do nothing
1082

1083        } else if (actionCode == ActionCode.ACTION_REQ_HOST_ADDR_ATTRIBUTE) {
1084
1085            // Get remote host address
1086
if ((remoteAddr == null) && (socket != null)) {
1087                InetAddress JavaDoc inetAddr = socket.getIOChannel().socket().getInetAddress();
1088                if (inetAddr != null) {
1089                    remoteAddr = inetAddr.getHostAddress();
1090                }
1091            }
1092            request.remoteAddr().setString(remoteAddr);
1093
1094        } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_NAME_ATTRIBUTE) {
1095
1096            // Get local host name
1097
if ((localName == null) && (socket != null)) {
1098                InetAddress JavaDoc inetAddr = socket.getIOChannel().socket().getLocalAddress();
1099                if (inetAddr != null) {
1100                    localName = inetAddr.getHostName();
1101                }
1102            }
1103            request.localName().setString(localName);
1104
1105        } else if (actionCode == ActionCode.ACTION_REQ_HOST_ATTRIBUTE) {
1106
1107            // Get remote host name
1108
if ((remoteHost == null) && (socket != null)) {
1109                InetAddress JavaDoc inetAddr = socket.getIOChannel().socket().getInetAddress();
1110                if (inetAddr != null) {
1111                    remoteHost = inetAddr.getHostName();
1112                }
1113                if(remoteHost == null) {
1114                    if(remoteAddr != null) {
1115                        remoteHost = remoteAddr;
1116                    } else { // all we can do is punt
1117
request.remoteHost().recycle();
1118                    }
1119                }
1120            }
1121            request.remoteHost().setString(remoteHost);
1122
1123        } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_ADDR_ATTRIBUTE) {
1124
1125            if (localAddr == null)
1126               localAddr = socket.getIOChannel().socket().getLocalAddress().getHostAddress();
1127
1128            request.localAddr().setString(localAddr);
1129
1130        } else if (actionCode == ActionCode.ACTION_REQ_REMOTEPORT_ATTRIBUTE) {
1131
1132            if ((remotePort == -1 ) && (socket !=null)) {
1133                remotePort = socket.getIOChannel().socket().getPort();
1134            }
1135            request.setRemotePort(remotePort);
1136
1137        } else if (actionCode == ActionCode.ACTION_REQ_LOCALPORT_ATTRIBUTE) {
1138
1139            if ((localPort == -1 ) && (socket !=null)) {
1140                localPort = socket.getIOChannel().socket().getLocalPort();
1141            }
1142            request.setLocalPort(localPort);
1143
1144        } else if (actionCode == ActionCode.ACTION_REQ_SSL_ATTRIBUTE ) {
1145
1146            try {
1147                if (sslSupport != null) {
1148                    Object JavaDoc sslO = sslSupport.getCipherSuite();
1149                    if (sslO != null)
1150                        request.setAttribute
1151                            (SSLSupport.CIPHER_SUITE_KEY, sslO);
1152                    sslO = sslSupport.getPeerCertificateChain(false);
1153                    if (sslO != null)
1154                        request.setAttribute
1155                            (SSLSupport.CERTIFICATE_KEY, sslO);
1156                    sslO = sslSupport.getKeySize();
1157                    if (sslO != null)
1158                        request.setAttribute
1159                            (SSLSupport.KEY_SIZE_KEY, sslO);
1160                    sslO = sslSupport.getSessionId();
1161                    if (sslO != null)
1162                        request.setAttribute
1163                            (SSLSupport.SESSION_ID_KEY, sslO);
1164                }
1165            } catch (Exception JavaDoc e) {
1166                log.warn(sm.getString("http11processor.socket.ssl"), e);
1167            }
1168
1169        } else if (actionCode == ActionCode.ACTION_REQ_SSL_CERTIFICATE) {
1170
1171            if( sslSupport != null) {
1172                /*
1173                 * Consume and buffer the request body, so that it does not
1174                 * interfere with the client's handshake messages
1175                 */

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                    Object JavaDoc sslO = sslSupport.getPeerCertificateChain(true);
1183                    if( sslO != null) {
1184                        request.setAttribute
1185                            (SSLSupport.CERTIFICATE_KEY, sslO);
1186                    }
1187                } catch (Exception JavaDoc e) {
1188                    log.warn(sm.getString("http11processor.socket.ssl"), e);
1189                }
1190            }
1191
1192        } else if (actionCode == ActionCode.ACTION_REQ_SET_BODY_REPLAY) {
1193            ByteChunk body = (ByteChunk) param;
1194
1195            InputFilter savedBody = new SavedRequestInputFilter(body);
1196            savedBody.setRequest(request);
1197
1198            InternalNioInputBuffer internalBuffer = (InternalNioInputBuffer)
1199                request.getInputBuffer();
1200            internalBuffer.addActiveFilter(savedBody);
1201
1202        } else if (actionCode == ActionCode.ACTION_COMET_BEGIN) {
1203            comet = true;
1204        } else if (actionCode == ActionCode.ACTION_COMET_END) {
1205            comet = false;
1206        }
1207
1208    }
1209
1210
1211    // ------------------------------------------------------ Connector Methods
1212

1213
1214    /**
1215     * Set the associated adapter.
1216     *
1217     * @param adapter the new adapter
1218     */

1219    public void setAdapter(Adapter adapter) {
1220        this.adapter = adapter;
1221    }
1222
1223    public void setSslSupport(SSLSupport sslSupport) {
1224        this.sslSupport = sslSupport;
1225    }
1226
1227    /**
1228     * Get the associated adapter.
1229     *
1230     * @return the associated adapter
1231     */

1232    public Adapter getAdapter() {
1233        return adapter;
1234    }
1235
1236    public SSLSupport getSslSupport() {
1237        return sslSupport;
1238    }
1239
1240    // ------------------------------------------------------ Protected Methods
1241

1242
1243    /**
1244     * After reading the request headers, we have to setup the request filters.
1245     */

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

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

1495    private boolean isCompressable() {
1496
1497        // Nope Compression could works in HTTP 1.0 also
1498
// cf: mod_deflate
1499

1500        // Compression only since HTTP 1.1
1501
// if (! http11)
1502
// return false;
1503

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

1557    protected void prepareResponse() throws IOException JavaDoc {
1558
1559        boolean entityBody = true;
1560        contentDelimitation = false;
1561
1562        OutputFilter[] outputFilters = outputBuffer.getFilters();
1563
1564        if (http09 == true) {
1565            // HTTP/0.9
1566
outputBuffer.addActiveFilter
1567                (outputFilters[Constants.IDENTITY_FILTER]);
1568            return;
1569        }
1570
1571        int statusCode = response.getStatus();
1572        if ((statusCode == 204) || (statusCode == 205)
1573            || (statusCode == 304)) {
1574            // No entity body
1575
outputBuffer.addActiveFilter
1576                (outputFilters[Constants.VOID_FILTER]);
1577            entityBody = false;
1578            contentDelimitation = true;
1579        }
1580
1581        MessageBytes methodMB = request.method();
1582        if (methodMB.equals("HEAD")) {
1583            // No entity body
1584
outputBuffer.addActiveFilter
1585                (outputFilters[Constants.VOID_FILTER]);
1586            contentDelimitation = true;
1587        }
1588
1589
1590        // Check for compression
1591
boolean useCompression = false;
1592        if (entityBody && (compressionLevel > 0)) {
1593            useCompression = isCompressable();
1594            // Change content-length to -1 to force chunking
1595
if (useCompression) {
1596                response.setContentLength(-1);
1597            }
1598        }
1599
1600        MimeHeaders headers = response.getMimeHeaders();
1601        if (!entityBody) {
1602            response.setContentLength(-1);
1603        } else {
1604            String JavaDoc contentType = response.getContentType();
1605            if (contentType != null) {
1606                headers.setValue("Content-Type").setString(contentType);
1607            }
1608            String JavaDoc contentLanguage = response.getContentLanguage();
1609            if (contentLanguage != null) {
1610                headers.setValue("Content-Language")
1611                    .setString(contentLanguage);
1612            }
1613        }
1614
1615        long contentLength = response.getContentLengthLong();
1616        if (contentLength != -1) {
1617            headers.setValue("Content-Length").setLong(contentLength);
1618            outputBuffer.addActiveFilter
1619                (outputFilters[Constants.IDENTITY_FILTER]);
1620            contentDelimitation = true;
1621        } else {
1622            if (entityBody && http11 && keepAlive) {
1623                outputBuffer.addActiveFilter
1624                    (outputFilters[Constants.CHUNKED_FILTER]);
1625                contentDelimitation = true;
1626                headers.addValue(Constants.TRANSFERENCODING).setString(Constants.CHUNKED);
1627            } else {
1628                outputBuffer.addActiveFilter
1629                    (outputFilters[Constants.IDENTITY_FILTER]);
1630            }
1631        }
1632
1633        if (useCompression) {
1634            outputBuffer.addActiveFilter(outputFilters[Constants.GZIP_FILTER]);
1635            headers.setValue("Content-Encoding").setString("gzip");
1636            // Make Proxies happy via Vary (from mod_deflate)
1637
headers.setValue("Vary").setString("Accept-Encoding");
1638        }
1639
1640        // Add date header
1641
headers.setValue("Date").setString(FastHttpDateFormat.getCurrentDate());
1642
1643        // FIXME: Add transfer encoding header
1644

1645        if ((entityBody) && (!contentDelimitation)) {
1646            // Mark as close the connection after the request, and add the
1647
// connection: close header
1648
keepAlive = false;
1649        }
1650
1651        // If we know that the request is bad this early, add the
1652
// Connection: close header.
1653
keepAlive = keepAlive && !statusDropsConnection(statusCode);
1654        if (!keepAlive) {
1655            headers.addValue(Constants.CONNECTION).setString(Constants.CLOSE);
1656        } else if (!http11 && !error) {
1657            headers.addValue(Constants.CONNECTION).setString(Constants.KEEPALIVE);
1658        }
1659
1660        // Build the response header
1661
outputBuffer.sendStatus();
1662
1663        // Add server header
1664
if (server != null) {
1665            headers.setValue("Server").setString(server);
1666        } else {
1667            outputBuffer.write(Constants.SERVER_BYTES);
1668        }
1669
1670        int size = headers.size();
1671        for (int i = 0; i < size; i++) {
1672            outputBuffer.sendHeader(headers.getName(i), headers.getValue(i));
1673        }
1674        outputBuffer.endHeaders();
1675
1676    }
1677
1678
1679    /**
1680     * Initialize standard input and output filters.
1681     */

1682    protected void initializeFilters() {
1683
1684        // Create and add the identity filters.
1685
inputBuffer.addFilter(new IdentityInputFilter());
1686        outputBuffer.addFilter(new IdentityOutputFilter());
1687
1688        // Create and add the chunked filters.
1689
inputBuffer.addFilter(new ChunkedInputFilter());
1690        outputBuffer.addFilter(new ChunkedOutputFilter());
1691
1692        // Create and add the void filters.
1693
inputBuffer.addFilter(new VoidInputFilter());
1694        outputBuffer.addFilter(new VoidOutputFilter());
1695
1696        // Create and add buffered input filter
1697
inputBuffer.addFilter(new BufferedInputFilter());
1698
1699        // Create and add the chunked filters.
1700
//inputBuffer.addFilter(new GzipInputFilter());
1701
outputBuffer.addFilter(new GzipOutputFilter());
1702
1703    }
1704
1705
1706    /**
1707     * Add an input filter to the current request.
1708     *
1709     * @return false if the encoding was not found (which would mean it is
1710     * unsupported)
1711     */

1712    protected boolean addInputFilter(InputFilter[] inputFilters,
1713                                     String JavaDoc encodingName) {
1714        if (encodingName.equals("identity")) {
1715            // Skip
1716
} else if (encodingName.equals("chunked")) {
1717            inputBuffer.addActiveFilter
1718                (inputFilters[Constants.CHUNKED_FILTER]);
1719            contentDelimitation = true;
1720        } else {
1721            for (int i = 2; i < inputFilters.length; i++) {
1722                if (inputFilters[i].getEncodingName()
1723                    .toString().equals(encodingName)) {
1724                    inputBuffer.addActiveFilter(inputFilters[i]);
1725                    return true;
1726                }
1727            }
1728            return false;
1729        }
1730        return true;
1731    }
1732
1733
1734    /**
1735     * Specialized utility method: find a sequence of lower case bytes inside
1736     * a ByteChunk.
1737     */

1738    protected int findBytes(ByteChunk bc, byte[] b) {
1739
1740        byte first = b[0];
1741        byte[] buff = bc.getBuffer();
1742        int start = bc.getStart();
1743        int end = bc.getEnd();
1744
1745    // Look for first char
1746
int srcEnd = b.length;
1747
1748    for (int i = start; i <= (end - srcEnd); i++) {
1749        if (Ascii.toLower(buff[i]) != first) continue;
1750        // found first char, now look for a match
1751
int myPos = i+1;
1752        for (int srcPos = 1; srcPos < srcEnd; ) {
1753                if (Ascii.toLower(buff[myPos++]) != b[srcPos++])
1754            break;
1755                if (srcPos == srcEnd) return i - start; // found it
1756
}
1757    }
1758    return -1;
1759
1760    }
1761
1762    /**
1763     * Determine if we must drop the connection because of the HTTP status
1764     * code. Use the same list of codes as Apache/httpd.
1765     */

1766    protected boolean statusDropsConnection(int status) {
1767        return status == 400 /* SC_BAD_REQUEST */ ||
1768               status == 408 /* SC_REQUEST_TIMEOUT */ ||
1769               status == 411 /* SC_LENGTH_REQUIRED */ ||
1770               status == 413 /* SC_REQUEST_ENTITY_TOO_LARGE */ ||
1771               status == 414 /* SC_REQUEST_URI_TOO_LARGE */ ||
1772               status == 500 /* SC_INTERNAL_SERVER_ERROR */ ||
1773               status == 503 /* SC_SERVICE_UNAVAILABLE */ ||
1774               status == 501 /* SC_NOT_IMPLEMENTED */;
1775    }
1776
1777}
1778
Popular Tags