001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.coyote.http11;
019:
020: import java.io.EOFException;
021: import java.io.IOException;
022: import java.nio.channels.Selector;
023:
024: import org.apache.coyote.InputBuffer;
025: import org.apache.coyote.Request;
026: import org.apache.tomcat.util.buf.ByteChunk;
027: import org.apache.tomcat.util.buf.MessageBytes;
028: import org.apache.tomcat.util.http.MimeHeaders;
029: import org.apache.tomcat.util.net.NioChannel;
030: import org.apache.tomcat.util.net.NioSelectorPool;
031: import org.apache.tomcat.util.res.StringManager;
032:
033: /**
034: * Implementation of InputBuffer which provides HTTP request header parsing as
035: * well as transfer decoding.
036: *
037: * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
038: * @author Filip Hanik
039: */
040: public class InternalNioInputBuffer implements InputBuffer {
041:
042: // -------------------------------------------------------------- Constants
043:
044: enum HeaderParseStatus {
045: DONE, HAVE_MORE_HEADERS, NEED_MORE_DATA
046: }
047:
048: enum HeaderParsePosition {
049: HEADER_START, HEADER_NAME, HEADER_VALUE, HEADER_MULTI_LINE
050: }
051:
052: // ----------------------------------------------------------- Constructors
053:
054: /**
055: * Alternate constructor.
056: */
057: public InternalNioInputBuffer(Request request,
058: int headerBufferSize, long readTimeout) {
059:
060: this .request = request;
061: headers = request.getMimeHeaders();
062:
063: buf = new byte[headerBufferSize];
064: // if (headerBufferSize < (8 * 1024)) {
065: // bbuf = ByteBuffer.allocateDirect(6 * 1500);
066: // } else {
067: // bbuf = ByteBuffer.allocateDirect((headerBufferSize / 1500 + 1) * 1500);
068: // }
069:
070: inputStreamInputBuffer = new SocketInputBuffer();
071:
072: filterLibrary = new InputFilter[0];
073: activeFilters = new InputFilter[0];
074: lastActiveFilter = -1;
075:
076: parsingHeader = true;
077: parsingRequestLine = true;
078: headerParsePos = HeaderParsePosition.HEADER_START;
079: headerData.recycle();
080: swallowInput = true;
081:
082: if (readTimeout < 0) {
083: this .readTimeout = -1;
084: } else {
085: this .readTimeout = readTimeout;
086: }
087:
088: }
089:
090: // -------------------------------------------------------------- Variables
091:
092: /**
093: * The string manager for this package.
094: */
095: protected static StringManager sm = StringManager
096: .getManager(Constants.Package);
097:
098: // ----------------------------------------------------- Instance Variables
099:
100: /**
101: * Associated Coyote request.
102: */
103: protected Request request;
104:
105: /**
106: * Headers of the associated request.
107: */
108: protected MimeHeaders headers;
109:
110: /**
111: * State.
112: */
113: protected boolean parsingHeader;
114: protected boolean parsingRequestLine;
115: protected HeaderParsePosition headerParsePos;
116:
117: /**
118: * Swallow input ? (in the case of an expectation)
119: */
120: protected boolean swallowInput;
121:
122: /**
123: * Pointer to the current read buffer.
124: */
125: protected byte[] buf;
126:
127: /**
128: * Last valid byte.
129: */
130: protected int lastValid;
131:
132: /**
133: * Position in the buffer.
134: */
135: protected int pos;
136:
137: /**
138: * Pos of the end of the header in the buffer, which is also the
139: * start of the body.
140: */
141: protected int end;
142:
143: /**
144: * Underlying socket.
145: */
146: protected NioChannel socket;
147:
148: /**
149: * Selector pool, for blocking reads and blocking writes
150: */
151: protected NioSelectorPool pool;
152:
153: /**
154: * Underlying input buffer.
155: */
156: protected InputBuffer inputStreamInputBuffer;
157:
158: /**
159: * Filter library.
160: * Note: Filter[0] is always the "chunked" filter.
161: */
162: protected InputFilter[] filterLibrary;
163:
164: /**
165: * Active filters (in order).
166: */
167: protected InputFilter[] activeFilters;
168:
169: /**
170: * Index of the last active filter.
171: */
172: protected int lastActiveFilter;
173:
174: /**
175: * The socket timeout used when reading the first block of the request
176: * header.
177: */
178: protected long readTimeout;
179:
180: // ------------------------------------------------------------- Properties
181:
182: /**
183: * Set the underlying socket.
184: */
185: public void setSocket(NioChannel socket) {
186: this .socket = socket;
187: }
188:
189: /**
190: * Get the underlying socket input stream.
191: */
192: public NioChannel getSocket() {
193: return socket;
194: }
195:
196: public void setSelectorPool(NioSelectorPool pool) {
197: this .pool = pool;
198: }
199:
200: public NioSelectorPool getSelectorPool() {
201: return pool;
202: }
203:
204: /**
205: * Add an input filter to the filter library.
206: */
207: public void addFilter(InputFilter filter) {
208:
209: InputFilter[] newFilterLibrary = new InputFilter[filterLibrary.length + 1];
210: for (int i = 0; i < filterLibrary.length; i++) {
211: newFilterLibrary[i] = filterLibrary[i];
212: }
213: newFilterLibrary[filterLibrary.length] = filter;
214: filterLibrary = newFilterLibrary;
215:
216: activeFilters = new InputFilter[filterLibrary.length];
217:
218: }
219:
220: /**
221: * Get filters.
222: */
223: public InputFilter[] getFilters() {
224:
225: return filterLibrary;
226:
227: }
228:
229: /**
230: * Clear filters.
231: */
232: public void clearFilters() {
233:
234: filterLibrary = new InputFilter[0];
235: lastActiveFilter = -1;
236:
237: }
238:
239: /**
240: * Add an input filter to the filter library.
241: */
242: public void addActiveFilter(InputFilter filter) {
243:
244: if (lastActiveFilter == -1) {
245: filter.setBuffer(inputStreamInputBuffer);
246: } else {
247: for (int i = 0; i <= lastActiveFilter; i++) {
248: if (activeFilters[i] == filter)
249: return;
250: }
251: filter.setBuffer(activeFilters[lastActiveFilter]);
252: }
253:
254: activeFilters[++lastActiveFilter] = filter;
255:
256: filter.setRequest(request);
257:
258: }
259:
260: /**
261: * Set the swallow input flag.
262: */
263: public void setSwallowInput(boolean swallowInput) {
264: this .swallowInput = swallowInput;
265: }
266:
267: // --------------------------------------------------------- Public Methods
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 = null;
279: lastValid = 0;
280: pos = 0;
281: lastActiveFilter = -1;
282: parsingHeader = true;
283: headerParsePos = HeaderParsePosition.HEADER_START;
284: parsingRequestLine = true;
285: headerData.recycle();
286: swallowInput = true;
287:
288: }
289:
290: /**
291: * End processing of current HTTP request.
292: * Note: All bytes of the current request should have been already
293: * consumed. This method only resets all the pointers so that we are ready
294: * to parse the next HTTP request.
295: */
296: public void nextRequest() {
297:
298: // Recycle Request object
299: request.recycle();
300:
301: // Copy leftover bytes to the beginning of the buffer
302: if (lastValid - pos > 0) {
303: int npos = 0;
304: int opos = pos;
305: while (lastValid - opos > opos - npos) {
306: System.arraycopy(buf, opos, buf, npos, opos - npos);
307: npos += pos;
308: opos += pos;
309: }
310: System.arraycopy(buf, opos, buf, npos, lastValid - opos);
311: }
312:
313: // Recycle filters
314: for (int i = 0; i <= lastActiveFilter; i++) {
315: activeFilters[i].recycle();
316: }
317:
318: // Reset pointers
319: lastValid = lastValid - pos;
320: pos = 0;
321: lastActiveFilter = -1;
322: parsingHeader = true;
323: headerParsePos = HeaderParsePosition.HEADER_START;
324: parsingRequestLine = true;
325: headerData.recycle();
326: swallowInput = true;
327:
328: }
329:
330: /**
331: * End request (consumes leftover bytes).
332: *
333: * @throws IOException an undelying I/O error occured
334: */
335: public void endRequest() throws IOException {
336:
337: if (swallowInput && (lastActiveFilter != -1)) {
338: int extraBytes = (int) activeFilters[lastActiveFilter]
339: .end();
340: pos = pos - extraBytes;
341: }
342:
343: }
344:
345: /**
346: * Read the request line. This function is meant to be used during the
347: * HTTP request header parsing. Do NOT attempt to read the request body
348: * using it.
349: *
350: * @throws IOException If an exception occurs during the underlying socket
351: * read operations, or if the given buffer is not big enough to accomodate
352: * the whole line.
353: * @return true if data is properly fed; false if no data is available
354: * immediately and thread should be freed
355: */
356: public boolean parseRequestLine(boolean useAvailableData)
357: throws IOException {
358:
359: //check state
360: if (!parsingRequestLine)
361: return true;
362:
363: int start = 0;
364:
365: //
366: // Skipping blank lines
367: //
368:
369: byte chr = 0;
370: do {
371:
372: // Read new bytes if needed
373: if (pos >= lastValid) {
374: if (useAvailableData) {
375: return false;
376: }
377: if (readTimeout == -1) {
378: if (!fill(false, true)) //request line parsing
379: throw new EOFException(sm
380: .getString("iib.eof.error"));
381: } else {
382: // Do a simple read with a short timeout
383: if (!readSocket(true, false))
384: return false;
385: }
386: }
387:
388: chr = buf[pos++];
389:
390: } while ((chr == Constants.CR) || (chr == Constants.LF));
391:
392: pos--;
393:
394: // Mark the current buffer position
395: start = pos;
396:
397: if (pos >= lastValid) {
398: if (useAvailableData) {
399: return false;
400: }
401: if (readTimeout == -1) {
402: if (!fill(false, true)) //request line parsing
403: return false;
404: } else {
405: // Do a simple read with a short timeout
406: if (!readSocket(true, true))
407: return false;
408: }
409: }
410:
411: //
412: // Reading the method name
413: // Method name is always US-ASCII
414: //
415:
416: boolean space = false;
417:
418: while (!space) {
419:
420: // Read new bytes if needed
421: if (pos >= lastValid) {
422: if (!fill(true, true)) //request line parsing
423: return false;
424: }
425:
426: if (buf[pos] == Constants.SP) {
427: space = true;
428: request.method().setBytes(buf, start, pos - start);
429: }
430:
431: pos++;
432:
433: }
434:
435: // Mark the current buffer position
436: start = pos;
437: int end = 0;
438: int questionPos = -1;
439:
440: //
441: // Reading the URI
442: //
443:
444: space = false;
445: boolean eol = false;
446:
447: while (!space) {
448:
449: // Read new bytes if needed
450: if (pos >= lastValid) {
451: if (!fill(true, true)) //request line parsing
452: return false;
453: }
454:
455: if (buf[pos] == Constants.SP) {
456: space = true;
457: end = pos;
458: } else if ((buf[pos] == Constants.CR)
459: || (buf[pos] == Constants.LF)) {
460: // HTTP/0.9 style request
461: eol = true;
462: space = true;
463: end = pos;
464: } else if ((buf[pos] == Constants.QUESTION)
465: && (questionPos == -1)) {
466: questionPos = pos;
467: }
468:
469: pos++;
470:
471: }
472:
473: request.unparsedURI().setBytes(buf, start, end - start);
474: if (questionPos >= 0) {
475: request.queryString().setBytes(buf, questionPos + 1,
476: end - questionPos - 1);
477: request.requestURI().setBytes(buf, start,
478: questionPos - start);
479: } else {
480: request.requestURI().setBytes(buf, start, end - start);
481: }
482:
483: // Mark the current buffer position
484: start = pos;
485: end = 0;
486:
487: //
488: // Reading the protocol
489: // Protocol is always US-ASCII
490: //
491:
492: while (!eol) {
493:
494: // Read new bytes if needed
495: if (pos >= lastValid) {
496: if (!fill(true, true)) //reques line parsing
497: return false;
498: }
499:
500: if (buf[pos] == Constants.CR) {
501: end = pos;
502: } else if (buf[pos] == Constants.LF) {
503: if (end == 0)
504: end = pos;
505: eol = true;
506: }
507:
508: pos++;
509:
510: }
511:
512: if ((end - start) > 0) {
513: request.protocol().setBytes(buf, start, end - start);
514: } else {
515: request.protocol().setString("");
516: }
517: parsingRequestLine = false;
518: return true;
519:
520: }
521:
522: private void expand(int newsize) {
523: if (newsize > buf.length) {
524: byte[] tmp = new byte[newsize];
525: System.arraycopy(buf, 0, tmp, 0, buf.length);
526: buf = tmp;
527: tmp = null;
528: }
529: }
530:
531: /**
532: * Perform blocking read with a timeout if desired
533: * @param timeout boolean - if we want to use the timeout data
534: * @param block - true if the system should perform a blocking read, false otherwise
535: * @return boolean - true if data was read, false is no data read, EOFException if EOF is reached
536: * @throws IOException if a socket exception occurs
537: * @throws EOFException if end of stream is reached
538: */
539: private boolean readSocket(boolean timeout, boolean block)
540: throws IOException {
541: int nRead = 0;
542: long rto = timeout ? this .readTimeout : -1;
543: socket.getBufHandler().getReadBuffer().clear();
544: if (block) {
545: Selector selector = null;
546: try {
547: selector = getSelectorPool().get();
548: } catch (IOException x) {
549: }
550: try {
551: nRead = getSelectorPool().read(
552: socket.getBufHandler().getReadBuffer(), socket,
553: selector, rto);
554: } catch (EOFException eof) {
555: nRead = -1;
556: } finally {
557: if (selector != null)
558: getSelectorPool().put(selector);
559: }
560: } else {
561: nRead = socket.read(socket.getBufHandler().getReadBuffer());
562: }
563: if (nRead > 0) {
564: socket.getBufHandler().getReadBuffer().flip();
565: socket.getBufHandler().getReadBuffer().limit(nRead);
566: expand(nRead + pos);
567: socket.getBufHandler().getReadBuffer().get(buf, pos, nRead);
568: lastValid = pos + nRead;
569: return true;
570: } else if (nRead == -1) {
571: //return false;
572: throw new EOFException(sm.getString("iib.eof.error"));
573: } else {
574: return false;
575: }
576: }
577:
578: /**
579: * Parse the HTTP headers.
580: */
581: public boolean parseHeaders() throws IOException {
582: HeaderParseStatus status = HeaderParseStatus.HAVE_MORE_HEADERS;
583:
584: do {
585: status = parseHeader();
586: } while (status == HeaderParseStatus.HAVE_MORE_HEADERS);
587: if (status == HeaderParseStatus.DONE) {
588: parsingHeader = false;
589: end = pos;
590: return true;
591: } else {
592: return false;
593: }
594: }
595:
596: /**
597: * Parse an HTTP header.
598: *
599: * @return false after reading a blank line (which indicates that the
600: * HTTP header parsing is done
601: */
602: public HeaderParseStatus parseHeader() throws IOException {
603:
604: //
605: // Check for blank line
606: //
607:
608: byte chr = 0;
609: while (headerParsePos == HeaderParsePosition.HEADER_START) {
610:
611: // Read new bytes if needed
612: if (pos >= lastValid) {
613: if (!fill(true, true)) {//parse header
614: headerParsePos = HeaderParsePosition.HEADER_START;
615: return HeaderParseStatus.NEED_MORE_DATA;
616: }
617: }
618:
619: chr = buf[pos];
620:
621: if ((chr == Constants.CR) || (chr == Constants.LF)) {
622: if (chr == Constants.LF) {
623: pos++;
624: return HeaderParseStatus.DONE;
625: }
626: } else {
627: break;
628: }
629:
630: pos++;
631:
632: }
633:
634: if (headerParsePos == HeaderParsePosition.HEADER_START) {
635: // Mark the current buffer position
636: headerData.start = pos;
637: headerParsePos = HeaderParsePosition.HEADER_NAME;
638: }
639:
640: //
641: // Reading the header name
642: // Header name is always US-ASCII
643: //
644:
645: while (headerParsePos == HeaderParsePosition.HEADER_NAME) {
646:
647: // Read new bytes if needed
648: if (pos >= lastValid) {
649: if (!fill(true, true)) { //parse header
650: return HeaderParseStatus.NEED_MORE_DATA;
651: }
652: }
653:
654: if (buf[pos] == Constants.COLON) {
655: headerParsePos = HeaderParsePosition.HEADER_VALUE;
656: headerData.headerValue = headers.addValue(buf,
657: headerData.start, pos - headerData.start);
658: }
659: chr = buf[pos];
660: if ((chr >= Constants.A) && (chr <= Constants.Z)) {
661: buf[pos] = (byte) (chr - Constants.LC_OFFSET);
662: }
663:
664: pos++;
665: if (headerParsePos == HeaderParsePosition.HEADER_VALUE) {
666: // Mark the current buffer position
667: headerData.start = pos;
668: headerData.realPos = pos;
669: }
670: }
671:
672: //
673: // Reading the header value (which can be spanned over multiple lines)
674: //
675:
676: boolean eol = false;
677:
678: while (headerParsePos == HeaderParsePosition.HEADER_VALUE
679: || headerParsePos == HeaderParsePosition.HEADER_MULTI_LINE) {
680: if (headerParsePos == HeaderParsePosition.HEADER_VALUE) {
681:
682: boolean space = true;
683:
684: // Skipping spaces
685: while (space) {
686:
687: // Read new bytes if needed
688: if (pos >= lastValid) {
689: if (!fill(true, true)) {//parse header
690: //HEADER_VALUE, should already be set
691: return HeaderParseStatus.NEED_MORE_DATA;
692: }
693: }
694:
695: if ((buf[pos] == Constants.SP)
696: || (buf[pos] == Constants.HT)) {
697: pos++;
698: } else {
699: space = false;
700: }
701:
702: }
703:
704: headerData.lastSignificantChar = headerData.realPos;
705:
706: // Reading bytes until the end of the line
707: while (!eol) {
708:
709: // Read new bytes if needed
710: if (pos >= lastValid) {
711: if (!fill(true, true)) {//parse header
712: //HEADER_VALUE
713: return HeaderParseStatus.NEED_MORE_DATA;
714: }
715:
716: }
717:
718: if (buf[pos] == Constants.CR) {
719: } else if (buf[pos] == Constants.LF) {
720: eol = true;
721: } else if (buf[pos] == Constants.SP) {
722: buf[headerData.realPos] = buf[pos];
723: headerData.realPos++;
724: } else {
725: buf[headerData.realPos] = buf[pos];
726: headerData.realPos++;
727: headerData.lastSignificantChar = headerData.realPos;
728: }
729:
730: pos++;
731:
732: }
733:
734: headerData.realPos = headerData.lastSignificantChar;
735:
736: // Checking the first character of the new line. If the character
737: // is a LWS, then it's a multiline header
738: headerParsePos = HeaderParsePosition.HEADER_MULTI_LINE;
739: }
740: // Read new bytes if needed
741: if (pos >= lastValid) {
742: if (!fill(true, true)) {//parse header
743:
744: //HEADER_MULTI_LINE
745: return HeaderParseStatus.NEED_MORE_DATA;
746: }
747: }
748:
749: chr = buf[pos];
750: if (headerParsePos == HeaderParsePosition.HEADER_MULTI_LINE) {
751: if ((chr != Constants.SP) && (chr != Constants.HT)) {
752: headerParsePos = HeaderParsePosition.HEADER_START;
753: } else {
754: eol = false;
755: // Copying one extra space in the buffer (since there must
756: // be at least one space inserted between the lines)
757: buf[headerData.realPos] = chr;
758: headerData.realPos++;
759: }
760: }
761: }
762: // Set the header value
763: headerData.headerValue.setBytes(buf, headerData.start,
764: headerData.realPos - headerData.start);
765: headerData.recycle();
766: return HeaderParseStatus.HAVE_MORE_HEADERS;
767: }
768:
769: protected HeaderParseData headerData = new HeaderParseData();
770:
771: public static class HeaderParseData {
772: int start = 0;
773: int realPos = 0;
774: int lastSignificantChar = 0;
775: MessageBytes headerValue = null;
776:
777: public void recycle() {
778: start = 0;
779: realPos = 0;
780: lastSignificantChar = 0;
781: headerValue = null;
782: }
783: }
784:
785: /**
786: * Available bytes (note that due to encoding, this may not correspond )
787: */
788: public int available() {
789: int result = (lastValid - pos);
790: if ((result == 0) && (lastActiveFilter >= 0)) {
791: for (int i = 0; (result == 0) && (i <= lastActiveFilter); i++) {
792: result = activeFilters[i].available();
793: }
794: }
795: return result;
796: }
797:
798: // ---------------------------------------------------- InputBuffer Methods
799:
800: /**
801: * Read some bytes.
802: */
803: public int doRead(ByteChunk chunk, Request req) throws IOException {
804:
805: if (lastActiveFilter == -1)
806: return inputStreamInputBuffer.doRead(chunk, req);
807: else
808: return activeFilters[lastActiveFilter].doRead(chunk, req);
809:
810: }
811:
812: // ------------------------------------------------------ Protected Methods
813:
814: /**
815: * Fill the internal buffer using data from the undelying input stream.
816: *
817: * @return false if at end of stream
818: */
819: protected boolean fill(boolean timeout, boolean block)
820: throws IOException, EOFException {
821:
822: boolean read = false;
823:
824: if (parsingHeader) {
825:
826: if (lastValid == buf.length) {
827: throw new IOException(sm
828: .getString("iib.requestheadertoolarge.error"));
829: }
830:
831: // Do a simple read with a short timeout
832: read = readSocket(timeout, block);
833: } else {
834:
835: if (buf.length - end < 4500) {
836: // In this case, the request header was really large, so we allocate a
837: // brand new one; the old one will get GCed when subsequent requests
838: // clear all references
839: buf = new byte[buf.length];
840: end = 0;
841: }
842: pos = end;
843: lastValid = pos;
844: // Do a simple read with a short timeout
845: read = readSocket(timeout, block);
846: }
847: return read;
848: }
849:
850: // ------------------------------------- InputStreamInputBuffer Inner Class
851:
852: /**
853: * This class is an input buffer which will read its data from an input
854: * stream.
855: */
856: protected class SocketInputBuffer implements InputBuffer {
857:
858: /**
859: * Read bytes into the specified chunk.
860: */
861: public int doRead(ByteChunk chunk, Request req)
862: throws IOException {
863:
864: if (pos >= lastValid) {
865: if (!fill(true, true)) //read body, must be blocking, as the thread is inside the app
866: return -1;
867: }
868:
869: int length = lastValid - pos;
870: chunk.setBytes(buf, pos, length);
871: pos = lastValid;
872:
873: return (length);
874:
875: }
876:
877: }
878:
879: }
|