KickJava   Java API By Example, From Geeks To Geeks.

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


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.net.Socket JavaDoc;
24 import java.security.AccessController JavaDoc;
25 import java.security.PrivilegedAction JavaDoc;
26 import java.util.StringTokenizer JavaDoc;
27 import java.util.regex.Pattern JavaDoc;
28 import java.util.regex.PatternSyntaxException JavaDoc;
29
30 import org.apache.coyote.ActionCode;
31 import org.apache.coyote.ActionHook;
32 import org.apache.coyote.Adapter;
33 import org.apache.coyote.Request;
34 import org.apache.coyote.RequestInfo;
35 import org.apache.coyote.Response;
36 import org.apache.coyote.http11.filters.BufferedInputFilter;
37 import org.apache.coyote.http11.filters.ChunkedInputFilter;
38 import org.apache.coyote.http11.filters.ChunkedOutputFilter;
39 import org.apache.coyote.http11.filters.GzipOutputFilter;
40 import org.apache.coyote.http11.filters.IdentityInputFilter;
41 import org.apache.coyote.http11.filters.IdentityOutputFilter;
42 import org.apache.coyote.http11.filters.SavedRequestInputFilter;
43 import org.apache.coyote.http11.filters.VoidInputFilter;
44 import org.apache.coyote.http11.filters.VoidOutputFilter;
45 import org.apache.tomcat.util.buf.Ascii;
46 import org.apache.tomcat.util.buf.ByteChunk;
47 import org.apache.tomcat.util.buf.HexUtils;
48 import org.apache.tomcat.util.buf.MessageBytes;
49 import org.apache.tomcat.util.http.FastHttpDateFormat;
50 import org.apache.tomcat.util.http.MimeHeaders;
51 import org.apache.tomcat.util.net.JIoEndpoint;
52 import org.apache.tomcat.util.net.SSLSupport;
53 import org.apache.tomcat.util.res.StringManager;
54
55
56 /**
57  * Processes HTTP requests.
58  *
59  * @author Remy Maucherat
60  */

