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