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