61 public class Http11Processor implements ActionHook {
62
63
64     /**
65      * Logger.
66      */

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

73     protected static StringManager sm =
74         StringManager.getManager(Constants.Package);
75
76
77     // ------------------------------------------------------------ Constructor
78

79
80     public Http11Processor(int headerBufferSize, JIoEndpoint endpoint) {
81
82         this.endpoint = endpoint;
83         
84         request = new Request();
85         inputBuffer = new InternalInputBuffer(request, headerBufferSize);
86         request.setInputBuffer(inputBuffer);
87
88         response = new Response();
89         response.setHook(this);
90         outputBuffer = new InternalOutputBuffer(response, headerBufferSize);
91         response.setOutputBuffer(outputBuffer);
92         request.setResponse(response);
93
94         initializeFilters();
95
96         // Cause loading of HexUtils
97
int foo = HexUtils.DEC[0];
98
99     }
100
101
102     // ----------------------------------------------------- Instance Variables
103

104
105     /**
106      * Associated adapter.
107      */

108     protected Adapter adapter = null;
109
110
111     /**
112      * Request object.
113      */

114     protected Request request = null;
115
116
117     /**
118      * Response object.
119      */

120     protected Response response = null;
121
122
123     /**
124      * Input.
125      */

126     protected InternalInputBuffer inputBuffer = null;
127
128
129     /**
130      * Output.
131      */

132     protected InternalOutputBuffer outputBuffer = null;
133
134
135     /**
136      * State flag.
137      */

138     protected boolean started = false;
139
140
141     /**
142      * Error flag.
143      */

144     protected boolean error = false;
145
146
147     /**
148      * Keep-alive.
149      */

150     protected boolean keepAlive = true;
151
152
153     /**
154      * HTTP/1.1 flag.
155      */

156     protected boolean http11 = true;
157
158
159     /**
160      * HTTP/0.9 flag.
161      */

162     protected boolean http09 = false;
163
164
165     /**
166      * Content delimitator for the request (if false, the connection will
167      * be closed at the end of the request).
168      */

169     protected boolean contentDelimitation = true;
170
171
172     /**
173      * Is there an expectation ?
174      */

175     protected boolean expectation = false;
176
177
178     /**
179      * List of restricted user agents.
180      */

181     protected Pattern JavaDoc[] restrictedUserAgents = null;
182
183
184     /**
185      * Maximum number of Keep-Alive requests to honor.
186      */

187     protected int maxKeepAliveRequests = -1;
188
189
190     /**
191      * SSL information.
192      */

193     protected SSLSupport sslSupport;
194
195
196     /**
197      * Socket associated with the current connection.
198      */

199     protected Socket JavaDoc socket;
200
201
202     /**
203      * Remote Address associated with the current connection.
204      */

205     protected String JavaDoc remoteAddr = null;
206
207
208     /**
209      * Remote Host associated with the current connection.
210      */

211     protected String JavaDoc remoteHost = null;
212
213
214     /**
215      * Local Host associated with the current connection.
216      */

217     protected String JavaDoc localName = null;
218
219
220
221     /**
222      * Local port to which the socket is connected
223      */

224     protected int localPort = -1;
225
226
227     /**
228      * Remote port to which the socket is connected
229      */

230     protected int remotePort = -1;
231
232
233     /**
234      * The local Host address.
235      */

236     protected String JavaDoc localAddr = null;
237
238
239     /**
240      * Maximum timeout on uploads. 5 minutes as in Apache HTTPD server.
241      */

242     protected int timeout = 300000;
243
244
245     /**
246      * Flag to disable setting a different time-out on uploads.
247      */

248     protected boolean disableUploadTimeout = false;
249
250
251     /**
252      * Allowed compression level.
253      */

254     protected int compressionLevel = 0;
255
256
257     /**
258      * Minimum contentsize to make compression.
259      */

260     protected int compressionMinSize = 2048;
261
262
263     /**
264      * Socket buffering.
265      */

266     protected int socketBuffer = -1;
267
268
269     /**
270      * Max saved post size.
271      */

272     protected int maxSavePostSize = 4 * 1024;
273
274
275     /**
276      * List of user agents to not use gzip with
277      */

278     protected Pattern JavaDoc noCompressionUserAgents[] = null;
279
280     /**
281      * List of MIMES which could be gzipped
282      */

283     protected String JavaDoc[] compressableMimeTypes =
284     { "text/html", "text/xml", "text/plain" };
285
286
287     /**
288      * Host name (used to avoid useless B2C conversion on the host name).
289      */

290     protected char[] hostNameC = new char[0];
291
292
293     /**
294      * Associated endpoint.
295      */

296     protected JIoEndpoint endpoint;
297
298
299     /**
300      * Allow a customized the server header for the tin-foil hat folks.
301      */

302     protected String JavaDoc server = null;
303
304
305     // ------------------------------------------------------------- Properties
306

307
308     /**
309      * Return compression level.
310      */

311     public String JavaDoc getCompression() {
312         switch (compressionLevel) {
313         case 0:
314             return "off";
315         case 1:
316             return "on";
317         case 2:
318             return "force";
319         }
320         return "off";
321     }
322
323
324     /**
325      * Set compression level.
326      */

327     public void setCompression(String JavaDoc compression) {
328         if (compression.equals("on")) {
329             this.compressionLevel = 1;
330         } else if (compression.equals("force")) {
331             this.compressionLevel = 2;
332         } else if (compression.equals("off")) {
333             this.compressionLevel = 0;
334         } else {
335             try {
336                 // Try to parse compression as an int, which would give the
337
// minimum compression size
338
compressionMinSize = Integer.parseInt(compression);
339                 this.compressionLevel = 1;
340             } catch (Exception JavaDoc e) {
341                 this.compressionLevel = 0;
342             }
343         }
344     }
345
346     /**
347      * Set Minimum size to trigger compression.
348      */

349     public void setCompressionMinSize(int compressionMinSize) {
350         this.compressionMinSize = compressionMinSize;
351     }
352
353
354     /**
355      * Add user-agent for which gzip compression didn't works
356      * The user agent String given will be exactly matched
357      * to the user-agent header submitted by the client.
358      *
359      * @param userAgent user-agent string
360      */

361     public void addNoCompressionUserAgent(String JavaDoc userAgent) {
362         try {
363             Pattern JavaDoc nRule = Pattern.compile(userAgent);
364             noCompressionUserAgents =
365                 addREArray(noCompressionUserAgents, nRule);
366         } catch (PatternSyntaxException JavaDoc pse) {
367             log.error(sm.getString("http11processor.regexp.error", userAgent), pse);
368         }
369     }
370
371
372     /**
373      * Set no compression user agent list (this method is best when used with
374      * a large number of connectors, where it would be better to have all of
375      * them referenced a single array).
376      */

377     public void setNoCompressionUserAgents(Pattern JavaDoc[] noCompressionUserAgents) {
378         this.noCompressionUserAgents = noCompressionUserAgents;
379     }
380
381
382     /**
383      * Set no compression user agent list.
384      * List contains users agents separated by ',' :
385      *
386      * ie: "gorilla,desesplorer,tigrus"
387      */

388     public void setNoCompressionUserAgents(String JavaDoc noCompressionUserAgents) {
389         if (noCompressionUserAgents != null) {
390             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(noCompressionUserAgents, ",");
391
392             while (st.hasMoreTokens()) {
393                 addNoCompressionUserAgent(st.nextToken().trim());
394             }
395         }
396     }
397
398     /**
399      * Add a mime-type which will be compressable
400      * The mime-type String will be exactly matched
401      * in the response mime-type header .
402      *
403      * @param mimeType mime-type string
404      */

405     public void addCompressableMimeType(String JavaDoc mimeType) {
406         compressableMimeTypes =
407             addStringArray(compressableMimeTypes, mimeType);
408     }
409
410
411     /**
412      * Set compressable mime-type list (this method is best when used with
413      * a large number of connectors, where it would be better to have all of
414      * them referenced a single array).
415      */

416     public void setCompressableMimeTypes(String JavaDoc[] compressableMimeTypes) {
417         this.compressableMimeTypes = compressableMimeTypes;
418     }
419
420
421     /**
422      * Set compressable mime-type list
423      * List contains users agents separated by ',' :
424      *
425      * ie: "text/html,text/xml,text/plain"
426      */

427     public void setCompressableMimeTypes(String JavaDoc compressableMimeTypes) {
428         if (compressableMimeTypes != null) {
429             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(compressableMimeTypes, ",");
430
431             while (st.hasMoreTokens()) {
432                 addCompressableMimeType(st.nextToken().trim());
433             }
434         }
435     }
436
437
438     /**
439      * Return the list of restricted user agents.
440      */

441     public String JavaDoc[] findCompressableMimeTypes() {
442         return (compressableMimeTypes);
443     }
444
445
446
447     // --------------------------------------------------------- Public Methods
448

449
450     /**
451      * Add input or output filter.
452      *
453      * @param className class name of the filter
454      */

455     protected void addFilter(String JavaDoc className) {
456         try {
457             Class JavaDoc clazz = Class.forName(className);
458             Object JavaDoc obj = clazz.newInstance();
459             if (obj instanceof InputFilter) {
460                 inputBuffer.addFilter((InputFilter) obj);
461             } else if (obj instanceof OutputFilter) {
462                 outputBuffer.addFilter((OutputFilter) obj);
463             } else {
464                 log.warn(sm.getString("http11processor.filter.unknown", className));
465             }
466         } catch (Exception JavaDoc e) {
467             log.error(sm.getString("http11processor.filter.error", className), e);
468         }
469     }
470
471
472     /**
473      * General use method
474      *
475      * @param sArray the StringArray
476      * @param value string
477      */

478     private String JavaDoc[] addStringArray(String JavaDoc sArray[], String JavaDoc value) {
479         String JavaDoc[] result = null;
480         if (sArray == null) {
481             result = new String JavaDoc[1];
482             result[0] = value;
483         }
484         else {
485             result = new String JavaDoc[sArray.length + 1];
486             for (int i = 0; i < sArray.length; i++)
487                 result[i] = sArray[i];
488             result[sArray.length] = value;
489         }
490         return result;
491     }
492
493
494     /**
495      * General use method
496      *
497      * @param rArray the REArray
498      * @param value Obj
499      */

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

522     private boolean inStringArray(String JavaDoc sArray[], String JavaDoc value) {
523         for (int i = 0; i < sArray.length; i++) {
524             if (sArray[i].equals(value)) {
525                 return true;
526             }
527         }
528         return false;
529     }
530
531
532     /**
533      * Checks if any entry in the string array starts with the specified value
534      *
535      * @param sArray the StringArray
536      * @param value string
537      */

538     private boolean startsWithStringArray(String JavaDoc sArray[], String JavaDoc value) {
539         if (value == null)
540            return false;
541         for (int i = 0; i < sArray.length; i++) {
542             if (value.startsWith(sArray[i])) {
543                 return true;
544             }
545         }
546         return false;
547     }
548
549
550     /**
551      * Add restricted user-agent (which will downgrade the connector
552      * to HTTP/1.0 mode). The user agent String given will be matched
553      * via regexp to the user-agent header submitted by the client.
554      *
555      * @param userAgent user-agent string
556      */

557     public void addRestrictedUserAgent(String JavaDoc userAgent) {
558         try {
559             Pattern JavaDoc nRule = Pattern.compile(userAgent);
560             restrictedUserAgents = addREArray(restrictedUserAgents, nRule);
561         } catch (PatternSyntaxException JavaDoc pse) {
562             log.error(sm.getString("http11processor.regexp.error", userAgent), pse);
563         }
564     }
565
566
567     /**
568      * Set restricted user agent list (this method is best when used with
569      * a large number of connectors, where it would be better to have all of
570      * them referenced a single array).
571      */

572     public void setRestrictedUserAgents(Pattern JavaDoc[] restrictedUserAgents) {
573         this.restrictedUserAgents = restrictedUserAgents;
574     }
575
576
577     /**
578      * Set restricted user agent list (which will downgrade the connector
579      * to HTTP/1.0 mode). List contains users agents separated by ',' :
580      *
581      * ie: "gorilla,desesplorer,tigrus"
582      */

583     public void setRestrictedUserAgents(String JavaDoc restrictedUserAgents) {
584         if (restrictedUserAgents != null) {
585             StringTokenizer JavaDoc st =
586                 new StringTokenizer JavaDoc(restrictedUserAgents, ",");
587             while (st.hasMoreTokens()) {
588                 addRestrictedUserAgent(st.nextToken().trim());
589             }
590         }
591     }
592
593
594     /**
595      * Return the list of restricted user agents.
596      */

597     public String JavaDoc[] findRestrictedUserAgents() {
598         String JavaDoc[] sarr = new String JavaDoc [restrictedUserAgents.length];
599
600         for (int i = 0; i < restrictedUserAgents.length; i++)
601             sarr[i] = restrictedUserAgents[i].toString();
602
603         return (sarr);
604     }
605
606
607     /**
608      * Set the maximum number of Keep-Alive requests to honor.
609      * This is to safeguard from DoS attacks. Setting to a negative
610      * value disables the check.
611      */

612     public void setMaxKeepAliveRequests(int mkar) {
613         maxKeepAliveRequests = mkar;
614     }
615
616
617     /**
618      * Return the number of Keep-Alive requests that we will honor.
619      */

620     public int getMaxKeepAliveRequests() {
621         return maxKeepAliveRequests;
622     }
623
624
625     /**
626      * Set the maximum size of a POST which will be buffered in SSL mode.
627      */

628     public void setMaxSavePostSize(int msps) {
629         maxSavePostSize = msps;
630     }
631
632
633     /**
634      * Return the maximum size of a POST which will be buffered in SSL mode.
635      */

636     public int getMaxSavePostSize() {
637         return maxSavePostSize;
638     }
639
640
641     /**
642      * Set the SSL information for this HTTP connection.
643      */

644     public void setSSLSupport(SSLSupport sslSupport) {
645         this.sslSupport = sslSupport;
646     }
647
648
649     /**
650      * Set the flag to control upload time-outs.
651      */

652     public void setDisableUploadTimeout(boolean isDisabled) {
653         disableUploadTimeout = isDisabled;
654     }
655
656     /**
657      * Get the flag that controls upload time-outs.
658      */

659     public boolean getDisableUploadTimeout() {
660         return disableUploadTimeout;
661     }
662
663     /**
664      * Set the socket buffer flag.
665      */

666     public void setSocketBuffer(int socketBuffer) {
667         this.socketBuffer = socketBuffer;
668         outputBuffer.setSocketBuffer(socketBuffer);
669     }
670
671     /**
672      * Get the socket buffer flag.
673      */

674     public int getSocketBuffer() {
675         return socketBuffer;
676     }
677
678     /**
679      * Set the upload timeout.
680      */

681     public void setTimeout( int timeouts ) {
682         timeout = timeouts ;
683     }
684
685     /**
686      * Get the upload timeout.
687      */

688     public int getTimeout() {
689         return timeout;
690     }
691
692
693     /**
694      * Set the server header name.
695      */

696     public void setServer( String JavaDoc server ) {
697         if (server==null || server.equals("")) {
698             this.server = null;
699         } else {
700             this.server = server;
701         }
702     }
703
704     /**
705      * Get the server header name.
706      */

707     public String JavaDoc getServer() {
708         return server;
709     }
710
711
712     /** Get the request associated with this processor.
713      *
714      * @return The request
715      */

716     public Request getRequest() {
717         return request;
718     }
719
720     /**
721      * Process pipelined HTTP requests using the specified input and output
722      * streams.
723      *
724      * @param input stream from which the HTTP requests will be read
725      * @param output stream which will be used to output the HTTP
726      * responses
727      * @throws IOException error during an I/O operation
728      */

729     public void process(Socket JavaDoc socket)
730         throws IOException JavaDoc {
731         RequestInfo rp = request.getRequestProcessor();
732         rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
733
734         // Set the remote address
735
remoteAddr = null;
736         remoteHost = null;
737         localAddr = null;
738         localName = null;
739         remotePort = -1;
740         localPort = -1;
741
742         // Setting up the I/O
743
this.socket = socket;
744         inputBuffer.setInputStream(socket.getInputStream());
745         outputBuffer.setOutputStream(socket.getOutputStream());
746
747         // Error flag
748
error = false;
749         keepAlive = true;
750
751         int keepAliveLeft = maxKeepAliveRequests;
752         int soTimeout = socket.getSoTimeout();
753         int oldSoTimeout = soTimeout;
754
755         int threadRatio = (endpoint.getCurrentThreadsBusy() * 100)
756                 / endpoint.getMaxThreads();
757         if (threadRatio > 75) {
758             keepAliveLeft = 1;
759         }
760         
761         if (soTimeout != oldSoTimeout) {
762             try {
763                 socket.setSoTimeout(soTimeout);
764             } catch (Throwable JavaDoc t) {
765                 log.debug(sm.getString("http11processor.socket.timeout"), t);
766                 error = true;
767             }
768         }
769
770         boolean keptAlive = false;
771
772         while (started && !error && keepAlive) {
773
774             // Parsing the request header
775
try {
776                 if( !disableUploadTimeout && keptAlive && soTimeout > 0 ) {
777                     socket.setSoTimeout(soTimeout);
778                 }
779                 inputBuffer.parseRequestLine();
780                 request.setStartTime(System.currentTimeMillis());
781                 keptAlive = true;
782                 if (!disableUploadTimeout) {
783                     socket.setSoTimeout(timeout);
784                 }
785                 inputBuffer.parseHeaders();
786             } catch (IOException JavaDoc e) {
787                 error = true;
788                 break;
789             } catch (Throwable JavaDoc t) {
790                 if (log.isDebugEnabled()) {
791                     log.debug(sm.getString("http11processor.header.parse"), t);
792                 }
793                 // 400 - Bad Request
794
response.setStatus(400);
795                 error = true;
796             }
797
798             // Setting up filters, and parse some request headers
799
rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
800             try {
801                 prepareRequest();
802             } catch (Throwable JavaDoc t) {
803                 if (log.isDebugEnabled()) {
804                     log.debug(sm.getString("http11processor.request.prepare"), t);
805                 }
806                 // 400 - Internal Server Error
807
response.setStatus(400);
808                 error = true;
809             }
810
811             if (maxKeepAliveRequests > 0 && --keepAliveLeft == 0)
812                 keepAlive = false;
813
814             // Process the request in the adapter
815
if (!error) {
816                 try {
817                     rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
818                     adapter.service(request, response);
819                     // Handle when the response was committed before a serious
820
// error occurred. Throwing a ServletException should both
821
// set the status to 500 and set the errorException.
822
// If we fail here, then the response is likely already
823
// committed, so we can't try and set headers.
824
if(keepAlive && !error) { // Avoid checking twice.
825
error = response.getErrorException() != null ||
826                                 statusDropsConnection(response.getStatus());
827                     }
828
829                 } catch (InterruptedIOException JavaDoc e) {
830                     error = true;
831                 } catch (Throwable JavaDoc t) {
832                     log.error(sm.getString("http11processor.request.process"), t);
833                     // 500 - Internal Server Error
834
response.setStatus(500);
835                     error = true;
836                 }
837             }
838
839             // Finish the handling of the request
840
try {
841                 rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);
842                 inputBuffer.endRequest();
843             } catch (IOException JavaDoc e) {
844                 error = true;
845             } catch (Throwable JavaDoc t) {
846                 log.error(sm.getString("http11processor.request.finish"), t);
847                 // 500 - Internal Server Error
848
response.setStatus(500);
849                 error = true;
850             }
851             try {
852                 rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);
853                 outputBuffer.endRequest();
854             } catch (IOException JavaDoc e) {
855                 error = true;
856             } catch (Throwable JavaDoc t) {
857                 log.error(sm.getString("http11processor.response.finish"), t);
858                 error = true;
859             }
860
861             // If there was an error, make sure the request is counted as
862
// and error, and update the statistics counter
863
if (error) {
864                 response.setStatus(500);
865             }
866             request.updateCounters();
867
868             rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
869
870             // Don't reset the param - we'll see it as ended. Next request
871
// will reset it
872
// thrA.setParam(null);
873
// Next request
874
inputBuffer.nextRequest();
875             outputBuffer.nextRequest();
876
877         }
878
879         rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
880
881         // Recycle
882
inputBuffer.recycle();
883         outputBuffer.recycle();
884
885         // Recycle ssl info
886
sslSupport = null;
887     }
888
889
890     // ----------------------------------------------------- ActionHook Methods
891

