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
19 package org.apache.coyote.http11;
20
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.EOFException;
24
25 import org.apache.tomcat.util.buf.ByteChunk;
26 import org.apache.tomcat.util.buf.MessageBytes;
27 import org.apache.tomcat.util.http.MimeHeaders;
28 import org.apache.tomcat.util.res.StringManager;
29
30 import org.apache.coyote.InputBuffer;
31 import org.apache.coyote.Request;
32
33 /**
34 * Implementation of InputBuffer which provides HTTP request header parsing as
35 * well as transfer decoding.
36 *
37 * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
38 */
39 public class InternalInputBuffer implements InputBuffer {
40
41
42 // -------------------------------------------------------------- Constants
43
44
45 // ----------------------------------------------------------- Constructors
46
47
48 /**
49 * Default constructor.
50 */
51 public InternalInputBuffer(Request request) {
52 this(request, Constants.DEFAULT_HTTP_HEADER_BUFFER_SIZE);
53 }
54
55
56 /**
57 * Alternate constructor.
58 */
59 public InternalInputBuffer(Request request, int headerBufferSize) {
60
61 this.request = request;
62 headers = request.getMimeHeaders();
63
64 buf = new byte[headerBufferSize];
65
66 inputStreamInputBuffer = new InputStreamInputBuffer();
67
68 filterLibrary = new InputFilter[0];
69 activeFilters = new InputFilter[0];
70 lastActiveFilter = -1;
71
72 parsingHeader = true;
73 swallowInput = true;
74
75 }
76
77
78 // -------------------------------------------------------------- Variables
79
80
81 /**
82 * The string manager for this package.
83 */
84 protected static StringManager sm =
85 StringManager.getManager(Constants.Package);
86
87
88 // ----------------------------------------------------- Instance Variables
89
90
91 /**
92 * Associated Coyote request.
93 */
94 protected Request request;
95
96
97 /**
98 * Headers of the associated request.
99 */
100 protected MimeHeaders headers;
101
102
103 /**
104 * State.
105 */
106 protected boolean parsingHeader;
107
108
109 /**
110 * Swallow input ? (in the case of an expectation)
111 */
112 protected boolean swallowInput;
113
114
115 /**
116 * Pointer to the current read buffer.
117 */
118 protected byte[] buf;
119
120
121 /**
122 * Last valid byte.
123 */
124 protected int lastValid;
125
126
127 /**
128 * Position in the buffer.
129 */
130 protected int pos;
131
132
133 /**
134 * Pos of the end of the header in the buffer, which is also the
135 * start of the body.
136 */
137 protected int end;
138
139
140 /**
141 * Underlying input stream.
142 */
143 protected InputStream inputStream;
144
145
146 /**
147 * Underlying input buffer.
148 */
149 protected InputBuffer inputStreamInputBuffer;
150
151
152 /**
153 * Filter library.
154 * Note: Filter[0] is always the "chunked" filter.
155 */
156 protected InputFilter[] filterLibrary;
157
158
159 /**
160 * Active filters (in order).
161 */
162 protected InputFilter[] activeFilters;
163
164
165 /**
166 * Index of the last active filter.
167 */
168 protected int lastActiveFilter;
169
170
171 // ------------------------------------------------------------- Properties
172
173
174 /**
175 * Set the underlying socket input stream.
176 */
177 public void setInputStream(InputStream inputStream) {
178
179 // FIXME: Check for null ?
180
181 this.inputStream = inputStream;
182
183 }
184
185
186 /**
187 * Get the underlying socket input stream.
188 */
189 public InputStream getInputStream() {
190
191 return inputStream;
192
193 }
194
195
196 /**
197 * Add an input filter to the filter library.
198 */
199 public void addFilter(InputFilter filter) {
200
201 // FIXME: Check for null ?
202
203 InputFilter[] newFilterLibrary =
204 new InputFilter[filterLibrary.length + 1];
205 for (int i = 0; i < filterLibrary.length; i++) {
206 newFilterLibrary[i] = filterLibrary[i];
207 }
208 newFilterLibrary[filterLibrary.length] = filter;
209 filterLibrary = newFilterLibrary;
210
211 activeFilters = new InputFilter[filterLibrary.length];
212
213 }
214
215
216 /**
217 * Get filters.
218 */
219 public InputFilter[] getFilters() {
220
221 return filterLibrary;
222
223 }
224
225
226 /**
227 * Clear filters.
228 */
229 public void clearFilters() {
230
231 filterLibrary = new InputFilter[0];
232 lastActiveFilter = -1;
233
234 }
235
236
237 /**
238 * Add an input filter to the filter library.
239 */
240 public void addActiveFilter(InputFilter filter) {
241
242 if (lastActiveFilter == -1) {
243 filter.setBuffer(inputStreamInputBuffer);
244 } else {
245 for (int i = 0; i <= lastActiveFilter; i++) {
246 if (activeFilters[i] == filter)
247 return;
248 }
249 filter.setBuffer(activeFilters[lastActiveFilter]);
250 }
251
252 activeFilters[++lastActiveFilter] = filter;
253
254 filter.setRequest(request);
255
256 }
257
258
259 /**
260 * Set the swallow input flag.
261 */
262 public void setSwallowInput(boolean swallowInput) {
263 this.swallowInput = swallowInput;
264 }
265
266
267 // --------------------------------------------------------- Public Methods
268
269
270 /**
271 * Recycle the input buffer. This should be called when closing the
272 * connection.
273 */
274 public void recycle() {
275
276 // Recycle Request object
277 request.recycle();
278
279 inputStream = null;
280 lastValid = 0;
281 pos = 0;
282 lastActiveFilter = -1;
283 parsingHeader = true;
284 swallowInput = true;
285
286 }
287
288
289 /**
290 * End processing of current HTTP request.
291 * Note: All bytes of the current request should have been already
292 * consumed. This method only resets all the pointers so that we are ready
293 * to parse the next HTTP request.
294 */
295 public void nextRequest() {
296
297 // Recycle Request object
298 request.recycle();
299
300 // Copy leftover bytes to the beginning of the buffer
301 if (lastValid - pos > 0) {
302 int npos = 0;
303 int opos = pos;
304 while (lastValid - opos > opos - npos) {
305 System.arraycopy(buf, opos, buf, npos, opos - npos);
306 npos += pos;
307 opos += pos;
308 }
309 System.arraycopy(buf, opos, buf, npos, lastValid - opos);
310 }
311
312 // Recycle filters
313 for (int i = 0; i <= lastActiveFilter; i++) {
314 activeFilters[i].recycle();
315 }
316
317 // Reset pointers
318 lastValid = lastValid - pos;
319 pos = 0;
320 lastActiveFilter = -1;
321 parsingHeader = true;
322 swallowInput = true;
323
324 }
325
326
327 /**
328 * End request (consumes leftover bytes).
329 *
330 * @throws IOException an undelying I/O error occured
331 */
332 public void endRequest()
333 throws IOException {
334
335 if (swallowInput && (lastActiveFilter != -1)) {
336 int extraBytes = (int) activeFilters[lastActiveFilter].end();
337 pos = pos - extraBytes;
338 }
339
340 }
341
342
343 /**
344 * Read the request line. This function is meant to be used during the
345 * HTTP request header parsing. Do NOT attempt to read the request body
346 * using it.
347 *
348 * @throws IOException If an exception occurs during the underlying socket
349 * read operations, or if the given buffer is not big enough to accomodate
350 * the whole line.
351 */
352 public void parseRequestLine()
353 throws IOException {
354
355 int start = 0;
356
357 //
358 // Skipping blank lines
359 //
360
361 byte chr = 0;
362 do {
363
364 // Read new bytes if needed
365 if (pos >= lastValid) {
366 if (!fill())
367 throw new EOFException(sm.getString("iib.eof.error"));
368 }
369
370 chr = buf[pos++];
371
372 } while ((chr == Constants.CR) || (chr == Constants.LF));
373
374 pos--;
375
376 // Mark the current buffer position
377 start = pos;
378
379 //
380 // Reading the method name
381 // Method name is always US-ASCII
382 //
383
384 boolean space = false;
385
386 while (!space) {
387
388 // Read new bytes if needed
389 if (pos >= lastValid) {
390 if (!fill())
391 throw new EOFException(sm.getString("iib.eof.error"));
392 }
393
394 // Spec says no CR or LF in method name
395 if (buf[pos] == Constants.CR || buf[pos] == Constants.LF) {
396 throw new IllegalArgumentException(
397 sm.getString("iib.invalidmethod"));
398 }
399 // Spec says single SP but it also says be tolerant of HT
400 if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
401 space = true;
402 request.method().setBytes(buf, start, pos - start);
403 }
404
405 pos++;
406
407 }
408
409
410 // Spec says single SP but also says be tolerant of multiple and/or HT
411 while (space) {
412 // Read new bytes if needed
413 if (pos >= lastValid) {
414 if (!fill())
415 throw new EOFException(sm.getString("iib.eof.error"));
416 }
417 if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
418 pos++;
419 } else {
420 space = false;
421 }
422 }
423
424 // Mark the current buffer position
425 start = pos;
426 int end = 0;
427 int questionPos = -1;
428
429 //
430 // Reading the URI
431 //
432
433 boolean eol = false;
434
435 while (!space) {
436
437 // Read new bytes if needed
438 if (pos >= lastValid) {
439 if (!fill())
440 throw new EOFException(sm.getString("iib.eof.error"));
441 }
442
443 // Spec says single SP but it also says be tolerant of HT
444 if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
445 space = true;
446 end = pos;
447 } else if ((buf[pos] == Constants.CR)
448 || (buf[pos] == Constants.LF)) {
449 // HTTP/0.9 style request
450 eol = true;
451 space = true;
452 end = pos;
453 } else if ((buf[pos] == Constants.QUESTION)
454 && (questionPos == -1)) {
455 questionPos = pos;
456 }
457
458 pos++;
459
460 }
461
462 request.unparsedURI().setBytes(buf, start, end - start);
463 if (questionPos >= 0) {
464 request.queryString().setBytes(buf, questionPos + 1,
465 end - questionPos - 1);
466 request.requestURI().setBytes(buf, start, questionPos - start);
467 } else {
468 request.requestURI().setBytes(buf, start, end - start);
469 }
470
471 // Spec says single SP but also says be tolerant of multiple and/or HT
472 while (space) {
473 // Read new bytes if needed
474 if (pos >= lastValid) {
475 if (!fill())
476 throw new EOFException(sm.getString("iib.eof.error"));
477 }
478 if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
479 pos++;
480 } else {
481 space = false;
482 }
483 }
484
485 // Mark the current buffer position
486 start = pos;
487 end = 0;
488
489 //
490 // Reading the protocol
491 // Protocol is always US-ASCII
492 //
493
494 while (!eol) {
495
496 // Read new bytes if needed
497 if (pos >= lastValid) {
498 if (!fill())
499 throw new EOFException(sm.getString("iib.eof.error"));
500 }
501
502 if (buf[pos] == Constants.CR) {
503 end = pos;
504 } else if (buf[pos] == Constants.LF) {
505 if (end == 0)
506 end = pos;
507 eol = true;
508 }
509
510 pos++;
511
512 }
513
514 if ((end - start) > 0) {
515 request.protocol().setBytes(buf, start, end - start);
516 } else {
517 request.protocol().setString("");
518 }
519
520 }
521
522
523 /**
524 * Parse the HTTP headers.
525 */
526 public void parseHeaders()
527 throws IOException {
528
529 while (parseHeader()) {
530 }
531
532 parsingHeader = false;
533 end = pos;
534
535 }
536
537
538 /**
539 * Parse an HTTP header.
540 *
541 * @return false after reading a blank line (which indicates that the
542 * HTTP header parsing is done
543 */
544 public boolean parseHeader()
545 throws IOException {
546
547 //
548 // Check for blank line
549 //
550
551 byte chr = 0;
552 while (true) {
553
554 // Read new bytes if needed
555 if (pos >= lastValid) {
556 if (!fill())
557 throw new EOFException(sm.getString("iib.eof.error"));
558 }
559
560 chr = buf[pos];
561
562 if ((chr == Constants.CR) || (chr == Constants.LF)) {
563 if (chr == Constants.LF) {
564 pos++;
565 return false;
566 }
567 } else {
568 break;
569 }
570
571 pos++;
572
573 }
574
575 // Mark the current buffer position
576 int start = pos;
577
578 //
579 // Reading the header name
580 // Header name is always US-ASCII
581 //
582
583 boolean colon = false;
584 MessageBytes headerValue = null;
585
586 while (!colon) {
587
588 // Read new bytes if needed
589 if (pos >= lastValid) {
590 if (!fill())
591 throw new EOFException(sm.getString("iib.eof.error"));
592 }
593
594 if (buf[pos] == Constants.COLON) {
595 colon = true;
596 headerValue = headers.addValue(buf, start, pos - start);
597 }
598 chr = buf[pos];
599 if ((chr >= Constants.A) && (chr <= Constants.Z)) {
600 buf[pos] = (byte) (chr - Constants.LC_OFFSET);
601 }
602
603 pos++;
604
605 }
606
607 // Mark the current buffer position
608 start = pos;
609 int realPos = pos;
610
611 //
612 // Reading the header value (which can be spanned over multiple lines)
613 //
614
615 boolean eol = false;
616 boolean validLine = true;
617
618 while (validLine) {
619
620 boolean space = true;
621
622 // Skipping spaces
623 while (space) {
624
625 // Read new bytes if needed
626 if (pos >= lastValid) {
627 if (!fill())
628 throw new EOFException(sm.getString("iib.eof.error"));
629 }
630
631 if ((buf[pos] == Constants.SP) || (buf[pos] == Constants.HT)) {
632 pos++;
633 } else {
634 space = false;
635 }
636
637 }
638
639 int lastSignificantChar = realPos;
640
641 // Reading bytes until the end of the line
642 while (!eol) {
643
644 // Read new bytes if needed
645 if (pos >= lastValid) {
646 if (!fill())
647 throw new EOFException(sm.getString("iib.eof.error"));
648 }
649
650 if (buf[pos] == Constants.CR) {
651 } else if (buf[pos] == Constants.LF) {
652 eol = true;
653 } else if (buf[pos] == Constants.SP) {
654 buf[realPos] = buf[pos];
655 realPos++;
656 } else {
657 buf[realPos] = buf[pos];
658 realPos++;
659 lastSignificantChar = realPos;
660 }
661
662 pos++;
663
664 }
665
666 realPos = lastSignificantChar;
667
668 // Checking the first character of the new line. If the character
669 // is a LWS, then it's a multiline header
670
671 // Read new bytes if needed
672 if (pos >= lastValid) {
673 if (!fill())
674 throw new EOFException(sm.getString("iib.eof.error"));
675 }
676
677 chr = buf[pos];
678 if ((chr != Constants.SP) && (chr != Constants.HT)) {
679 validLine = false;
680 } else {
681 eol = false;
682 // Copying one extra space in the buffer (since there must
683 // be at least one space inserted between the lines)
684 buf[realPos] = chr;
685 realPos++;
686 }
687
688 }
689
690 // Set the header value
691 headerValue.setBytes(buf, start, realPos - start);
692
693 return true;
694
695 }
696
697
698 // ---------------------------------------------------- InputBuffer Methods
699
700
701 /**
702 * Read some bytes.
703 */
704 public int doRead(ByteChunk chunk, Request req)
705 throws IOException {
706
707 if (lastActiveFilter == -1)
708 return inputStreamInputBuffer.doRead(chunk, req);
709 else
710 return activeFilters[lastActiveFilter].doRead(chunk,req);
711
712 }
713
714
715 // ------------------------------------------------------ Protected Methods
716
717
718 /**
719 * Fill the internal buffer using data from the undelying input stream.
720 *
721 * @return false if at end of stream
722 */
723 protected boolean fill()
724 throws IOException {
725
726 int nRead = 0;
727
728 if (parsingHeader) {
729
730 if (lastValid == buf.length) {
731 throw new IllegalArgumentException
732 (sm.getString("iib.requestheadertoolarge.error"));
733 }
734
735 nRead = inputStream.read(buf, pos, buf.length - lastValid);
736 if (nRead > 0) {
737 lastValid = pos + nRead;
738 }
739
740 } else {
741
742 if (buf.length - end < 4500) {
743 // In this case, the request header was really large, so we allocate a
744 // brand new one; the old one will get GCed when subsequent requests
745 // clear all references
746 buf = new byte[buf.length];
747 end = 0;
748 }
749 pos = end;
750 lastValid = pos;
751 nRead = inputStream.read(buf, pos, buf.length - lastValid);
752 if (nRead > 0) {
753 lastValid = pos + nRead;
754 }
755
756 }
757
758 return (nRead > 0);
759
760 }
761
762
763 // ------------------------------------- InputStreamInputBuffer Inner Class
764
765
766 /**
767 * This class is an input buffer which will read its data from an input
768 * stream.
769 */
770 protected class InputStreamInputBuffer
771 implements InputBuffer {
772
773
774 /**
775 * Read bytes into the specified chunk.
776 */
777 public int doRead(ByteChunk chunk, Request req )
778 throws IOException {
779
780 if (pos >= lastValid) {
781 if (!fill())
782 return -1;
783 }
784
785 int length = lastValid - pos;
786 chunk.setBytes(buf, pos, length);
787 pos = lastValid;
788
789 return (length);
790
791 }
792
793
794 }
795
796
797 }