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