892
893     /**
894      * Send an action to the connector.
895      *
896      * @param actionCode Type of the action
897      * @param param Action parameter
898      */

899     public void action(ActionCode actionCode, Object JavaDoc param) {
900
901         if (actionCode == ActionCode.ACTION_COMMIT) {
902             // Commit current response
903

904             if (response.isCommitted())
905                 return;
906
907             // Validate and write response headers
908
prepareResponse();
909             try {
910                 outputBuffer.commit();
911             } catch (IOException JavaDoc e) {
912                 // Set error flag
913
error = true;
914             }
915
916         } else if (actionCode == ActionCode.ACTION_ACK) {
917
918             // Acknowlege request
919

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

923             if ((response.isCommitted()) || !expectation)
924                 return;
925
926             inputBuffer.setSwallowInput(true);
927             try {
928                 outputBuffer.sendAck();
929             } catch (IOException JavaDoc e) {
930                 // Set error flag
931
error = true;
932             }
933
934         } else if (actionCode == ActionCode.ACTION_CLIENT_FLUSH) {
935
936             try {
937                 outputBuffer.flush();
938             } catch (IOException JavaDoc e) {
939                 // Set error flag
940
error = true;
941                 response.setErrorException(e);
942             }
943
944         } else if (actionCode == ActionCode.ACTION_CLOSE) {
945             // Close
946

947             // End the processing of the current request, and stop any further
948
// transactions with the client
949

950             try {
951                 outputBuffer.endRequest();
952             } catch (IOException JavaDoc e) {
953                 // Set error flag
954
error = true;
955             }
956
957         } else if (actionCode == ActionCode.ACTION_RESET) {
958
959             // Reset response
960

961             // Note: This must be called before the response is committed
962

963             outputBuffer.reset();
964
965         } else if (actionCode == ActionCode.ACTION_CUSTOM) {
966
967             // Do nothing
968

969         } else if (actionCode == ActionCode.ACTION_START) {
970
971             started = true;
972
973         } else if (actionCode == ActionCode.ACTION_STOP) {
974
975             started = false;
976
977         } else if (actionCode == ActionCode.ACTION_REQ_SSL_ATTRIBUTE ) {
978
979             try {
980                 if (sslSupport != null) {
981                     Object JavaDoc sslO = sslSupport.getCipherSuite();
982                     if (sslO != null)
983                         request.setAttribute
984                             (SSLSupport.CIPHER_SUITE_KEY, sslO);
985                     sslO = sslSupport.getPeerCertificateChain(false);
986                     if (sslO != null)
987                         request.setAttribute
988                             (SSLSupport.CERTIFICATE_KEY, sslO);
989                     sslO = sslSupport.getKeySize();
990                     if (sslO != null)
991                         request.setAttribute
992                             (SSLSupport.KEY_SIZE_KEY, sslO);
993                     sslO = sslSupport.getSessionId();
994                     if (sslO != null)
995                         request.setAttribute
996                             (SSLSupport.SESSION_ID_KEY, sslO);
997                 }
998             } catch (Exception JavaDoc e) {
999                 log.warn(sm.getString("http11processor.socket.ssl"), e);
1000            }
1001
1002        } else if (actionCode == ActionCode.ACTION_REQ_HOST_ADDR_ATTRIBUTE) {
1003
1004            if ((remoteAddr == null) && (socket != null)) {
1005                InetAddress JavaDoc inetAddr = socket.getInetAddress();
1006                if (inetAddr != null) {
1007                    remoteAddr = inetAddr.getHostAddress();
1008                }
1009            }
1010            request.remoteAddr().setString(remoteAddr);
1011
1012        } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_NAME_ATTRIBUTE) {
1013
1014            if ((localName == null) && (socket != null)) {
1015                InetAddress JavaDoc inetAddr = socket.getLocalAddress();
1016                if (inetAddr != null) {
1017                    localName = inetAddr.getHostName();
1018                }
1019            }
1020            request.localName().setString(localName);
1021
1022        } else if (actionCode == ActionCode.ACTION_REQ_HOST_ATTRIBUTE) {
1023
1024            if ((remoteHost == null) && (socket != null)) {
1025                InetAddress JavaDoc inetAddr = socket.getInetAddress();
1026                if (inetAddr != null) {
1027                    remoteHost = inetAddr.getHostName();
1028                }
1029                if(remoteHost == null) {
1030                    if(remoteAddr != null) {
1031                        remoteHost = remoteAddr;
1032                    } else { // all we can do is punt
1033
request.remoteHost().recycle();
1034                    }
1035                }
1036            }
1037            request.remoteHost().setString(remoteHost);
1038
1039        } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_ADDR_ATTRIBUTE) {
1040
1041            if (localAddr == null)
1042               localAddr = socket.getLocalAddress().getHostAddress();
1043
1044            request.localAddr().setString(localAddr);
1045
1046        } else if (actionCode == ActionCode.ACTION_REQ_REMOTEPORT_ATTRIBUTE) {
1047
1048            if ((remotePort == -1 ) && (socket !=null)) {
1049                remotePort = socket.getPort();
1050            }
1051            request.setRemotePort(remotePort);
1052
1053        } else if (actionCode == ActionCode.ACTION_REQ_LOCALPORT_ATTRIBUTE) {
1054
1055            if ((localPort == -1 ) && (socket !=null)) {
1056                localPort = socket.getLocalPort();
1057            }
1058            request.setLocalPort(localPort);
1059
1060        } else if (actionCode == ActionCode.ACTION_REQ_SSL_CERTIFICATE) {
1061            if( sslSupport != null) {
1062                /*
1063                 * Consume and buffer the request body, so that it does not
1064                 * interfere with the client's handshake messages
1065                 */

1066                InputFilter[] inputFilters = inputBuffer.getFilters();
1067                ((BufferedInputFilter) inputFilters[Constants.BUFFERED_FILTER])
1068                    .setLimit(maxSavePostSize);
1069                inputBuffer.addActiveFilter
1070                    (inputFilters[Constants.BUFFERED_FILTER]);
1071                try {
1072                    Object JavaDoc sslO = sslSupport.getPeerCertificateChain(true);
1073                    if( sslO != null) {
1074                        request.setAttribute
1075                            (SSLSupport.CERTIFICATE_KEY, sslO);
1076                    }
1077                } catch (Exception JavaDoc e) {
1078                    log.warn(sm.getString("http11processor.socket.ssl"), e);
1079                }
1080            }
1081        } else if (actionCode == ActionCode.ACTION_REQ_SET_BODY_REPLAY) {
1082            ByteChunk body = (ByteChunk) param;
1083            
1084            InputFilter savedBody = new SavedRequestInputFilter(body);
1085            savedBody.setRequest(request);
1086
1087            InternalInputBuffer internalBuffer = (InternalInputBuffer)
1088                request.getInputBuffer();
1089            internalBuffer.addActiveFilter(savedBody);
1090        }
1091
1092    }
1093
1094
1095    // ------------------------------------------------------ Connector Methods
1096

