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;
21 import java.io.InterruptedIOException;
22 import java.net.InetAddress;
23 import java.net.Socket;
24 import java.security.AccessController;
25 import java.security.PrivilegedAction;
26 import java.util.StringTokenizer;
27 import java.util.regex.Pattern;
28 import java.util.regex.PatternSyntaxException;
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.juli.logging.Log log
68 = org.apache.juli.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[] restrictedUserAgents = null;
182
183
184 /**
185 * Maximum number of Keep-Alive requests to honor.
186 */
187 protected int maxKeepAliveRequests = -1;
188
189 /**
190 * The number of seconds Tomcat will wait for a subsequent request
191 * before closing the connection.
192 */
193 protected int keepAliveTimeout = -1;
194
195
196 /**
197 * SSL information.
198 */
199 protected SSLSupport sslSupport;
200
201
202 /**
203 * Socket associated with the current connection.
204 */
205 protected Socket socket;
206
207
208 /**
209 * Remote Address associated with the current connection.
210 */
211 protected String remoteAddr = null;
212
213
214 /**
215 * Remote Host associated with the current connection.
216 */
217 protected String remoteHost = null;
218
219
220 /**
221 * Local Host associated with the current connection.
222 */
223 protected String localName = null;
224
225
226
227 /**
228 * Local port to which the socket is connected
229 */
230 protected int localPort = -1;
231
232
233 /**
234 * Remote port to which the socket is connected
235 */
236 protected int remotePort = -1;
237
238
239 /**
240 * The local Host address.
241 */
242 protected String localAddr = null;
243
244
245 /**
246 * Maximum timeout on uploads. 5 minutes as in Apache HTTPD server.
247 */
248 protected int timeout = 300000;
249
250
251 /**
252 * Flag to disable setting a different time-out on uploads.
253 */
254 protected boolean disableUploadTimeout = false;
255
256
257 /**
258 * Allowed compression level.
259 */
260 protected int compressionLevel = 0;
261
262
263 /**
264 * Minimum contentsize to make compression.
265 */
266 protected int compressionMinSize = 2048;
267
268
269 /**
270 * Socket buffering.
271 */
272 protected int socketBuffer = -1;
273
274
275 /**
276 * Max saved post size.
277 */
278 protected int maxSavePostSize = 4 * 1024;
279
280
281 /**
282 * List of user agents to not use gzip with
283 */
284 protected Pattern noCompressionUserAgents[] = null;
285
286 /**
287 * List of MIMES which could be gzipped
288 */
289 protected String[] compressableMimeTypes =
290 { "text/html", "text/xml", "text/plain" };
291
292
293 /**
294 * Host name (used to avoid useless B2C conversion on the host name).
295 */
296 protected char[] hostNameC = new char[0];
297
298
299 /**
300 * Associated endpoint.
301 */
302 protected JIoEndpoint endpoint;
303
304
305 /**
306 * Allow a customized the server header for the tin-foil hat folks.
307 */
308 protected String server = null;
309
310
311 // ------------------------------------------------------------- Properties
312
313
314 /**
315 * Return compression level.
316 */
317 public String getCompression() {
318 switch (compressionLevel) {
319 case 0:
320 return "off";
321 case 1:
322 return "on";
323 case 2:
324 return "force";
325 }
326 return "off";
327 }
328
329
330 /**
331 * Set compression level.
332 */
333 public void setCompression(String compression) {
334 if (compression.equals("on")) {
335 this.compressionLevel = 1;
336 } else if (compression.equals("force")) {
337 this.compressionLevel = 2;
338 } else if (compression.equals("off")) {
339 this.compressionLevel = 0;
340 } else {
341 try {
342 // Try to parse compression as an int, which would give the
343 // minimum compression size
344 compressionMinSize = Integer.parseInt(compression);
345 this.compressionLevel = 1;
346 } catch (Exception e) {
347 this.compressionLevel = 0;
348 }
349 }
350 }
351
352 /**
353 * Set Minimum size to trigger compression.
354 */
355 public void setCompressionMinSize(int compressionMinSize) {
356 this.compressionMinSize = compressionMinSize;
357 }
358
359
360 /**
361 * Add user-agent for which gzip compression didn't works
362 * The user agent String given will be exactly matched
363 * to the user-agent header submitted by the client.
364 *
365 * @param userAgent user-agent string
366 */
367 public void addNoCompressionUserAgent(String userAgent) {
368 try {
369 Pattern nRule = Pattern.compile(userAgent);
370 noCompressionUserAgents =
371 addREArray(noCompressionUserAgents, nRule);
372 } catch (PatternSyntaxException pse) {
373 log.error(sm.getString("http11processor.regexp.error", userAgent), pse);
374 }
375 }
376
377
378 /**
379 * Set no compression user agent list (this method is best when used with
380 * a large number of connectors, where it would be better to have all of
381 * them referenced a single array).
382 */
383 public void setNoCompressionUserAgents(Pattern[] noCompressionUserAgents) {
384 this.noCompressionUserAgents = noCompressionUserAgents;
385 }
386
387
388 /**
389 * Set no compression user agent list.
390 * List contains users agents separated by ',' :
391 *
392 * ie: "gorilla,desesplorer,tigrus"
393 */
394 public void setNoCompressionUserAgents(String noCompressionUserAgents) {
395 if (noCompressionUserAgents != null) {
396 StringTokenizer st = new StringTokenizer(noCompressionUserAgents, ",");
397
398 while (st.hasMoreTokens()) {
399 addNoCompressionUserAgent(st.nextToken().trim());
400 }
401 }
402 }
403
404 /**
405 * Add a mime-type which will be compressable
406 * The mime-type String will be exactly matched
407 * in the response mime-type header .
408 *
409 * @param mimeType mime-type string
410 */
411 public void addCompressableMimeType(String mimeType) {
412 compressableMimeTypes =
413 addStringArray(compressableMimeTypes, mimeType);
414 }
415
416
417 /**
418 * Set compressable mime-type list (this method is best when used with
419 * a large number of connectors, where it would be better to have all of
420 * them referenced a single array).
421 */
422 public void setCompressableMimeTypes(String[] compressableMimeTypes) {
423 this.compressableMimeTypes = compressableMimeTypes;
424 }
425
426
427 /**
428 * Set compressable mime-type list
429 * List contains users agents separated by ',' :
430 *
431 * ie: "text/html,text/xml,text/plain"
432 */
433 public void setCompressableMimeTypes(String compressableMimeTypes) {
434 if (compressableMimeTypes != null) {
435 StringTokenizer st = new StringTokenizer(compressableMimeTypes, ",");
436
437 while (st.hasMoreTokens()) {
438 addCompressableMimeType(st.nextToken().trim());
439 }
440 }
441 }
442
443
444 /**
445 * Return the list of restricted user agents.
446 */
447 public String[] findCompressableMimeTypes() {
448 return (compressableMimeTypes);
449 }
450
451
452
453 // --------------------------------------------------------- Public Methods
454
455
456 /**
457 * Add input or output filter.
458 *
459 * @param className class name of the filter
460 */
461 protected void addFilter(String className) {
462 try {
463 Class clazz = Class.forName(className);
464 Object obj = clazz.newInstance();
465 if (obj instanceof InputFilter) {
466 inputBuffer.addFilter((InputFilter) obj);
467 } else if (obj instanceof OutputFilter) {
468 outputBuffer.addFilter((OutputFilter) obj);
469 } else {
470 log.warn(sm.getString("http11processor.filter.unknown", className));
471 }
472 } catch (Exception e) {
473 log.error(sm.getString("http11processor.filter.error", className), e);
474 }
475 }
476
477
478 /**
479 * General use method
480 *
481 * @param sArray the StringArray
482 * @param value string
483 */
484 private String[] addStringArray(String sArray[], String value) {
485 String[] result = null;
486 if (sArray == null) {
487 result = new String[1];
488 result[0] = value;
489 }
490 else {
491 result = new String[sArray.length + 1];
492 for (int i = 0; i < sArray.length; i++)
493 result[i] = sArray[i];
494 result[sArray.length] = value;
495 }
496 return result;
497 }
498
499
500 /**
501 * General use method
502 *
503 * @param rArray the REArray
504 * @param value Obj
505 */
506 private Pattern[] addREArray(Pattern rArray[], Pattern value) {
507 Pattern[] result = null;
508 if (rArray == null) {
509 result = new Pattern[1];
510 result[0] = value;
511 }
512 else {
513 result = new Pattern[rArray.length + 1];
514 for (int i = 0; i < rArray.length; i++)
515 result[i] = rArray[i];
516 result[rArray.length] = value;
517 }
518 return result;
519 }
520
521
522 /**
523 * General use method
524 *
525 * @param sArray the StringArray
526 * @param value string
527 */
528 private boolean inStringArray(String sArray[], String value) {
529 for (int i = 0; i < sArray.length; i++) {
530 if (sArray[i].equals(value)) {
531 return true;
532 }
533 }
534 return false;
535 }
536
537
538 /**
539 * Checks if any entry in the string array starts with the specified value
540 *
541 * @param sArray the StringArray
542 * @param value string
543 */
544 private boolean startsWithStringArray(String sArray[], String value) {
545 if (value == null)
546 return false;
547 for (int i = 0; i < sArray.length; i++) {
548 if (value.startsWith(sArray[i])) {
549 return true;
550 }
551 }
552 return false;
553 }
554
555
556 /**
557 * Add restricted user-agent (which will downgrade the connector
558 * to HTTP/1.0 mode). The user agent String given will be matched
559 * via regexp to the user-agent header submitted by the client.
560 *
561 * @param userAgent user-agent string
562 */
563 public void addRestrictedUserAgent(String userAgent) {
564 try {
565 Pattern nRule = Pattern.compile(userAgent);
566 restrictedUserAgents = addREArray(restrictedUserAgents, nRule);
567 } catch (PatternSyntaxException pse) {
568 log.error(sm.getString("http11processor.regexp.error", userAgent), pse);
569 }
570 }
571
572
573 /**
574 * Set restricted user agent list (this method is best when used with
575 * a large number of connectors, where it would be better to have all of
576 * them referenced a single array).
577 */
578 public void setRestrictedUserAgents(Pattern[] restrictedUserAgents) {
579 this.restrictedUserAgents = restrictedUserAgents;
580 }
581
582
583 /**
584 * Set restricted user agent list (which will downgrade the connector
585 * to HTTP/1.0 mode). List contains users agents separated by ',' :
586 *
587 * ie: "gorilla,desesplorer,tigrus"
588 */
589 public void setRestrictedUserAgents(String restrictedUserAgents) {
590 if (restrictedUserAgents != null) {
591 StringTokenizer st =
592 new StringTokenizer(restrictedUserAgents, ",");
593 while (st.hasMoreTokens()) {
594 addRestrictedUserAgent(st.nextToken().trim());
595 }
596 }
597 }
598
599
600 /**
601 * Return the list of restricted user agents.
602 */
603 public String[] findRestrictedUserAgents() {
604 String[] sarr = new String [restrictedUserAgents.length];
605
606 for (int i = 0; i < restrictedUserAgents.length; i++)
607 sarr[i] = restrictedUserAgents[i].toString();
608
609 return (sarr);
610 }
611
612
613 /**
614 * Set the maximum number of Keep-Alive requests to honor.
615 * This is to safeguard from DoS attacks. Setting to a negative
616 * value disables the check.
617 */
618 public void setMaxKeepAliveRequests(int mkar) {
619 maxKeepAliveRequests = mkar;
620 }
621
622
623 /**
624 * Return the number of Keep-Alive requests that we will honor.
625 */
626 public int getMaxKeepAliveRequests() {
627 return maxKeepAliveRequests;
628 }
629
630 /**
631 * Set the Keep-Alive timeout.
632 */
633 public void setKeepAliveTimeout(int timeout) {
634 keepAliveTimeout = timeout;
635 }
636
637
638 /**
639 * Return the number Keep-Alive timeout.
640 */
641 public int getKeepAliveTimeout() {
642 return keepAliveTimeout;
643 }
644
645
646 /**
647 * Set the maximum size of a POST which will be buffered in SSL mode.
648 */
649 public void setMaxSavePostSize(int msps) {
650 maxSavePostSize = msps;
651 }
652
653
654 /**
655 * Return the maximum size of a POST which will be buffered in SSL mode.
656 */
657 public int getMaxSavePostSize() {
658 return maxSavePostSize;
659 }
660
661
662 /**
663 * Set the SSL information for this HTTP connection.
664 */
665 public void setSSLSupport(SSLSupport sslSupport) {
666 this.sslSupport = sslSupport;
667 }
668
669
670 /**
671 * Set the flag to control upload time-outs.
672 */
673 public void setDisableUploadTimeout(boolean isDisabled) {
674 disableUploadTimeout = isDisabled;
675 }
676
677 /**
678 * Get the flag that controls upload time-outs.
679 */
680 public boolean getDisableUploadTimeout() {
681 return disableUploadTimeout;
682 }
683
684 /**
685 * Set the socket buffer flag.
686 */
687 public void setSocketBuffer(int socketBuffer) {
688 this.socketBuffer = socketBuffer;
689 outputBuffer.setSocketBuffer(socketBuffer);
690 }
691
692 /**
693 * Get the socket buffer flag.
694 */
695 public int getSocketBuffer() {
696 return socketBuffer;
697 }
698
699 /**
700 * Set the upload timeout.
701 */
702 public void setTimeout( int timeouts ) {
703 timeout = timeouts ;
704 }
705
706 /**
707 * Get the upload timeout.
708 */
709 public int getTimeout() {
710 return timeout;
711 }
712
713
714 /**
715 * Set the server header name.
716 */
717 public void setServer( String server ) {
718 if (server==null || server.equals("")) {
719 this.server = null;
720 } else {
721 this.server = server;
722 }
723 }
724
725 /**
726 * Get the server header name.
727 */
728 public String getServer() {
729 return server;
730 }
731
732
733 /** Get the request associated with this processor.
734 *
735 * @return The request
736 */
737 public Request getRequest() {
738 return request;
739 }
740
741 /**
742 * Process pipelined HTTP requests using the specified input and output
743 * streams.
744 *
745 * @param input stream from which the HTTP requests will be read
746 * @param output stream which will be used to output the HTTP
747 * responses
748 * @throws IOException error during an I/O operation
749 */
750 public void process(Socket socket)
751 throws IOException {
752 RequestInfo rp = request.getRequestProcessor();
753 rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
754
755 // Set the remote address
756 remoteAddr = null;
757 remoteHost = null;
758 localAddr = null;
759 localName = null;
760 remotePort = -1;
761 localPort = -1;
762
763 // Setting up the I/O
764 this.socket = socket;
765 inputBuffer.setInputStream(socket.getInputStream());
766 outputBuffer.setOutputStream(socket.getOutputStream());
767
768 // Error flag
769 error = false;
770 keepAlive = true;
771
772 int keepAliveLeft = maxKeepAliveRequests;
773 int soTimeout = socket.getSoTimeout();
774 int oldSoTimeout = soTimeout;
775
776 int threadRatio = (endpoint.getCurrentThreadsBusy() * 100)
777 / endpoint.getMaxThreads();
778 if (threadRatio > 75) {
779 keepAliveLeft = 1;
780 }
781
782 if (soTimeout != oldSoTimeout) {
783 try {
784 socket.setSoTimeout(soTimeout);
785 } catch (Throwable t) {
786 log.debug(sm.getString("http11processor.socket.timeout"), t);
787 error = true;
788 }
789 }
790
791 boolean keptAlive = false;
792
793 while (started && !error && keepAlive) {
794
795 // Parsing the request header
796 try {
797 if (!disableUploadTimeout && keptAlive) {
798 if (keepAliveTimeout > 0) {
799 socket.setSoTimeout(keepAliveTimeout);
800 }
801 else if (soTimeout > 0) {
802 socket.setSoTimeout(soTimeout);
803 }
804 }
805 inputBuffer.parseRequestLine();
806 request.setStartTime(System.currentTimeMillis());
807 keptAlive = true;
808 if (!disableUploadTimeout) {
809 socket.setSoTimeout(timeout);
810 }
811 inputBuffer.parseHeaders();
812 } catch (IOException e) {
813 error = true;
814 break;
815 } catch (Throwable t) {
816 if (log.isDebugEnabled()) {
817 log.debug(sm.getString("http11processor.header.parse"), t);
818 }
819 // 400 - Bad Request
820 response.setStatus(400);
821 error = true;
822 }
823
824 // Setting up filters, and parse some request headers
825 rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
826 try {
827 prepareRequest();
828 } catch (Throwable t) {
829 if (log.isDebugEnabled()) {
830 log.debug(sm.getString("http11processor.request.prepare"), t);
831 }
832 // 400 - Internal Server Error
833 response.setStatus(400);
834 error = true;
835 }
836
837 if (maxKeepAliveRequests > 0 && --keepAliveLeft == 0)
838 keepAlive = false;
839
840 // Process the request in the adapter
841 if (!error) {
842 try {
843 rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
844 adapter.service(request, response);
845 // Handle when the response was committed before a serious
846 // error occurred. Throwing a ServletException should both
847 // set the status to 500 and set the errorException.
848 // If we fail here, then the response is likely already
849 // committed, so we can't try and set headers.
850 if(keepAlive && !error) { // Avoid checking twice.
851 error = response.getErrorException() != null ||
852 statusDropsConnection(response.getStatus());
853 }
854
855 } catch (InterruptedIOException e) {
856 error = true;
857 } catch (Throwable t) {
858 log.error(sm.getString("http11processor.request.process"), t);
859 // 500 - Internal Server Error
860 response.setStatus(500);
861 error = true;
862 }
863 }
864
865 // Finish the handling of the request
866 try {
867 rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);
868 inputBuffer.endRequest();
869 } catch (IOException e) {
870 error = true;
871 } catch (Throwable t) {
872 log.error(sm.getString("http11processor.request.finish"), t);
873 // 500 - Internal Server Error
874 response.setStatus(500);
875 error = true;
876 }
877 try {
878 rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);
879 outputBuffer.endRequest();
880 } catch (IOException e) {
881 error = true;
882 } catch (Throwable t) {
883 log.error(sm.getString("http11processor.response.finish"), t);
884 error = true;
885 }
886
887 // If there was an error, make sure the request is counted as
888 // and error, and update the statistics counter
889 if (error) {
890 response.setStatus(500);
891 }
892 request.updateCounters();
893
894 rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
895
896 // Don't reset the param - we'll see it as ended. Next request
897 // will reset it
898 // thrA.setParam(null);
899 // Next request
900 inputBuffer.nextRequest();
901 outputBuffer.nextRequest();
902
903 }
904
905 rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
906
907 // Recycle
908 inputBuffer.recycle();
909 outputBuffer.recycle();
910
911 // Recycle ssl info
912 sslSupport = null;
913 }
914
915
916 // ----------------------------------------------------- ActionHook Methods
917
918
919 /**
920 * Send an action to the connector.
921 *
922 * @param actionCode Type of the action
923 * @param param Action parameter
924 */
925 public void action(ActionCode actionCode, Object param) {
926
927 if (actionCode == ActionCode.ACTION_COMMIT) {
928 // Commit current response
929
930 if (response.isCommitted())
931 return;
932
933 // Validate and write response headers
934 prepareResponse();
935 try {
936 outputBuffer.commit();
937 } catch (IOException e) {
938 // Set error flag
939 error = true;
940 }
941
942 } else if (actionCode == ActionCode.ACTION_ACK) {
943
944 // Acknowlege request
945
946 // Send a 100 status back if it makes sense (response not committed
947 // yet, and client specified an expectation for 100-continue)
948
949 if ((response.isCommitted()) || !expectation)
950 return;
951
952 inputBuffer.setSwallowInput(true);
953 try {
954 outputBuffer.sendAck();
955 } catch (IOException e) {
956 // Set error flag
957 error = true;
958 }
959
960 } else if (actionCode == ActionCode.ACTION_CLIENT_FLUSH) {
961
962 try {
963 outputBuffer.flush();
964 } catch (IOException e) {
965 // Set error flag
966 error = true;
967 response.setErrorException(e);
968 }
969
970 } else if (actionCode == ActionCode.ACTION_CLOSE) {
971 // Close
972
973 // End the processing of the current request, and stop any further
974 // transactions with the client
975
976 try {
977 outputBuffer.endRequest();
978 } catch (IOException e) {
979 // Set error flag
980 error = true;
981 }
982
983 } else if (actionCode == ActionCode.ACTION_RESET) {
984
985 // Reset response
986
987 // Note: This must be called before the response is committed
988
989 outputBuffer.reset();
990
991 } else if (actionCode == ActionCode.ACTION_CUSTOM) {
992
993 // Do nothing
994
995 } else if (actionCode == ActionCode.ACTION_START) {
996
997 started = true;
998
999 } else if (actionCode == ActionCode.ACTION_STOP) {
1000
1001 started = false;
1002
1003 } else if (actionCode == ActionCode.ACTION_REQ_SSL_ATTRIBUTE ) {
1004
1005 try {
1006 if (sslSupport != null) {
1007 Object sslO = sslSupport.getCipherSuite();
1008 if (sslO != null)
1009 request.setAttribute
1010 (SSLSupport.CIPHER_SUITE_KEY, sslO);
1011 sslO = sslSupport.getPeerCertificateChain(false);
1012 if (sslO != null)
1013 request.setAttribute
1014 (SSLSupport.CERTIFICATE_KEY, sslO);
1015 sslO = sslSupport.getKeySize();
1016 if (sslO != null)
1017 request.setAttribute
1018 (SSLSupport.KEY_SIZE_KEY, sslO);
1019 sslO = sslSupport.getSessionId();
1020 if (sslO != null)
1021 request.setAttribute
1022 (SSLSupport.SESSION_ID_KEY, sslO);
1023 }
1024 } catch (Exception e) {
1025 log.warn(sm.getString("http11processor.socket.ssl"), e);
1026 }
1027
1028 } else if (actionCode == ActionCode.ACTION_REQ_HOST_ADDR_ATTRIBUTE) {
1029
1030 if ((remoteAddr == null) && (socket != null)) {
1031 InetAddress inetAddr = socket.getInetAddress();
1032 if (inetAddr != null) {
1033 remoteAddr = inetAddr.getHostAddress();
1034 }
1035 }
1036 request.remoteAddr().setString(remoteAddr);
1037
1038 } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_NAME_ATTRIBUTE) {
1039
1040 if ((localName == null) && (socket != null)) {
1041 InetAddress inetAddr = socket.getLocalAddress();
1042 if (inetAddr != null) {
1043 localName = inetAddr.getHostName();
1044 }
1045 }
1046 request.localName().setString(localName);
1047
1048 } else if (actionCode == ActionCode.ACTION_REQ_HOST_ATTRIBUTE) {
1049
1050 if ((remoteHost == null) && (socket != null)) {
1051 InetAddress inetAddr = socket.getInetAddress();
1052 if (inetAddr != null) {
1053 remoteHost = inetAddr.getHostName();
1054 }
1055 if(remoteHost == null) {
1056 if(remoteAddr != null) {
1057 remoteHost = remoteAddr;
1058 } else { // all we can do is punt
1059 request.remoteHost().recycle();
1060 }
1061 }
1062 }
1063 request.remoteHost().setString(remoteHost);
1064
1065 } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_ADDR_ATTRIBUTE) {
1066
1067 if (localAddr == null)
1068 localAddr = socket.getLocalAddress().getHostAddress();
1069
1070 request.localAddr().setString(localAddr);
1071
1072 } else if (actionCode == ActionCode.ACTION_REQ_REMOTEPORT_ATTRIBUTE) {
1073
1074 if ((remotePort == -1 ) && (socket !=null)) {
1075 remotePort = socket.getPort();
1076 }
1077 request.setRemotePort(remotePort);
1078
1079 } else if (actionCode == ActionCode.ACTION_REQ_LOCALPORT_ATTRIBUTE) {
1080
1081 if ((localPort == -1 ) && (socket !=null)) {
1082 localPort = socket.getLocalPort();
1083 }
1084 request.setLocalPort(localPort);
1085
1086 } else if (actionCode == ActionCode.ACTION_REQ_SSL_CERTIFICATE) {
1087 if( sslSupport != null) {
1088 /*
1089 * Consume and buffer the request body, so that it does not
1090 * interfere with the client's handshake messages
1091 */
1092 InputFilter[] inputFilters = inputBuffer.getFilters();
1093 ((BufferedInputFilter) inputFilters[Constants.BUFFERED_FILTER])
1094 .setLimit(maxSavePostSize);
1095 inputBuffer.addActiveFilter
1096 (inputFilters[Constants.BUFFERED_FILTER]);
1097 try {
1098 Object sslO = sslSupport.getPeerCertificateChain(true);
1099 if( sslO != null) {
1100 request.setAttribute
1101 (SSLSupport.CERTIFICATE_KEY, sslO);
1102 }
1103 } catch (Exception e) {
1104 log.warn(sm.getString("http11processor.socket.ssl"), e);
1105 }
1106 }
1107 } else if (actionCode == ActionCode.ACTION_REQ_SET_BODY_REPLAY) {
1108 ByteChunk body = (ByteChunk) param;
1109
1110 InputFilter savedBody = new SavedRequestInputFilter(body);
1111 savedBody.setRequest(request);
1112
1113 InternalInputBuffer internalBuffer = (InternalInputBuffer)
1114 request.getInputBuffer();
1115 internalBuffer.addActiveFilter(savedBody);
1116 }
1117
1118 }
1119
1120
1121 // ------------------------------------------------------ Connector Methods
1122
1123
1124 /**
1125 * Set the associated adapter.
1126 *
1127 * @param adapter the new adapter
1128 */
1129 public void setAdapter(Adapter adapter) {
1130 this.adapter = adapter;
1131 }
1132
1133
1134 /**
1135 * Get the associated adapter.
1136 *
1137 * @return the associated adapter
1138 */
1139 public Adapter getAdapter() {
1140 return adapter;
1141 }
1142
1143
1144 // ------------------------------------------------------ Protected Methods
1145
1146
1147 /**
1148 * After reading the request headers, we have to setup the request filters.
1149 */
1150 protected void prepareRequest() {
1151
1152 http11 = true;
1153 http09 = false;
1154 contentDelimitation = false;
1155 expectation = false;
1156 if (sslSupport != null) {
1157 request.scheme().setString("https");
1158 }
1159 MessageBytes protocolMB = request.protocol();
1160 if (protocolMB.equals(Constants.HTTP_11)) {
1161 http11 = true;
1162 protocolMB.setString(Constants.HTTP_11);
1163 } else if (protocolMB.equals(Constants.HTTP_10)) {
1164 http11 = false;
1165 keepAlive = false;
1166 protocolMB.setString(Constants.HTTP_10);
1167 } else if (protocolMB.equals("")) {
1168 // HTTP/0.9
1169 http09 = true;
1170 http11 = false;
1171 keepAlive = false;
1172 } else {
1173 // Unsupported protocol
1174 http11 = false;
1175 error = true;
1176 // Send 505; Unsupported HTTP version
1177 if (log.isDebugEnabled()) {
1178 log.debug(sm.getString("http11processor.request.prepare")+
1179 " Unsupported HTTP version \""+protocolMB+"\"");
1180 }
1181 response.setStatus(505);
1182 }
1183
1184 MessageBytes methodMB = request.method();
1185 if (methodMB.equals(Constants.GET)) {
1186 methodMB.setString(Constants.GET);
1187 } else if (methodMB.equals(Constants.POST)) {
1188 methodMB.setString(Constants.POST);
1189 }
1190
1191 MimeHeaders headers = request.getMimeHeaders();
1192
1193 // Check connection header
1194 MessageBytes connectionValueMB = headers.getValue("connection");
1195 if (connectionValueMB != null) {
1196 ByteChunk connectionValueBC = connectionValueMB.getByteChunk();
1197 if (findBytes(connectionValueBC, Constants.CLOSE_BYTES) != -1) {
1198 keepAlive = false;
1199 } else if (findBytes(connectionValueBC,
1200 Constants.KEEPALIVE_BYTES) != -1) {
1201 keepAlive = true;
1202 }
1203 }
1204
1205 MessageBytes expectMB = null;
1206 if (http11)
1207 expectMB = headers.getValue("expect");
1208 if ((expectMB != null)
1209 && (expectMB.indexOfIgnoreCase("100-continue", 0) != -1)) {
1210 inputBuffer.setSwallowInput(false);
1211 expectation = true;
1212 }
1213
1214 // Check user-agent header
1215 if ((restrictedUserAgents != null) && ((http11) || (keepAlive))) {
1216 MessageBytes userAgentValueMB = headers.getValue("user-agent");
1217 // Check in the restricted list, and adjust the http11
1218 // and keepAlive flags accordingly
1219 if(userAgentValueMB != null) {
1220 String userAgentValue = userAgentValueMB.toString();
1221 for (int i = 0; i < restrictedUserAgents.length; i++) {
1222 if (restrictedUserAgents[i].matcher(userAgentValue).matches()) {
1223 http11 = false;
1224 keepAlive = false;
1225 break;
1226 }
1227 }
1228 }
1229 }
1230
1231 // Check for a full URI (including protocol://host:port/)
1232 ByteChunk uriBC = request.requestURI().getByteChunk();
1233 if (uriBC.startsWithIgnoreCase("http", 0)) {
1234
1235 int pos = uriBC.indexOf("://", 0, 3, 4);
1236 int uriBCStart = uriBC.getStart();
1237 int slashPos = -1;
1238 if (pos != -1) {
1239 byte[] uriB = uriBC.getBytes();
1240 slashPos = uriBC.indexOf('/', pos + 3);
1241 if (slashPos == -1) {
1242 slashPos = uriBC.getLength();
1243 // Set URI as "/"
1244 request.requestURI().setBytes
1245 (uriB, uriBCStart + pos + 1, 1);
1246 } else {
1247 request.requestURI().setBytes
1248 (uriB, uriBCStart + slashPos,
1249 uriBC.getLength() - slashPos);
1250 }
1251 MessageBytes hostMB = headers.setValue("host");
1252 hostMB.setBytes(uriB, uriBCStart + pos + 3,
1253 slashPos - pos - 3);
1254 }
1255
1256 }
1257
1258 // Input filter setup
1259 InputFilter[] inputFilters = inputBuffer.getFilters();
1260
1261 // Parse transfer-encoding header
1262 MessageBytes transferEncodingValueMB = null;
1263 if (http11)
1264 transferEncodingValueMB = headers.getValue("transfer-encoding");
1265 if (transferEncodingValueMB != null) {
1266 String transferEncodingValue = transferEncodingValueMB.toString();
1267 // Parse the comma separated list. "identity" codings are ignored
1268 int startPos = 0;
1269 int commaPos = transferEncodingValue.indexOf(',');
1270 String encodingName = null;
1271 while (commaPos != -1) {
1272 encodingName = transferEncodingValue.substring
1273 (startPos, commaPos).toLowerCase().trim();
1274 if (!addInputFilter(inputFilters, encodingName)) {
1275 // Unsupported transfer encoding
1276 error = true;
1277 // 501 - Unimplemented
1278 response.setStatus(501);
1279 }
1280 startPos = commaPos + 1;
1281 commaPos = transferEncodingValue.indexOf(',', startPos);
1282 }
1283 encodingName = transferEncodingValue.substring(startPos)
1284 .toLowerCase().trim();
1285 if (!addInputFilter(inputFilters, encodingName)) {
1286 // Unsupported transfer encoding
1287 error = true;
1288 // 501 - Unimplemented
1289 if (log.isDebugEnabled()) {
1290 log.debug(sm.getString("http11processor.request.prepare")+
1291 " Unsupported transfer encoding \""+encodingName+"\"");
1292 }
1293 response.setStatus(501);
1294 }
1295 }
1296
1297 // Parse content-length header
1298 long contentLength = request.getContentLengthLong();
1299 if (contentLength >= 0 && !contentDelimitation) {
1300 inputBuffer.addActiveFilter
1301 (inputFilters[Constants.IDENTITY_FILTER]);
1302 contentDelimitation = true;
1303 }
1304
1305 MessageBytes valueMB = headers.getValue("host");
1306
1307 // Check host header
1308 if (http11 && (valueMB == null)) {
1309 error = true;
1310 // 400 - Bad request
1311 if (log.isDebugEnabled()) {
1312 log.debug(sm.getString("http11processor.request.prepare")+
1313 " host header missing");
1314 }
1315 response.setStatus(400);
1316 }
1317
1318 parseHost(valueMB);
1319
1320 if (!contentDelimitation) {
1321 // If there's no content length
1322 // (broken HTTP/1.0 or HTTP/1.1), assume
1323 // the client is not broken and didn't send a body
1324 inputBuffer.addActiveFilter
1325 (inputFilters[Constants.VOID_FILTER]);
1326 contentDelimitation = true;
1327 }
1328
1329 }
1330
1331
1332 /**
1333 * Parse host.
1334 */
1335 public void parseHost(MessageBytes valueMB) {
1336
1337 if (valueMB == null || valueMB.isNull()) {
1338 // HTTP/1.0
1339 // Default is what the socket tells us. Overriden if a host is
1340 // found/parsed
1341 request.setServerPort(socket.getLocalPort());
1342 InetAddress localAddress = socket.getLocalAddress();
1343 // Setting the socket-related fields. The adapter doesn't know
1344 // about socket.
1345 request.serverName().setString(localAddress.getHostName());
1346 return;
1347 }
1348
1349 ByteChunk valueBC = valueMB.getByteChunk();
1350 byte[] valueB = valueBC.getBytes();
1351 int valueL = valueBC.getLength();
1352 int valueS = valueBC.getStart();
1353 int colonPos = -1;
1354 if (hostNameC.length < valueL) {
1355 hostNameC = new char[valueL];
1356 }
1357
1358 boolean ipv6 = (valueB[valueS] == '[');
1359 boolean bracketClosed = false;
1360 for (int i = 0; i < valueL; i++) {
1361 char b = (char) valueB[i + valueS];
1362 hostNameC[i] = b;
1363 if (b == ']') {
1364 bracketClosed = true;
1365 } else if (b == ':') {
1366 if (!ipv6 || bracketClosed) {
1367 colonPos = i;
1368 break;
1369 }
1370 }
1371 }
1372
1373 if (colonPos < 0) {
1374 if (sslSupport == null) {
1375 // 80 - Default HTTP port
1376 request.setServerPort(80);
1377 } else {
1378 // 443 - Default HTTPS port
1379 request.setServerPort(443);
1380 }
1381 request.serverName().setChars(hostNameC, 0, valueL);
1382 } else {
1383
1384 request.serverName().setChars(hostNameC, 0, colonPos);
1385
1386 int port = 0;
1387 int mult = 1;
1388 for (int i = valueL - 1; i > colonPos; i--) {
1389 int charValue = HexUtils.DEC[(int) valueB[i + valueS]];
1390 if (charValue == -1) {
1391 // Invalid character
1392 error = true;
1393 // 400 - Bad request
1394 response.setStatus(400);
1395 break;
1396 }
1397 port = port + (charValue * mult);
1398 mult = 10 * mult;
1399 }
1400 request.setServerPort(port);
1401
1402 }
1403
1404 }
1405
1406
1407 /**
1408 * Check for compression
1409 */
1410 private boolean isCompressable() {
1411
1412 // Nope Compression could works in HTTP 1.0 also
1413 // cf: mod_deflate
1414
1415 // Compression only since HTTP 1.1
1416 // if (! http11)
1417 // return false;
1418
1419 // Check if browser support gzip encoding
1420 MessageBytes acceptEncodingMB =
1421 request.getMimeHeaders().getValue("accept-encoding");
1422
1423 if ((acceptEncodingMB == null)
1424 || (acceptEncodingMB.indexOf("gzip") == -1))
1425 return false;
1426
1427 // Check if content is not allready gzipped
1428 MessageBytes contentEncodingMB =
1429 response.getMimeHeaders().getValue("Content-Encoding");
1430
1431 if ((contentEncodingMB != null)
1432 && (contentEncodingMB.indexOf("gzip") != -1))
1433 return false;
1434
1435 // If force mode, allways compress (test purposes only)
1436 if (compressionLevel == 2)
1437 return true;
1438
1439 // Check for incompatible Browser
1440 if (noCompressionUserAgents != null) {
1441 MessageBytes userAgentValueMB =
1442 request.getMimeHeaders().getValue("user-agent");
1443 if(userAgentValueMB != null) {
1444 String userAgentValue = userAgentValueMB.toString();
1445
1446 // If one Regexp rule match, disable compression
1447 for (int i = 0; i < noCompressionUserAgents.length; i++)
1448 if (noCompressionUserAgents[i].matcher(userAgentValue).matches())
1449 return false;
1450 }
1451 }
1452
1453 // Check if suffisant len to trig the compression
1454 long contentLength = response.getContentLengthLong();
1455 if ((contentLength == -1)
1456 || (contentLength > compressionMinSize)) {
1457 // Check for compatible MIME-TYPE
1458 if (compressableMimeTypes != null) {
1459 return (startsWithStringArray(compressableMimeTypes,
1460 response.getContentType()));
1461 }
1462 }
1463
1464 return false;
1465 }
1466
1467
1468 /**
1469 * When committing the response, we have to validate the set of headers, as
1470 * well as setup the response filters.
1471 */
1472 protected void prepareResponse() {
1473
1474 boolean entityBody = true;
1475 contentDelimitation = false;
1476
1477 OutputFilter[] outputFilters = outputBuffer.getFilters();
1478
1479 if (http09 == true) {
1480 // HTTP/0.9
1481 outputBuffer.addActiveFilter
1482 (outputFilters[Constants.IDENTITY_FILTER]);
1483 return;
1484 }
1485
1486 int statusCode = response.getStatus();
1487 if ((statusCode == 204) || (statusCode == 205)
1488 || (statusCode == 304)) {
1489 // No entity body
1490 outputBuffer.addActiveFilter
1491 (outputFilters[Constants.VOID_FILTER]);
1492 entityBody = false;
1493 contentDelimitation = true;
1494 }
1495
1496 MessageBytes methodMB = request.method();
1497 if (methodMB.equals("HEAD")) {
1498 // No entity body
1499 outputBuffer.addActiveFilter
1500 (outputFilters[Constants.VOID_FILTER]);
1501 contentDelimitation = true;
1502 }
1503
1504 // Check for compression
1505 boolean useCompression = false;
1506 if (entityBody && (compressionLevel > 0)) {
1507 useCompression = isCompressable();
1508
1509 // Change content-length to -1 to force chunking
1510 if (useCompression) {
1511 response.setContentLength(-1);
1512 }
1513 }
1514
1515 MimeHeaders headers = response.getMimeHeaders();
1516 if (!entityBody) {
1517 response.setContentLength(-1);
1518 } else {
1519 String contentType = response.getContentType();
1520 if (contentType != null) {
1521 headers.setValue("Content-Type").setString(contentType);
1522 }
1523 String contentLanguage = response.getContentLanguage();
1524 if (contentLanguage != null) {
1525 headers.setValue("Content-Language")
1526 .setString(contentLanguage);
1527 }
1528 }
1529
1530 long contentLength = response.getContentLengthLong();
1531 if (contentLength != -1) {
1532 headers.setValue("Content-Length").setLong(contentLength);
1533 outputBuffer.addActiveFilter
1534 (outputFilters[Constants.IDENTITY_FILTER]);
1535 contentDelimitation = true;
1536 } else {
1537 if (entityBody && http11 && keepAlive) {
1538 outputBuffer.addActiveFilter
1539 (outputFilters[Constants.CHUNKED_FILTER]);
1540 contentDelimitation = true;
1541 headers.addValue(Constants.TRANSFERENCODING).setString(Constants.CHUNKED);
1542 } else {
1543 outputBuffer.addActiveFilter
1544 (outputFilters[Constants.IDENTITY_FILTER]);
1545 }
1546 }
1547
1548 if (useCompression) {
1549 outputBuffer.addActiveFilter(outputFilters[Constants.GZIP_FILTER]);
1550 headers.setValue("Content-Encoding").setString("gzip");
1551 // Make Proxies happy via Vary (from mod_deflate)
1552 headers.setValue("Vary").setString("Accept-Encoding");
1553 }
1554
1555 // Add date header
1556 String date = null;
1557 if (org.apache.coyote.Constants.IS_SECURITY_ENABLED){
1558 date = (String)AccessController.doPrivileged(
1559 new PrivilegedAction() {
1560 public Object run(){
1561 return FastHttpDateFormat.getCurrentDate();
1562 }
1563 }
1564 );
1565 } else {
1566 date = FastHttpDateFormat.getCurrentDate();
1567 }
1568 headers.setValue("Date").setString(date);
1569
1570 // FIXME: Add transfer encoding header
1571
1572 if ((entityBody) && (!contentDelimitation)) {
1573 // Mark as close the connection after the request, and add the
1574 // connection: close header
1575 keepAlive = false;
1576 }
1577
1578 // If we know that the request is bad this early, add the
1579 // Connection: close header.
1580 keepAlive = keepAlive && !statusDropsConnection(statusCode);
1581 if (!keepAlive) {
1582 headers.addValue(Constants.CONNECTION).setString(Constants.CLOSE);
1583 } else if (!http11 && !error) {
1584 headers.addValue(Constants.CONNECTION).setString(Constants.KEEPALIVE);
1585 }
1586
1587 // Build the response header
1588 outputBuffer.sendStatus();
1589
1590 // Add server header
1591 if (server != null) {
1592 headers.setValue("Server").setString(server);
1593 } else {
1594 outputBuffer.write(Constants.SERVER_BYTES);
1595 }
1596
1597 int size = headers.size();
1598 for (int i = 0; i < size; i++) {
1599 outputBuffer.sendHeader(headers.getName(i), headers.getValue(i));
1600 }
1601 outputBuffer.endHeaders();
1602
1603 }
1604
1605
1606 /**
1607 * Initialize standard input and output filters.
1608 */
1609 protected void initializeFilters() {
1610
1611 // Create and add the identity filters.
1612 inputBuffer.addFilter(new IdentityInputFilter());
1613 outputBuffer.addFilter(new IdentityOutputFilter());
1614
1615 // Create and add the chunked filters.
1616 inputBuffer.addFilter(new ChunkedInputFilter());
1617 outputBuffer.addFilter(new ChunkedOutputFilter());
1618
1619 // Create and add the void filters.
1620 inputBuffer.addFilter(new VoidInputFilter());
1621 outputBuffer.addFilter(new VoidOutputFilter());
1622
1623 // Create and add buffered input filter
1624 inputBuffer.addFilter(new BufferedInputFilter());
1625
1626 // Create and add the chunked filters.
1627 //inputBuffer.addFilter(new GzipInputFilter());
1628 outputBuffer.addFilter(new GzipOutputFilter());
1629
1630 }
1631
1632
1633 /**
1634 * Add an input filter to the current request.
1635 *
1636 * @return false if the encoding was not found (which would mean it is
1637 * unsupported)
1638 */
1639 protected boolean addInputFilter(InputFilter[] inputFilters,
1640 String encodingName) {
1641 if (encodingName.equals("identity")) {
1642 // Skip
1643 } else if (encodingName.equals("chunked")) {
1644 inputBuffer.addActiveFilter
1645 (inputFilters[Constants.CHUNKED_FILTER]);
1646 contentDelimitation = true;
1647 } else {
1648 for (int i = 2; i < inputFilters.length; i++) {
1649 if (inputFilters[i].getEncodingName()
1650 .toString().equals(encodingName)) {
1651 inputBuffer.addActiveFilter(inputFilters[i]);
1652 return true;
1653 }
1654 }
1655 return false;
1656 }
1657 return true;
1658 }
1659
1660
1661 /**
1662 * Specialized utility method: find a sequence of lower case bytes inside
1663 * a ByteChunk.
1664 */
1665 protected int findBytes(ByteChunk bc, byte[] b) {
1666
1667 byte first = b[0];
1668 byte[] buff = bc.getBuffer();
1669 int start = bc.getStart();
1670 int end = bc.getEnd();
1671
1672 // Look for first char
1673 int srcEnd = b.length;
1674
1675 for (int i = start; i <= (end - srcEnd); i++) {
1676 if (Ascii.toLower(buff[i]) != first) continue;
1677 // found first char, now look for a match
1678 int myPos = i+1;
1679 for (int srcPos = 1; srcPos < srcEnd; ) {
1680 if (Ascii.toLower(buff[myPos++]) != b[srcPos++])
1681 break;
1682 if (srcPos == srcEnd) return i - start; // found it
1683 }
1684 }
1685 return -1;
1686
1687 }
1688
1689 /**
1690 * Determine if we must drop the connection because of the HTTP status
1691 * code. Use the same list of codes as Apache/httpd.
1692 */
1693 protected boolean statusDropsConnection(int status) {
1694 return status == 400 /* SC_BAD_REQUEST */ ||
1695 status == 408 /* SC_REQUEST_TIMEOUT */ ||
1696 status == 411 /* SC_LENGTH_REQUIRED */ ||
1697 status == 413 /* SC_REQUEST_ENTITY_TOO_LARGE */ ||
1698 status == 414 /* SC_REQUEST_URI_TOO_LARGE */ ||
1699 status == 500 /* SC_INTERNAL_SERVER_ERROR */ ||
1700 status == 503 /* SC_SERVICE_UNAVAILABLE */ ||
1701 status == 501 /* SC_NOT_IMPLEMENTED */;
1702 }
1703
1704 }