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 package org.apache.coyote.http11;
18
19 import java.io.EOFException;
20 import java.io.IOException;
21 import java.nio.ByteBuffer;
22 import java.nio.charset.StandardCharsets;
23 import java.util.Arrays;
24
25 import org.apache.coyote.CloseNowException;
26 import org.apache.coyote.InputBuffer;
27 import org.apache.coyote.Request;
28 import org.apache.juli.logging.Log;
29 import org.apache.juli.logging.LogFactory;
30 import org.apache.tomcat.util.buf.MessageBytes;
31 import org.apache.tomcat.util.http.HeaderUtil;
32 import org.apache.tomcat.util.http.MimeHeaders;
33 import org.apache.tomcat.util.http.parser.HttpParser;
34 import org.apache.tomcat.util.net.ApplicationBufferHandler;
35 import org.apache.tomcat.util.net.SocketWrapperBase;
36 import org.apache.tomcat.util.res.StringManager;
37
38 /**
39 * InputBuffer for HTTP that provides request header parsing as well as transfer
40 * encoding.
41 */
42 public class Http11InputBuffer implements InputBuffer, ApplicationBufferHandler {
43
44 // -------------------------------------------------------------- Constants
45
46 private static final Log log = LogFactory.getLog(Http11InputBuffer.class);
47
48 /**
49 * The string manager for this package.
50 */
51 private static final StringManager sm = StringManager.getManager(Http11InputBuffer.class);
52
53
54 private static final byte[] CLIENT_PREFACE_START =
55 "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".getBytes(StandardCharsets.ISO_8859_1);
56
57 /**
58 * Associated Coyote request.
59 */
60 private final Request request;
61
62
63 /**
64 * Headers of the associated request.
65 */
66 private final MimeHeaders headers;
67
68
69 private final boolean rejectIllegalHeader;
70
71 /**
72 * State.
73 */
74 private boolean parsingHeader;
75
76
77 /**
78 * Swallow input ? (in the case of an expectation)
79 */
80 private boolean swallowInput;
81
82
83 /**
84 * The read buffer.
85 */
86 private ByteBuffer byteBuffer;
87
88
89 /**
90 * Pos of the end of the header in the buffer, which is also the
91 * start of the body.
92 */
93 private int end;
94
95
96 /**
97 * Wrapper that provides access to the underlying socket.
98 */
99 private SocketWrapperBase<?> wrapper;
100
101
102 /**
103 * Underlying input buffer.
104 */
105 private InputBuffer inputStreamInputBuffer;
106
107
108 /**
109 * Filter library.
110 * Note: Filter[Constants.CHUNKED_FILTER] is always the "chunked" filter.
111 */
112 private InputFilter[] filterLibrary;
113
114
115 /**
116 * Active filters (in order).
117 */
118 private InputFilter[] activeFilters;
119
120
121 /**
122 * Index of the last active filter.
123 */
124 private int lastActiveFilter;
125
126
127 /**
128 * Parsing state - used for non blocking parsing so that
129 * when more data arrives, we can pick up where we left off.
130 */
131 private byte prevChr = 0;
132 private byte chr = 0;
133 private boolean parsingRequestLine;
134 private int parsingRequestLinePhase = 0;
135 private boolean parsingRequestLineEol = false;
136 private int parsingRequestLineStart = 0;
137 private int parsingRequestLineQPos = -1;
138 private HeaderParsePosition headerParsePos;
139 private final HeaderParseData headerData = new HeaderParseData();
140 private final HttpParser httpParser;
141
142 /**
143 * Maximum allowed size of the HTTP request line plus headers plus any
144 * leading blank lines.
145 */
146 private final int headerBufferSize;
147
148 /**
149 * Known size of the NioChannel read buffer.
150 */
151 private int socketReadBufferSize;
152
153
154 // ----------------------------------------------------------- Constructors
155
156 public Http11InputBuffer(Request request, int headerBufferSize,
157 boolean rejectIllegalHeader, HttpParser httpParser) {
158
159 this.request = request;
160 headers = request.getMimeHeaders();
161
162 this.headerBufferSize = headerBufferSize;
163 this.rejectIllegalHeader = rejectIllegalHeader;
164 this.httpParser = httpParser;
165
166 filterLibrary = new InputFilter[0];
167 activeFilters = new InputFilter[0];
168 lastActiveFilter = -1;
169
170 parsingHeader = true;
171 parsingRequestLine = true;
172 parsingRequestLinePhase = 0;
173 parsingRequestLineEol = false;
174 parsingRequestLineStart = 0;
175 parsingRequestLineQPos = -1;
176 headerParsePos = HeaderParsePosition.HEADER_START;
177 swallowInput = true;
178
179 inputStreamInputBuffer = new SocketInputBuffer();
180 }
181
182
183 // ------------------------------------------------------------- Properties
184
185 /**
186 * Add an input filter to the filter library.
187 *
188 * @throws NullPointerException if the supplied filter is null
189 */
190 void addFilter(InputFilter filter) {
191
192 if (filter == null) {
193 throw new NullPointerException(sm.getString("iib.filter.npe"));
194 }
195
196 InputFilter[] newFilterLibrary = Arrays.copyOf(filterLibrary, filterLibrary.length + 1);
197 newFilterLibrary[filterLibrary.length] = filter;
198 filterLibrary = newFilterLibrary;
199
200 activeFilters = new InputFilter[filterLibrary.length];
201 }
202
203
204 /**
205 * Get filters.
206 */
207 InputFilter[] getFilters() {
208 return filterLibrary;
209 }
210
211
212 /**
213 * Add an input filter to the filter library.
214 */
215 void addActiveFilter(InputFilter filter) {
216
217 if (lastActiveFilter == -1) {
218 filter.setBuffer(inputStreamInputBuffer);
219 } else {
220 for (int i = 0; i <= lastActiveFilter; i++) {
221 if (activeFilters[i] == filter)
222 return;
223 }
224 filter.setBuffer(activeFilters[lastActiveFilter]);
225 }
226
227 activeFilters[++lastActiveFilter] = filter;
228
229 filter.setRequest(request);
230 }
231
232
233 /**
234 * Set the swallow input flag.
235 */
236 void setSwallowInput(boolean swallowInput) {
237 this.swallowInput = swallowInput;
238 }
239
240
241 // ---------------------------------------------------- InputBuffer Methods
242
243 @Override
244 public int doRead(ApplicationBufferHandler handler) throws IOException {
245
246 if (lastActiveFilter == -1)
247 return inputStreamInputBuffer.doRead(handler);
248 else
249 return activeFilters[lastActiveFilter].doRead(handler);
250
251 }
252
253
254 // ------------------------------------------------------- Protected Methods
255
256 /**
257 * Recycle the input buffer. This should be called when closing the
258 * connection.
259 */
260 void recycle() {
261 wrapper = null;
262 request.recycle();
263
264 for (int i = 0; i <= lastActiveFilter; i++) {
265 activeFilters[i].recycle();
266 }
267
268 byteBuffer.limit(0).position(0);
269 lastActiveFilter = -1;
270 parsingHeader = true;
271 swallowInput = true;
272
273 chr = 0;
274 prevChr = 0;
275 headerParsePos = HeaderParsePosition.HEADER_START;
276 parsingRequestLine = true;
277 parsingRequestLinePhase = 0;
278 parsingRequestLineEol = false;
279 parsingRequestLineStart = 0;
280 parsingRequestLineQPos = -1;
281 headerData.recycle();
282 }
283
284
285 /**
286 * End processing of current HTTP request.
287 * Note: All bytes of the current request should have been already
288 * consumed. This method only resets all the pointers so that we are ready
289 * to parse the next HTTP request.
290 */
291 void nextRequest() {
292 request.recycle();
293
294 if (byteBuffer.position() > 0) {
295 if (byteBuffer.remaining() > 0) {
296 // Copy leftover bytes to the beginning of the buffer
297 byteBuffer.compact();
298 byteBuffer.flip();
299 } else {
300 // Reset position and limit to 0
301 byteBuffer.position(0).limit(0);
302 }
303 }
304
305 // Recycle filters
306 for (int i = 0; i <= lastActiveFilter; i++) {
307 activeFilters[i].recycle();
308 }
309
310 // Reset pointers
311 lastActiveFilter = -1;
312 parsingHeader = true;
313 swallowInput = true;
314
315 headerParsePos = HeaderParsePosition.HEADER_START;
316 parsingRequestLine = true;
317 parsingRequestLinePhase = 0;
318 parsingRequestLineEol = false;
319 parsingRequestLineStart = 0;
320 parsingRequestLineQPos = -1;
321 headerData.recycle();
322 }
323
324
325 /**
326 * Read the request line. This function is meant to be used during the
327 * HTTP request header parsing. Do NOT attempt to read the request body
328 * using it.
329 *
330 * @throws IOException If an exception occurs during the underlying socket
331 * read operations, or if the given buffer is not big enough to accommodate
332 * the whole line.
333 *
334 * @return true if data is properly fed; false if no data is available
335 * immediately and thread should be freed
336 */
337 boolean parseRequestLine(boolean keptAlive, int connectionTimeout, int keepAliveTimeout)
338 throws IOException {
339
340 // check state
341 if (!parsingRequestLine) {
342 return true;
343 }
344 //
345 // Skipping blank lines
346 //
347 if (parsingRequestLinePhase < 2) {
348 do {
349 // Read new bytes if needed
350 if (byteBuffer.position() >= byteBuffer.limit()) {
351 if (keptAlive) {
352 // Haven't read any request data yet so use the keep-alive
353 // timeout.
354 wrapper.setReadTimeout(keepAliveTimeout);
355 }
356 if (!fill(false)) {
357 // A read is pending, so no longer in initial state
358 parsingRequestLinePhase = 1;
359 return false;
360 }
361 // At least one byte of the request has been received.
362 // Switch to the socket timeout.
363 wrapper.setReadTimeout(connectionTimeout);
364 }
365 if (!keptAlive && byteBuffer.position() == 0 && byteBuffer.limit() >= CLIENT_PREFACE_START.length - 1) {
366 boolean prefaceMatch = true;
367 for (int i = 0; i < CLIENT_PREFACE_START.length && prefaceMatch; i++) {
368 if (CLIENT_PREFACE_START[i] != byteBuffer.get(i)) {
369 prefaceMatch = false;
370 }
371 }
372 if (prefaceMatch) {
373 // HTTP/2 preface matched
374 parsingRequestLinePhase = -1;
375 return false;
376 }
377 }
378 // Set the start time once we start reading data (even if it is
379 // just skipping blank lines)
380 if (request.getStartTime() < 0) {
381 request.setStartTime(System.currentTimeMillis());
382 }
383 chr = byteBuffer.get();
384 } while ((chr == Constants.CR) || (chr == Constants.LF));
385 byteBuffer.position(byteBuffer.position() - 1);
386
387 parsingRequestLineStart = byteBuffer.position();
388 parsingRequestLinePhase = 2;
389 if (log.isDebugEnabled()) {
390 log.debug("Received ["
391 + new String(byteBuffer.array(), byteBuffer.position(), byteBuffer.remaining(), StandardCharsets.ISO_8859_1) + "]");
392 }
393 }
394 if (parsingRequestLinePhase == 2) {
395 //
396 // Reading the method name
397 // Method name is a token
398 //
399 boolean space = false;
400 while (!space) {
401 // Read new bytes if needed
402 if (byteBuffer.position() >= byteBuffer.limit()) {
403 if (!fill(false)) // request line parsing
404 return false;
405 }
406 // Spec says method name is a token followed by a single SP but
407 // also be tolerant of multiple SP and/or HT.
408 int pos = byteBuffer.position();
409 chr = byteBuffer.get();
410 if (chr == Constants.SP || chr == Constants.HT) {
411 space = true;
412 request.method().setBytes(byteBuffer.array(), parsingRequestLineStart,
413 pos - parsingRequestLineStart);
414 } else if (!HttpParser.isToken(chr)) {
415 byteBuffer.position(byteBuffer.position() - 1);
416 // Avoid unknown protocol triggering an additional error
417 request.protocol().setString(Constants.HTTP_11);
418 throw new IllegalArgumentException(sm.getString("iib.invalidmethod"));
419 }
420 }
421 parsingRequestLinePhase = 3;
422 }
423 if (parsingRequestLinePhase == 3) {
424 // Spec says single SP but also be tolerant of multiple SP and/or HT
425 boolean space = true;
426 while (space) {
427 // Read new bytes if needed
428 if (byteBuffer.position() >= byteBuffer.limit()) {
429 if (!fill(false)) // request line parsing
430 return false;
431 }
432 chr = byteBuffer.get();
433 if (!(chr == Constants.SP || chr == Constants.HT)) {
434 space = false;
435 byteBuffer.position(byteBuffer.position() - 1);
436 }
437 }
438 parsingRequestLineStart = byteBuffer.position();
439 parsingRequestLinePhase = 4;
440 }
441 if (parsingRequestLinePhase == 4) {
442 // Mark the current buffer position
443
444 int end = 0;
445 //
446 // Reading the URI
447 //
448 boolean space = false;
449 while (!space) {
450 // Read new bytes if needed
451 if (byteBuffer.position() >= byteBuffer.limit()) {
452 if (!fill(false)) // request line parsing
453 return false;
454 }
455 int pos = byteBuffer.position();
456 prevChr = chr;
457 chr = byteBuffer.get();
458 if (prevChr == Constants.CR && chr != Constants.LF) {
459 // CR not followed by LF so not an HTTP/0.9 request and
460 // therefore invalid. Trigger error handling.
461 // Avoid unknown protocol triggering an additional error
462 request.protocol().setString(Constants.HTTP_11);
463 throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget"));
464 }
465 if (chr == Constants.SP || chr == Constants.HT) {
466 space = true;
467 end = pos;
468 } else if (chr == Constants.CR) {
469 // HTTP/0.9 style request. CR is optional. LF is not.
470 } else if (chr == Constants.LF) {
471 // HTTP/0.9 style request
472 // Stop this processing loop
473 space = true;
474 // Skip the protocol processing
475 parsingRequestLinePhase = 6;
476 parsingRequestLineEol = true;
477 if (prevChr == Constants.CR) {
478 end = pos - 1;
479 } else {
480 end = pos;
481 }
482 } else if (chr == Constants.QUESTION && parsingRequestLineQPos == -1) {
483 parsingRequestLineQPos = pos;
484 } else if (parsingRequestLineQPos != -1 && !httpParser.isQueryRelaxed(chr)) {
485 // Avoid unknown protocol triggering an additional error
486 request.protocol().setString(Constants.HTTP_11);
487 // %nn decoding will be checked at the point of decoding
488 throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget"));
489 } else if (httpParser.isNotRequestTargetRelaxed(chr)) {
490 // Avoid unknown protocol triggering an additional error
491 request.protocol().setString(Constants.HTTP_11);
492 // This is a general check that aims to catch problems early
493 // Detailed checking of each part of the request target will
494 // happen in Http11Processor#prepareRequest()
495 throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget"));
496 }
497 }
498 if (parsingRequestLineQPos >= 0) {
499 request.queryString().setBytes(byteBuffer.array(), parsingRequestLineQPos + 1,
500 end - parsingRequestLineQPos - 1);
501 request.requestURI().setBytes(byteBuffer.array(), parsingRequestLineStart,
502 parsingRequestLineQPos - parsingRequestLineStart);
503 } else {
504 request.requestURI().setBytes(byteBuffer.array(), parsingRequestLineStart,
505 end - parsingRequestLineStart);
506 }
507 if (!parsingRequestLineEol) {
508 parsingRequestLinePhase = 5;
509 }
510 }
511 if (parsingRequestLinePhase == 5) {
512 // Spec says single SP but also be tolerant of multiple and/or HT
513 boolean space = true;
514 while (space) {
515 // Read new bytes if needed
516 if (byteBuffer.position() >= byteBuffer.limit()) {
517 if (!fill(false)) // request line parsing
518 return false;
519 }
520 byte chr = byteBuffer.get();
521 if (!(chr == Constants.SP || chr == Constants.HT)) {
522 space = false;
523 byteBuffer.position(byteBuffer.position() - 1);
524 }
525 }
526 parsingRequestLineStart = byteBuffer.position();
527 parsingRequestLinePhase = 6;
528
529 // Mark the current buffer position
530 end = 0;
531 }
532 if (parsingRequestLinePhase == 6) {
533 //
534 // Reading the protocol
535 // Protocol is always "HTTP/" DIGIT "." DIGIT
536 //
537 while (!parsingRequestLineEol) {
538 // Read new bytes if needed
539 if (byteBuffer.position() >= byteBuffer.limit()) {
540 if (!fill(false)) // request line parsing
541 return false;
542 }
543
544 int pos = byteBuffer.position();
545 prevChr = chr;
546 chr = byteBuffer.get();
547 if (chr == Constants.CR) {
548 // Possible end of request line. Need LF next.
549 } else if (prevChr == Constants.CR && chr == Constants.LF) {
550 end = pos - 1;
551 parsingRequestLineEol = true;
552 } else if (!HttpParser.isHttpProtocol(chr)) {
553 throw new IllegalArgumentException(sm.getString("iib.invalidHttpProtocol"));
554 }
555 }
556
557 if ((end - parsingRequestLineStart) > 0) {
558 request.protocol().setBytes(byteBuffer.array(), parsingRequestLineStart,
559 end - parsingRequestLineStart);
560 } else {
561 request.protocol().setString("");
562 }
563 parsingRequestLine = false;
564 parsingRequestLinePhase = 0;
565 parsingRequestLineEol = false;
566 parsingRequestLineStart = 0;
567 return true;
568 }
569 throw new IllegalStateException(sm.getString("iib.invalidPhase", Integer.valueOf(parsingRequestLinePhase)));
570 }
571
572
573 /**
574 * Parse the HTTP headers.
575 */
576 boolean parseHeaders() throws IOException {
577 if (!parsingHeader) {
578 throw new IllegalStateException(sm.getString("iib.parseheaders.ise.error"));
579 }
580
581 HeaderParseStatus status = HeaderParseStatus.HAVE_MORE_HEADERS;
582
583 do {
584 status = parseHeader();
585 // Checking that
586 // (1) Headers plus request line size does not exceed its limit
587 // (2) There are enough bytes to avoid expanding the buffer when
588 // reading body
589 // Technically, (2) is technical limitation, (1) is logical
590 // limitation to enforce the meaning of headerBufferSize
591 // From the way how buf is allocated and how blank lines are being
592 // read, it should be enough to check (1) only.
593 if (byteBuffer.position() > headerBufferSize || byteBuffer.capacity() - byteBuffer.position() < socketReadBufferSize) {
594 throw new IllegalArgumentException(sm.getString("iib.requestheadertoolarge.error"));
595 }
596 } while (status == HeaderParseStatus.HAVE_MORE_HEADERS);
597 if (status == HeaderParseStatus.DONE) {
598 parsingHeader = false;
599 end = byteBuffer.position();
600 return true;
601 } else {
602 return false;
603 }
604 }
605
606
607 int getParsingRequestLinePhase() {
608 return parsingRequestLinePhase;
609 }
610
611
612 /**
613 * End request (consumes leftover bytes).
614 *
615 * @throws IOException an underlying I/O error occurred
616 */
617 void endRequest() throws IOException {
618
619 if (swallowInput && (lastActiveFilter != -1)) {
620 int extraBytes = (int) activeFilters[lastActiveFilter].end();
621 byteBuffer.position(byteBuffer.position() - extraBytes);
622 }
623 }
624
625
626 /**
627 * Available bytes in the buffers (note that due to encoding, this may not
628 * correspond).
629 */
630 int available(boolean read) {
631 int available = byteBuffer.remaining();
632 if ((available == 0) && (lastActiveFilter >= 0)) {
633 for (int i = 0; (available == 0) && (i <= lastActiveFilter); i++) {
634 available = activeFilters[i].available();
635 }
636 }
637 if (available > 0 || !read) {
638 return available;
639 }
640
641 try {
642 if (wrapper.hasDataToRead()) {
643 fill(false);
644 available = byteBuffer.remaining();
645 }
646 } catch (IOException ioe) {
647 if (log.isDebugEnabled()) {
648 log.debug(sm.getString("iib.available.readFail"), ioe);
649 }
650 // Not ideal. This will indicate that data is available which should
651 // trigger a read which in turn will trigger another IOException and
652 // that one can be thrown.
653 available = 1;
654 }
655 return available;
656 }
657
658
659 /**
660 * Has all of the request body been read? There are subtle differences
661 * between this and available() > 0 primarily because of having to handle
662 * faking non-blocking reads with the blocking IO connector.
663 */
664 boolean isFinished() {
665 if (byteBuffer.limit() > byteBuffer.position()) {
666 // Data to read in the buffer so not finished
667 return false;
668 }
669
670 /*
671 * Don't use fill(false) here because in the following circumstances
672 * BIO will block - possibly indefinitely
673 * - client is using keep-alive and connection is still open
674 * - client has sent the complete request
675 * - client has not sent any of the next request (i.e. no pipelining)
676 * - application has read the complete request
677 */
678
679 // Check the InputFilters
680
681 if (lastActiveFilter >= 0) {
682 return activeFilters[lastActiveFilter].isFinished();
683 } else {
684 // No filters. Assume request is not finished. EOF will signal end of
685 // request.
686 return false;
687 }
688 }
689
690 ByteBuffer getLeftover() {
691 int available = byteBuffer.remaining();
692 if (available > 0) {
693 return ByteBuffer.wrap(byteBuffer.array(), byteBuffer.position(), available);
694 } else {
695 return null;
696 }
697 }
698
699
700 boolean isChunking() {
701 for (int i = 0; i < lastActiveFilter; i++) {
702 if (activeFilters[i] == filterLibrary[Constants.CHUNKED_FILTER]) {
703 return true;
704 }
705 }
706 return false;
707 }
708
709
710 void init(SocketWrapperBase<?> socketWrapper) {
711
712 wrapper = socketWrapper;
713 wrapper.setAppReadBufHandler(this);
714
715 int bufLength = headerBufferSize +
716 wrapper.getSocketBufferHandler().getReadBuffer().capacity();
717 if (byteBuffer == null || byteBuffer.capacity() < bufLength) {
718 byteBuffer = ByteBuffer.allocate(bufLength);
719 byteBuffer.position(0).limit(0);
720 }
721 }
722
723
724
725 // --------------------------------------------------------- Private Methods
726
727 /**
728 * Attempts to read some data into the input buffer.
729 *
730 * @return <code>true</code> if more data was added to the input buffer
731 * otherwise <code>false</code>
732 */
733 private boolean fill(boolean block) throws IOException {
734
735 if (parsingHeader) {
736 if (byteBuffer.limit() >= headerBufferSize) {
737 if (parsingRequestLine) {
738 // Avoid unknown protocol triggering an additional error
739 request.protocol().setString(Constants.HTTP_11);
740 }
741 throw new IllegalArgumentException(sm.getString("iib.requestheadertoolarge.error"));
742 }
743 } else {
744 byteBuffer.limit(end).position(end);
745 }
746
747 byteBuffer.mark();
748 if (byteBuffer.position() < byteBuffer.limit()) {
749 byteBuffer.position(byteBuffer.limit());
750 }
751 byteBuffer.limit(byteBuffer.capacity());
752 SocketWrapperBase<?> socketWrapper = this.wrapper;
753 int nRead = -1;
754 if (socketWrapper != null) {
755 nRead = socketWrapper.read(block, byteBuffer);
756 } else {
757 throw new CloseNowException(sm.getString("iib.eof.error"));
758 }
759 byteBuffer.limit(byteBuffer.position()).reset();
760 if (nRead > 0) {
761 return true;
762 } else if (nRead == -1) {
763 throw new EOFException(sm.getString("iib.eof.error"));
764 } else {
765 return false;
766 }
767
768 }
769
770
771 /**
772 * Parse an HTTP header.
773 *
774 * @return false after reading a blank line (which indicates that the
775 * HTTP header parsing is done
776 */
777 private HeaderParseStatus parseHeader() throws IOException {
778
779 while (headerParsePos == HeaderParsePosition.HEADER_START) {
780
781 // Read new bytes if needed
782 if (byteBuffer.position() >= byteBuffer.limit()) {
783 if (!fill(false)) {// parse header
784 headerParsePos = HeaderParsePosition.HEADER_START;
785 return HeaderParseStatus.NEED_MORE_DATA;
786 }
787 }
788
789 prevChr = chr;
790 chr = byteBuffer.get();
791
792 if (chr == Constants.CR && prevChr != Constants.CR) {
793 // Possible start of CRLF - process the next byte.
794 } else if (prevChr == Constants.CR && chr == Constants.LF) {
795 return HeaderParseStatus.DONE;
796 } else {
797 if (prevChr == Constants.CR) {
798 // Must have read two bytes (first was CR, second was not LF)
799 byteBuffer.position(byteBuffer.position() - 2);
800 } else {
801 // Must have only read one byte
802 byteBuffer.position(byteBuffer.position() - 1);
803 }
804 break;
805 }
806 }
807
808 if (headerParsePos == HeaderParsePosition.HEADER_START) {
809 // Mark the current buffer position
810 headerData.start = byteBuffer.position();
811 headerData.lineStart = headerData.start;
812 headerParsePos = HeaderParsePosition.HEADER_NAME;
813 }
814
815 //
816 // Reading the header name
817 // Header name is always US-ASCII
818 //
819
820 while (headerParsePos == HeaderParsePosition.HEADER_NAME) {
821
822 // Read new bytes if needed
823 if (byteBuffer.position() >= byteBuffer.limit()) {
824 if (!fill(false)) { // parse header
825 return HeaderParseStatus.NEED_MORE_DATA;
826 }
827 }
828
829 int pos = byteBuffer.position();
830 chr = byteBuffer.get();
831 if (chr == Constants.COLON) {
832 headerParsePos = HeaderParsePosition.HEADER_VALUE_START;
833 headerData.headerValue = headers.addValue(byteBuffer.array(), headerData.start,
834 pos - headerData.start);
835 pos = byteBuffer.position();
836 // Mark the current buffer position
837 headerData.start = pos;
838 headerData.realPos = pos;
839 headerData.lastSignificantChar = pos;
840 break;
841 } else if (!HttpParser.isToken(chr)) {
842 // Non-token characters are illegal in header names
843 // Parsing continues so the error can be reported in context
844 headerData.lastSignificantChar = pos;
845 byteBuffer.position(byteBuffer.position() - 1);
846 // skipLine() will handle the error
847 return skipLine();
848 }
849
850 // chr is next byte of header name. Convert to lowercase.
851 if ((chr >= Constants.A) && (chr <= Constants.Z)) {
852 byteBuffer.put(pos, (byte) (chr - Constants.LC_OFFSET));
853 }
854 }
855
856 // Skip the line and ignore the header
857 if (headerParsePos == HeaderParsePosition.HEADER_SKIPLINE) {
858 return skipLine();
859 }
860
861 //
862 // Reading the header value (which can be spanned over multiple lines)
863 //
864
865 while (headerParsePos == HeaderParsePosition.HEADER_VALUE_START ||
866 headerParsePos == HeaderParsePosition.HEADER_VALUE ||
867 headerParsePos == HeaderParsePosition.HEADER_MULTI_LINE) {
868
869 if (headerParsePos == HeaderParsePosition.HEADER_VALUE_START) {
870 // Skipping spaces
871 while (true) {
872 // Read new bytes if needed
873 if (byteBuffer.position() >= byteBuffer.limit()) {
874 if (!fill(false)) {// parse header
875 // HEADER_VALUE_START
876 return HeaderParseStatus.NEED_MORE_DATA;
877 }
878 }
879
880 chr = byteBuffer.get();
881 if (!(chr == Constants.SP || chr == Constants.HT)) {
882 headerParsePos = HeaderParsePosition.HEADER_VALUE;
883 byteBuffer.position(byteBuffer.position() - 1);
884 break;
885 }
886 }
887 }
888 if (headerParsePos == HeaderParsePosition.HEADER_VALUE) {
889
890 // Reading bytes until the end of the line
891 boolean eol = false;
892 while (!eol) {
893
894 // Read new bytes if needed
895 if (byteBuffer.position() >= byteBuffer.limit()) {
896 if (!fill(false)) {// parse header
897 // HEADER_VALUE
898 return HeaderParseStatus.NEED_MORE_DATA;
899 }
900 }
901
902 prevChr = chr;
903 chr = byteBuffer.get();
904 if (chr == Constants.CR) {
905 // Possible start of CRLF - process the next byte.
906 } else if (prevChr == Constants.CR && chr == Constants.LF) {
907 eol = true;
908 } else if (prevChr == Constants.CR) {
909 // Invalid value
910 // Delete the header (it will be the most recent one)
911 headers.removeHeader(headers.size() - 1);
912 return skipLine();
913 } else if (chr != Constants.HT && HttpParser.isControl(chr)) {
914 // Invalid value
915 // Delete the header (it will be the most recent one)
916 headers.removeHeader(headers.size() - 1);
917 return skipLine();
918 } else if (chr == Constants.SP || chr == Constants.HT) {
919 byteBuffer.put(headerData.realPos, chr);
920 headerData.realPos++;
921 } else {
922 byteBuffer.put(headerData.realPos, chr);
923 headerData.realPos++;
924 headerData.lastSignificantChar = headerData.realPos;
925 }
926 }
927
928 // Ignore whitespaces at the end of the line
929 headerData.realPos = headerData.lastSignificantChar;
930
931 // Checking the first character of the new line. If the character
932 // is a LWS, then it's a multiline header
933 headerParsePos = HeaderParsePosition.HEADER_MULTI_LINE;
934 }
935 // Read new bytes if needed
936 if (byteBuffer.position() >= byteBuffer.limit()) {
937 if (!fill(false)) {// parse header
938 // HEADER_MULTI_LINE
939 return HeaderParseStatus.NEED_MORE_DATA;
940 }
941 }
942
943 byte peek = byteBuffer.get(byteBuffer.position());
944 if (headerParsePos == HeaderParsePosition.HEADER_MULTI_LINE) {
945 if ((peek != Constants.SP) && (peek != Constants.HT)) {
946 headerParsePos = HeaderParsePosition.HEADER_START;
947 break;
948 } else {
949 // Copying one extra space in the buffer (since there must
950 // be at least one space inserted between the lines)
951 byteBuffer.put(headerData.realPos, peek);
952 headerData.realPos++;
953 headerParsePos = HeaderParsePosition.HEADER_VALUE_START;
954 }
955 }
956 }
957 // Set the header value
958 headerData.headerValue.setBytes(byteBuffer.array(), headerData.start,
959 headerData.lastSignificantChar - headerData.start);
960 headerData.recycle();
961 return HeaderParseStatus.HAVE_MORE_HEADERS;
962 }
963
964
965 private HeaderParseStatus skipLine() throws IOException {
966 headerParsePos = HeaderParsePosition.HEADER_SKIPLINE;
967 boolean eol = false;
968
969 // Reading bytes until the end of the line
970 while (!eol) {
971
972 // Read new bytes if needed
973 if (byteBuffer.position() >= byteBuffer.limit()) {
974 if (!fill(false)) {
975 return HeaderParseStatus.NEED_MORE_DATA;
976 }
977 }
978
979 int pos = byteBuffer.position();
980 prevChr = chr;
981 chr = byteBuffer.get();
982 if (chr == Constants.CR) {
983 // Skip
984 } else if (prevChr == Constants.CR && chr == Constants.LF) {
985 eol = true;
986 } else {
987 headerData.lastSignificantChar = pos;
988 }
989 }
990 if (rejectIllegalHeader || log.isDebugEnabled()) {
991 String message = sm.getString("iib.invalidheader",
992 HeaderUtil.toPrintableString(byteBuffer.array(), headerData.lineStart,
993 headerData.lastSignificantChar - headerData.lineStart + 1));
994 if (rejectIllegalHeader) {
995 throw new IllegalArgumentException(message);
996 }
997 log.debug(message);
998 }
999
1000 headerParsePos = HeaderParsePosition.HEADER_START;
1001 return HeaderParseStatus.HAVE_MORE_HEADERS;
1002 }
1003
1004
1005 // ----------------------------------------------------------- Inner classes
1006
1007 private enum HeaderParseStatus {
1008 DONE, HAVE_MORE_HEADERS, NEED_MORE_DATA
1009 }
1010
1011
1012 private enum HeaderParsePosition {
1013 /**
1014 * Start of a new header. A CRLF here means that there are no more
1015 * headers. Any other character starts a header name.
1016 */
1017 HEADER_START,
1018 /**
1019 * Reading a header name. All characters of header are HTTP_TOKEN_CHAR.
1020 * Header name is followed by ':'. No whitespace is allowed.<br>
1021 * Any non-HTTP_TOKEN_CHAR (this includes any whitespace) encountered
1022 * before ':' will result in the whole line being ignored.
1023 */
1024 HEADER_NAME,
1025 /**
1026 * Skipping whitespace before text of header value starts, either on the
1027 * first line of header value (just after ':') or on subsequent lines
1028 * when it is known that subsequent line starts with SP or HT.
1029 */
1030 HEADER_VALUE_START,
1031 /**
1032 * Reading the header value. We are inside the value. Either on the
1033 * first line or on any subsequent line. We come into this state from
1034 * HEADER_VALUE_START after the first non-SP/non-HT byte is encountered
1035 * on the line.
1036 */
1037 HEADER_VALUE,
1038 /**
1039 * Before reading a new line of a header. Once the next byte is peeked,
1040 * the state changes without advancing our position. The state becomes
1041 * either HEADER_VALUE_START (if that first byte is SP or HT), or
1042 * HEADER_START (otherwise).
1043 */
1044 HEADER_MULTI_LINE,
1045 /**
1046 * Reading all bytes until the next CRLF. The line is being ignored.
1047 */
1048 HEADER_SKIPLINE
1049 }
1050
1051
1052 private static class HeaderParseData {
1053 /**
1054 * The first character of the header line.
1055 */
1056 int lineStart = 0;
1057 /**
1058 * When parsing header name: first character of the header.<br>
1059 * When skipping broken header line: first character of the header.<br>
1060 * When parsing header value: first character after ':'.
1061 */
1062 int start = 0;
1063 /**
1064 * When parsing header name: not used (stays as 0).<br>
1065 * When skipping broken header line: not used (stays as 0).<br>
1066 * When parsing header value: starts as the first character after ':'.
1067 * Then is increased as far as more bytes of the header are harvested.
1068 * Bytes from buf[pos] are copied to buf[realPos]. Thus the string from
1069 * [start] to [realPos-1] is the prepared value of the header, with
1070 * whitespaces removed as needed.<br>
1071 */
1072 int realPos = 0;
1073 /**
1074 * When parsing header name: not used (stays as 0).<br>
1075 * When skipping broken header line: last non-CR/non-LF character.<br>
1076 * When parsing header value: position after the last not-LWS character.<br>
1077 */
1078 int lastSignificantChar = 0;
1079 /**
1080 * MB that will store the value of the header. It is null while parsing
1081 * header name and is created after the name has been parsed.
1082 */
1083 MessageBytes headerValue = null;
1084 public void recycle() {
1085 lineStart = 0;
1086 start = 0;
1087 realPos = 0;
1088 lastSignificantChar = 0;
1089 headerValue = null;
1090 }
1091 }
1092
1093
1094 // ------------------------------------- InputStreamInputBuffer Inner Class
1095
1096 /**
1097 * This class is an input buffer which will read its data from an input
1098 * stream.
1099 */
1100 private class SocketInputBuffer implements InputBuffer {
1101
1102 @Override
1103 public int doRead(ApplicationBufferHandler handler) throws IOException {
1104
1105 if (byteBuffer.position() >= byteBuffer.limit()) {
1106 // The application is reading the HTTP request body which is
1107 // always a blocking operation.
1108 if (!fill(true))
1109 return -1;
1110 }
1111
1112 int length = byteBuffer.remaining();
1113 handler.setByteBuffer(byteBuffer.duplicate());
1114 byteBuffer.position(byteBuffer.limit());
1115
1116 return length;
1117 }
1118 }
1119
1120
1121 @Override
1122 public void setByteBuffer(ByteBuffer buffer) {
1123 byteBuffer = buffer;
1124 }
1125
1126
1127 @Override
1128 public ByteBuffer getByteBuffer() {
1129 return byteBuffer;
1130 }
1131
1132
1133 @Override
1134 public void expand(int size) {
1135 if (byteBuffer.capacity() >= size) {
1136 byteBuffer.limit(size);
1137 }
1138 ByteBuffer temp = ByteBuffer.allocate(size);
1139 temp.put(byteBuffer);
1140 byteBuffer = temp;
1141 byteBuffer.mark();
1142 temp = null;
1143 }
1144 }
1145