1097
1098    /**
1099     * Set the associated adapter.
1100     *
1101     * @param adapter the new adapter
1102     */

1103    public void setAdapter(Adapter adapter) {
1104        this.adapter = adapter;
1105    }
1106
1107
1108    /**
1109     * Get the associated adapter.
1110     *
1111     * @return the associated adapter
1112     */

1113    public Adapter getAdapter() {
1114        return adapter;
1115    }
1116
1117
1118    // ------------------------------------------------------ Protected Methods
1119

1120
1121    /**
1122     * After reading the request headers, we have to setup the request filters.
1123     */

1124    protected void prepareRequest() {
1125
1126        http11 = true;
1127        http09 = false;
1128        contentDelimitation = false;
1129        expectation = false;
1130        if (sslSupport != null) {
1131            request.scheme().setString("https");
1132        }
1133        MessageBytes protocolMB = request.protocol();
1134        if (protocolMB.equals(Constants.HTTP_11)) {
1135            http11 = true;
1136            protocolMB.setString(Constants.HTTP_11);
1137        } else if (protocolMB.equals(Constants.HTTP_10)) {
1138            http11 = false;
1139            keepAlive = false;
1140            protocolMB.setString(Constants.HTTP_10);
1141        } else if (protocolMB.equals("")) {
1142            // HTTP/0.9
1143
http09 = true;
1144            http11 = false;
1145            keepAlive = false;
1146        } else {
1147            // Unsupported protocol
1148
http11 = false;
1149            error = true;
1150            // Send 505; Unsupported HTTP version
1151
response.setStatus(505);
1152        }
1153
1154        MessageBytes methodMB = request.method();
1155        if (methodMB.equals(Constants.GET)) {
1156            methodMB.setString(Constants.GET);
1157        } else if (methodMB.equals(Constants.POST)) {
1158            methodMB.setString(Constants.POST);
1159        }
1160
1161        MimeHeaders headers = request.getMimeHeaders();
1162
1163        // Check connection header
1164
MessageBytes connectionValueMB = headers.getValue("connection");
1165        if (connectionValueMB != null) {
1166            ByteChunk connectionValueBC = connectionValueMB.getByteChunk();
1167            if (findBytes(connectionValueBC, Constants.CLOSE_BYTES) != -1) {
1168                keepAlive = false;
1169            } else if (findBytes(connectionValueBC,
1170                                 Constants.KEEPALIVE_BYTES) != -1) {
1171                keepAlive = true;
1172            }
1173        }
1174
1175        MessageBytes expectMB = null;
1176        if (http11)
1177            expectMB = headers.getValue("expect");
1178        if ((expectMB != null)
1179            && (expectMB.indexOfIgnoreCase("100-continue", 0) != -1)) {
1180            inputBuffer.setSwallowInput(false);
1181            expectation = true;
1182        }
1183
1184        // Check user-agent header
1185
if ((restrictedUserAgents != null) && ((http11) || (keepAlive))) {
1186            MessageBytes userAgentValueMB = headers.getValue("user-agent");
1187            // Check in the restricted list, and adjust the http11
1188
// and keepAlive flags accordingly
1189
if(userAgentValueMB != null) {
1190                String JavaDoc userAgentValue = userAgentValueMB.toString();
1191                for (int i = 0; i < restrictedUserAgents.length; i++) {
1192                    if (restrictedUserAgents[i].matcher(userAgentValue).matches()) {
1193                        http11 = false;
1194                        keepAlive = false;
1195                        break;
1196                    }
1197                }
1198            }
1199        }
1200
1201        // Check for a full URI (including protocol://host:port/)
1202
ByteChunk uriBC = request.requestURI().getByteChunk();
1203        if (uriBC.startsWithIgnoreCase("http", 0)) {
1204
1205            int pos = uriBC.indexOf("://", 0, 3, 4);
1206            int uriBCStart = uriBC.getStart();
1207            int slashPos = -1;
1208            if (pos != -1) {
1209                byte[] uriB = uriBC.getBytes();
1210                slashPos = uriBC.indexOf('/', pos + 3);
1211                if (slashPos == -1) {
1212                    slashPos = uriBC.getLength();
1213                    // Set URI as "/"
1214
request.requestURI().setBytes
1215                        (uriB, uriBCStart + pos + 1, 1);
1216                } else {
1217                    request.requestURI().setBytes
1218                        (uriB, uriBCStart + slashPos,
1219                         uriBC.getLength() - slashPos);
1220                }
1221                MessageBytes hostMB = headers.setValue("host");
1222                hostMB.setBytes(uriB, uriBCStart + pos + 3,
1223                                slashPos - pos - 3);
1224            }
1225
1226        }
1227
1228        // Input filter setup
1229
InputFilter[] inputFilters = inputBuffer.getFilters();
1230
1231        // Parse transfer-encoding header
1232
MessageBytes transferEncodingValueMB = null;
1233        if (http11)
1234            transferEncodingValueMB = headers.getValue("transfer-encoding");
1235        if (transferEncodingValueMB != null) {
1236            String JavaDoc transferEncodingValue = transferEncodingValueMB.toString();
1237            // Parse the comma separated list. "identity" codings are ignored
1238
int startPos = 0;
1239            int commaPos = transferEncodingValue.indexOf(',');
1240            String JavaDoc encodingName = null;
1241            while (commaPos != -1) {
1242                encodingName = transferEncodingValue.substring
1243                    (startPos, commaPos).toLowerCase().trim();
1244                if (!addInputFilter(inputFilters, encodingName)) {
1245                    // Unsupported transfer encoding
1246
error = true;
1247                    // 501 - Unimplemented
1248
response.setStatus(501);
1249                }
1250                startPos = commaPos + 1;
1251                commaPos = transferEncodingValue.indexOf(',', startPos);
1252            }
1253            encodingName = transferEncodingValue.substring(startPos)
1254                .toLowerCase().trim();
1255            if (!addInputFilter(inputFilters, encodingName)) {
1256                // Unsupported transfer encoding
1257
error = true;
1258                // 501 - Unimplemented
1259
response.setStatus(501);
1260            }
1261        }
1262
1263        // Parse content-length header
1264
long contentLength = request.getContentLengthLong();
1265        if (contentLength >= 0 && !contentDelimitation) {
1266            inputBuffer.addActiveFilter
1267                (inputFilters[Constants.IDENTITY_FILTER]);
1268            contentDelimitation = true;
1269        }
1270
1271        MessageBytes valueMB = headers.getValue("host");
1272
1273        // Check host header
1274
if (http11 && (valueMB == null)) {
1275            error = true;
1276            // 400 - Bad request
1277
response.setStatus(400);
1278        }
1279
1280        parseHost(valueMB);
1281
1282        if (!contentDelimitation) {
1283            // If there's no content length
1284
// (broken HTTP/1.0 or HTTP/1.1), assume
1285
// the client is not broken and didn't send a body
1286
inputBuffer.addActiveFilter
1287                    (inputFilters[Constants.VOID_FILTER]);
1288            contentDelimitation = true;
1289        }
1290
1291    }
1292
1293
1294    /**
1295     * Parse host.
1296     */

1297    public void parseHost(MessageBytes valueMB) {
1298
1299        if (valueMB == null || valueMB.isNull()) {
1300            // HTTP/1.0
1301
// Default is what the socket tells us. Overriden if a host is
1302
// found/parsed
1303
request.setServerPort(socket.getLocalPort());
1304            InetAddress JavaDoc localAddress = socket.getLocalAddress();
1305            // Setting the socket-related fields. The adapter doesn't know
1306
// about socket.
1307
request.serverName().setString(localAddress.getHostName());
1308            return;
1309        }
1310
1311        ByteChunk valueBC = valueMB.getByteChunk();
1312        byte[] valueB = valueBC.getBytes();
1313        int valueL = valueBC.getLength();
1314        int valueS = valueBC.getStart();
1315        int colonPos = -1;
1316        if (hostNameC.length < valueL) {
1317            hostNameC = new char[valueL];
1318        }
1319
1320        boolean ipv6 = (valueB[valueS] == '[');
1321        boolean bracketClosed = false;
1322        for (int i = 0; i < valueL; i++) {
1323            char b = (char) valueB[i + valueS];
1324            hostNameC[i] = b;
1325            if (b == ']') {
1326                bracketClosed = true;
1327            } else if (b == ':') {
1328                if (!ipv6 || bracketClosed) {
1329                    colonPos = i;
1330                    break;
1331                }
1332            }
1333        }
1334
1335        if (colonPos < 0) {
1336            if (sslSupport == null) {
1337                // 80 - Default HTTP port
1338
request.setServerPort(80);
1339            } else {
1340                // 443 - Default HTTPS port
1341
request.setServerPort(443);
1342            }
1343            request.serverName().setChars(hostNameC, 0, valueL);
1344        } else {
1345
1346            request.serverName().setChars(hostNameC, 0, colonPos);
1347
1348            int port = 0;
1349            int mult = 1;
1350            for (int i = valueL - 1; i > colonPos; i--) {
1351                int charValue = HexUtils.DEC[(int) valueB[i + valueS]];
1352                if (charValue == -1) {
1353                    // Invalid character
1354
error = true;
1355                    // 400 - Bad request
1356
response.setStatus(400);
1357                    break;
1358                }
1359                port = port + (charValue * mult);
1360                mult = 10 * mult;
1361            }
1362            request.setServerPort(port);
1363
1364        }
1365
1366    }
1367
1368
1369    /**
1370     * Check for compression
1371     */

1372    private boolean isCompressable() {
1373
1374        // Nope Compression could works in HTTP 1.0 also
1375
// cf: mod_deflate
1376

1377        // Compression only since HTTP 1.1
1378
// if (! http11)
1379
// return false;
1380

1381        // Check if browser support gzip encoding
1382
MessageBytes acceptEncodingMB =
1383            request.getMimeHeaders().getValue("accept-encoding");
1384
1385        if ((acceptEncodingMB == null)
1386            || (acceptEncodingMB.indexOf("gzip") == -1))
1387            return false;
1388
1389        // Check if content is not allready gzipped
1390
MessageBytes contentEncodingMB =
1391            response.getMimeHeaders().getValue("Content-Encoding");
1392
1393        if ((contentEncodingMB != null)
1394            && (contentEncodingMB.indexOf("gzip") != -1))
1395            return false;
1396
1397        // If force mode, allways compress (test purposes only)
1398
if (compressionLevel == 2)
1399           return true;
1400
1401        // Check for incompatible Browser
1402
if (noCompressionUserAgents != null) {
1403            MessageBytes userAgentValueMB =
1404                request.getMimeHeaders().getValue("user-agent");
1405            if(userAgentValueMB != null) {
1406                String JavaDoc userAgentValue = userAgentValueMB.toString();
1407
1408                // If one Regexp rule match, disable compression
1409
for (int i = 0; i < noCompressionUserAgents.length; i++)
1410                    if (noCompressionUserAgents[i].matcher(userAgentValue).matches())
1411                        return false;
1412            }
1413        }
1414
1415        // Check if suffisant len to trig the compression
1416
long contentLength = response.getContentLengthLong();
1417        if ((contentLength == -1)
1418            || (contentLength > compressionMinSize)) {
1419            // Check for compatible MIME-TYPE
1420
if (compressableMimeTypes != null) {
1421                return (startsWithStringArray(compressableMimeTypes,
1422                                              response.getContentType()));
1423            }
1424        }
1425
1426        return false;
1427    }
1428
1429
1430    /**
1431     * When committing the response, we have to validate the set of headers, as
1432     * well as setup the response filters.
1433     */

1434    protected void prepareResponse() {
1435
1436        boolean entityBody = true;
1437        contentDelimitation = false;
1438
1439        OutputFilter[] outputFilters = outputBuffer.getFilters();
1440
1441        if (http09 == true) {
1442            // HTTP/0.9
1443
outputBuffer.addActiveFilter
1444                (outputFilters[Constants.IDENTITY_FILTER]);
1445            return;
1446        }
1447
1448        int statusCode = response.getStatus();
1449        if ((statusCode == 204) || (statusCode == 205)
1450            || (statusCode == 304)) {
1451            // No entity body
1452
outputBuffer.addActiveFilter
1453                (outputFilters[Constants.VOID_FILTER]);
1454            entityBody = false;
1455            contentDelimitation = true;
1456        }
1457
1458        MessageBytes methodMB = request.method();
1459        if (methodMB.equals("HEAD")) {
1460            // No entity body
1461
outputBuffer.addActiveFilter
1462                (outputFilters[Constants.VOID_FILTER]);
1463            contentDelimitation = true;
1464        }
1465
1466        // Check for compression
1467
boolean useCompression = false;
1468        if (entityBody && (compressionLevel > 0)) {
1469            useCompression = isCompressable();
1470
1471            // Change content-length to -1 to force chunking
1472
if (useCompression) {
1473                response.setContentLength(-1);
1474            }
1475        }
1476
1477        MimeHeaders headers = response.getMimeHeaders();
1478        if (!entityBody) {
1479            response.setContentLength(-1);
1480        } else {
1481            String JavaDoc contentType = response.getContentType();
1482            if (contentType != null) {
1483                headers.setValue("Content-Type").setString(contentType);
1484            }
1485            String JavaDoc contentLanguage = response.getContentLanguage();
1486            if (contentLanguage != null) {
1487                headers.setValue("Content-Language")
1488                    .setString(contentLanguage);
1489            }
1490        }
1491
1492        long contentLength = response.getContentLengthLong();
1493        if (contentLength != -1) {
1494            headers.setValue("Content-Length").setLong(contentLength);
1495            outputBuffer.addActiveFilter
1496                (outputFilters[Constants.IDENTITY_FILTER]);
1497            contentDelimitation = true;
1498        } else {
1499            if (entityBody && http11 && keepAlive) {
1500                outputBuffer.addActiveFilter
1501                    (outputFilters[Constants.CHUNKED_FILTER]);
1502                contentDelimitation = true;
1503                headers.addValue(Constants.TRANSFERENCODING).setString(Constants.CHUNKED);
1504            } else {
1505                outputBuffer.addActiveFilter
1506                    (outputFilters[Constants.IDENTITY_FILTER]);
1507            }
1508        }
1509
1510        if (useCompression) {
1511            outputBuffer.addActiveFilter(outputFilters[Constants.GZIP_FILTER]);
1512            headers.setValue("Content-Encoding").setString("gzip");
1513            // Make Proxies happy via Vary (from mod_deflate)
1514
headers.setValue("Vary").setString("Accept-Encoding");
1515        }
1516
1517        // Add date header
1518
String JavaDoc date = null;
1519        if (System.getSecurityManager() != null){
1520            date = (String JavaDoc)AccessController.doPrivileged(
1521                    new PrivilegedAction JavaDoc() {
1522                        public Object JavaDoc run(){
1523                            return FastHttpDateFormat.getCurrentDate();
1524                        }
1525                    }
1526            );
1527        } else {
1528            date = FastHttpDateFormat.getCurrentDate();
1529        }
1530        headers.setValue("Date").setString(date);
1531
1532        // FIXME: Add transfer encoding header
1533

1534        if ((entityBody) && (!contentDelimitation)) {
1535            // Mark as close the connection after the request, and add the
1536
// connection: close header
1537
keepAlive = false;
1538        }
1539
1540        // If we know that the request is bad this early, add the
1541
// Connection: close header.
1542
keepAlive = keepAlive && !statusDropsConnection(statusCode);
1543        if (!keepAlive) {
1544            headers.addValue(Constants.CONNECTION).setString(Constants.CLOSE);
1545        } else if (!http11 && !error) {
1546            headers.addValue(Constants.CONNECTION).setString(Constants.KEEPALIVE);
1547        }
1548
1549        // Build the response header
1550
outputBuffer.sendStatus();
1551
1552        // Add server header
1553
if (server != null) {
1554            headers.setValue("Server").setString(server);
1555        } else {
1556            outputBuffer.write(Constants.SERVER_BYTES);
1557        }
1558
1559        int size = headers.size();
1560        for (int i = 0; i < size; i++) {
1561            outputBuffer.sendHeader(headers.getName(i), headers.getValue(i));
1562        }
1563        outputBuffer.endHeaders();
1564
1565    }
1566
1567
1568    /**
1569     * Initialize standard input and output filters.
1570     */

1571    protected void initializeFilters() {
1572
1573        // Create and add the identity filters.
1574
inputBuffer.addFilter(new IdentityInputFilter());
1575        outputBuffer.addFilter(new IdentityOutputFilter());
1576
1577        // Create and add the chunked filters.
1578
inputBuffer.addFilter(new ChunkedInputFilter());
1579        outputBuffer.addFilter(new ChunkedOutputFilter());
1580
1581        // Create and add the void filters.
1582
inputBuffer.addFilter(new VoidInputFilter());
1583        outputBuffer.addFilter(new VoidOutputFilter());
1584
1585        // Create and add buffered input filter
1586
inputBuffer.addFilter(new BufferedInputFilter());
1587
1588        // Create and add the chunked filters.
1589
//inputBuffer.addFilter(new GzipInputFilter());
1590
outputBuffer.addFilter(new GzipOutputFilter());
1591
1592    }
1593
1594
1595    /**
1596     * Add an input filter to the current request.
1597     *
1598     * @return false if the encoding was not found (which would mean it is
1599     * unsupported)
1600     */

1601    protected boolean addInputFilter(InputFilter[] inputFilters,
1602                                     String JavaDoc encodingName) {
1603        if (encodingName.equals("identity")) {
1604            // Skip
1605
} else if (encodingName.equals("chunked")) {
1606            inputBuffer.addActiveFilter
1607                (inputFilters[Constants.CHUNKED_FILTER]);
1608            contentDelimitation = true;
1609        } else {
1610            for (int i = 2; i < inputFilters.length; i++) {
1611                if (inputFilters[i].getEncodingName()
1612                    .toString().equals(encodingName)) {
1613                    inputBuffer.addActiveFilter(inputFilters[i]);
1614                    return true;
1615                }
1616            }
1617            return false;
1618        }
1619        return true;
1620    }
1621
1622
1623    /**
1624     * Specialized utility method: find a sequence of lower case bytes inside
1625     * a ByteChunk.
1626     */

1627    protected int findBytes(ByteChunk bc, byte[] b) {
1628
1629        byte first = b[0];
1630        byte[] buff = bc.getBuffer();
1631        int start = bc.getStart();
1632        int end = bc.getEnd();
1633
1634    // Look for first char
1635
int srcEnd = b.length;
1636
1637    for (int i = start; i <= (end - srcEnd); i++) {
1638        if (Ascii.toLower(buff[i]) != first) continue;
1639        // found first char, now look for a match
1640
int myPos = i+1;
1641        for (int srcPos = 1; srcPos < srcEnd; ) {
1642                if (Ascii.toLower(buff[myPos++]) != b[srcPos++])
1643            break;
1644                if (srcPos == srcEnd) return i - start; // found it
1645
}
1646    }
1647    return -1;
1648
1649    }
1650
1651    /**
1652     * Determine if we must drop the connection because of the HTTP status
1653     * code. Use the same list of codes as Apache/httpd.
1654     */

1655    protected boolean statusDropsConnection(int status) {
1656        return status == 400 /* SC_BAD_REQUEST */ ||
1657               status == 408 /* SC_REQUEST_TIMEOUT */ ||
1658               status == 411 /* SC_LENGTH_REQUIRED */ ||
1659               status == 413 /* SC_REQUEST_ENTITY_TOO_LARGE */ ||
1660               status == 414 /* SC_REQUEST_URI_TOO_LARGE */ ||
1661               status == 500 /* SC_INTERNAL_SERVER_ERROR */ ||
1662               status == 503 /* SC_SERVICE_UNAVAILABLE */ ||
1663               status == 501 /* SC_NOT_IMPLEMENTED */;
1664    }
1665
1666}
1667
Popular Tags