001: // ========================================================================
002: // Copyright 2004-2006 Mort Bay Consulting Pty. Ltd.
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: // http://www.apache.org/licenses/LICENSE-2.0
008: // Unless required by applicable law or agreed to in writing, software
009: // distributed under the License is distributed on an "AS IS" BASIS,
010: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011: // See the License for the specific language governing permissions and
012: // limitations under the License.
013: // ========================================================================
014:
015: package org.mortbay.jetty;
016:
017: import java.io.IOException;
018: import java.io.InterruptedIOException;
019:
020: import javax.servlet.ServletInputStream;
021: import javax.servlet.http.HttpServletResponse;
022:
023: import org.mortbay.io.Buffer;
024: import org.mortbay.io.BufferUtil;
025: import org.mortbay.io.Buffers;
026: import org.mortbay.io.ByteArrayBuffer;
027: import org.mortbay.io.EndPoint;
028: import org.mortbay.io.View;
029: import org.mortbay.io.BufferCache.CachedBuffer;
030: import org.mortbay.log.Log;
031:
032: /* ------------------------------------------------------------------------------- */
033: /**
034: * @author gregw
035: */
036: public class HttpParser implements Parser {
037: // States
038: public static final int STATE_START = -11;
039: public static final int STATE_FIELD0 = -10;
040: public static final int STATE_SPACE1 = -9;
041: public static final int STATE_FIELD1 = -8;
042: public static final int STATE_SPACE2 = -7;
043: public static final int STATE_END0 = -6;
044: public static final int STATE_END1 = -5;
045: public static final int STATE_FIELD2 = -4;
046: public static final int STATE_HEADER = -3;
047: public static final int STATE_HEADER_NAME = -2;
048: public static final int STATE_HEADER_VALUE = -1;
049: public static final int STATE_END = 0;
050: public static final int STATE_EOF_CONTENT = 1;
051: public static final int STATE_CONTENT = 2;
052: public static final int STATE_CHUNKED_CONTENT = 3;
053: public static final int STATE_CHUNK_SIZE = 4;
054: public static final int STATE_CHUNK_PARAMS = 5;
055: public static final int STATE_CHUNK = 6;
056:
057: private Buffers _buffers; // source of buffers
058: private EndPoint _endp;
059: private Buffer _header; // Buffer for header data (and small _content)
060: private Buffer _body; // Buffer for large content
061: private Buffer _buffer; // The current buffer in use (either _header or _content)
062: private View _contentView = new View(); // View of the content in the buffer for {@link Input}
063: private int _headerBufferSize;
064:
065: private int _contentBufferSize;
066: private EventHandler _handler;
067: private CachedBuffer _cached;
068: private View _tok0; // Saved token: header name, request method or response version
069: private View _tok1; // Saved token: header value, request URI or response code
070: private String _multiLineValue;
071: private boolean _response = false; // true if parsing a HTTP response
072: /* ------------------------------------------------------------------------------- */
073: protected int _state = STATE_START;
074: protected byte _eol;
075: protected int _length;
076: protected long _contentLength;
077: protected long _contentPosition;
078: protected int _chunkLength;
079: protected int _chunkPosition;
080:
081: /* ------------------------------------------------------------------------------- */
082: /**
083: * Constructor.
084: */
085: public HttpParser(Buffer buffer, EventHandler handler) {
086: this ._header = buffer;
087: this ._buffer = buffer;
088: this ._handler = handler;
089:
090: if (buffer != null) {
091: _tok0 = new View(buffer);
092: _tok1 = new View(buffer);
093: _tok0.setPutIndex(_tok0.getIndex());
094: _tok1.setPutIndex(_tok1.getIndex());
095: }
096: }
097:
098: /* ------------------------------------------------------------------------------- */
099: /**
100: * Constructor.
101: * @param headerBufferSize size in bytes of header buffer
102: * @param contentBufferSize size in bytes of content buffer
103: */
104: public HttpParser(Buffers buffers, EndPoint endp,
105: EventHandler handler, int headerBufferSize,
106: int contentBufferSize) {
107: _buffers = buffers;
108: _endp = endp;
109: _handler = handler;
110: _headerBufferSize = headerBufferSize;
111: _contentBufferSize = contentBufferSize;
112: }
113:
114: /* ------------------------------------------------------------------------------- */
115: public long getContentLength() {
116: return _contentLength;
117: }
118:
119: /* ------------------------------------------------------------------------------- */
120: public int getState() {
121: return _state;
122: }
123:
124: /* ------------------------------------------------------------------------------- */
125: public boolean inContentState() {
126: return _state > 0;
127: }
128:
129: /* ------------------------------------------------------------------------------- */
130: public boolean inHeaderState() {
131: return _state < 0;
132: }
133:
134: /* ------------------------------------------------------------------------------- */
135: public boolean isChunking() {
136: return _contentLength == HttpTokens.CHUNKED_CONTENT;
137: }
138:
139: /* ------------------------------------------------------------ */
140: public boolean isIdle() {
141: return isState(STATE_START);
142: }
143:
144: /* ------------------------------------------------------------ */
145: public boolean isComplete() {
146: return isState(STATE_END);
147: }
148:
149: /* ------------------------------------------------------------ */
150: public boolean isMoreInBuffer() throws IOException {
151: if (_header != null && _header.hasContent() || _body != null
152: && _body.hasContent())
153: return true;
154:
155: return false;
156: }
157:
158: /* ------------------------------------------------------------------------------- */
159: public boolean isState(int state) {
160: return _state == state;
161: }
162:
163: /* ------------------------------------------------------------------------------- */
164: /**
165: * Parse until {@link #STATE_END END} state.
166: * If the parser is already in the END state, then it is {@link #reset reset} and re-parsed.
167: * @throws IllegalStateException If the buffers have already been partially parsed.
168: */
169: public void parse() throws IOException {
170: if (_state == STATE_END)
171: reset(false);
172: if (_state != STATE_START)
173: throw new IllegalStateException("!START");
174:
175: // continue parsing
176: while (_state != STATE_END)
177: parseNext();
178: }
179:
180: /* ------------------------------------------------------------------------------- */
181: /**
182: * Parse until END state.
183: * This method will parse any remaining content in the current buffer. It does not care about the
184: * {@link #getState current state} of the parser.
185: * @see #parse
186: * @see #parseNext
187: */
188: public long parseAvailable() throws IOException {
189: long len = parseNext();
190: long total = len > 0 ? len : 0;
191:
192: // continue parsing
193: while (!isComplete() && _buffer != null && _buffer.length() > 0) {
194: len = parseNext();
195: if (len > 0)
196: total += len;
197: }
198: return total;
199: }
200:
201: /* ------------------------------------------------------------------------------- */
202: /**
203: * Parse until next Event.
204: * @returns number of bytes filled from endpoint or -1 if fill never called.
205: */
206: public long parseNext() throws IOException {
207: long total_filled = -1;
208:
209: if (_buffer == null) {
210: if (_header == null) {
211: _header = _buffers.getBuffer(_headerBufferSize);
212: }
213: _buffer = _header;
214: _tok0 = new View(_header);
215: _tok1 = new View(_header);
216: _tok0.setPutIndex(_tok0.getIndex());
217: _tok1.setPutIndex(_tok1.getIndex());
218: }
219:
220: if (_state == STATE_END)
221: throw new IllegalStateException("STATE_END");
222: if (_state == STATE_CONTENT
223: && _contentPosition == _contentLength) {
224: _state = STATE_END;
225: _handler.messageComplete(_contentPosition);
226: return total_filled;
227: }
228:
229: int length = _buffer.length();
230:
231: // Fill buffer if we can
232: if (length == 0) {
233: int filled = -1;
234: if (_body != null && _buffer != _body) {
235: _buffer = _body;
236: filled = _buffer.length();
237: }
238:
239: if (_buffer.markIndex() == 0
240: && _buffer.putIndex() == _buffer.capacity())
241: throw new IOException("FULL");
242: if (_endp != null && filled <= 0) {
243: // Compress buffer if handling _content buffer
244: // TODO check this is not moving data too much
245: if (_buffer == _body)
246: _buffer.compact();
247:
248: if (_buffer.space() == 0) {
249: throw new IOException("FULL");
250: }
251:
252: try {
253: if (total_filled < 0)
254: total_filled = 0;
255: filled = _endp.fill(_buffer);
256: if (filled > 0)
257: total_filled += filled;
258: } catch (IOException e) {
259: Log.debug(e);
260: reset(true);
261: throw (e instanceof EofException) ? e
262: : new EofException(e);
263: }
264: }
265:
266: if (filled < 0) {
267: if (_state == STATE_EOF_CONTENT) {
268: _state = STATE_END;
269: _handler.messageComplete(_contentPosition);
270: return total_filled;
271: }
272: reset(true);
273: throw new EofException();
274: }
275: length = _buffer.length();
276: }
277:
278: // EventHandler header
279: byte ch;
280: byte[] array = _buffer.array();
281:
282: while (_state < STATE_END && length-- > 0) {
283: ch = _buffer.get();
284:
285: if (_eol == HttpTokens.CARRIAGE_RETURN
286: && ch == HttpTokens.LINE_FEED) {
287: _eol = HttpTokens.LINE_FEED;
288: continue;
289: }
290: _eol = 0;
291: switch (_state) {
292: case STATE_START:
293: _contentLength = HttpTokens.UNKNOWN_CONTENT;
294: _cached = null;
295: if (ch > HttpTokens.SPACE || ch < 0) {
296: _buffer.mark();
297: _state = STATE_FIELD0;
298: }
299: break;
300:
301: case STATE_FIELD0:
302: if (ch == HttpTokens.SPACE) {
303: _tok0.update(_buffer.markIndex(), _buffer
304: .getIndex() - 1);
305: _state = STATE_SPACE1;
306: continue;
307: } else if (ch < HttpTokens.SPACE && ch >= 0) {
308: throw new HttpException(
309: HttpServletResponse.SC_BAD_REQUEST);
310: }
311: break;
312:
313: case STATE_SPACE1:
314: if (ch > HttpTokens.SPACE || ch < 0) {
315: _buffer.mark();
316: _state = STATE_FIELD1;
317: _response = ch >= '1' && ch <= '5';
318: } else if (ch < HttpTokens.SPACE) {
319: throw new HttpException(
320: HttpServletResponse.SC_BAD_REQUEST);
321: }
322: break;
323:
324: case STATE_FIELD1:
325: if (ch == HttpTokens.SPACE) {
326: _tok1.update(_buffer.markIndex(), _buffer
327: .getIndex() - 1);
328: _state = STATE_SPACE2;
329: continue;
330: } else if (ch < HttpTokens.SPACE && ch >= 0) {
331: // HTTP/0.9
332: _handler.startRequest(HttpMethods.CACHE
333: .lookup(_tok0), _buffer.sliceFromMark(),
334: null);
335: _state = STATE_END;
336: _handler.headerComplete();
337: _handler.messageComplete(_contentPosition);
338: return total_filled;
339: }
340: break;
341:
342: case STATE_SPACE2:
343: if (ch > HttpTokens.SPACE || ch < 0) {
344: _buffer.mark();
345: _state = STATE_FIELD2;
346: } else if (ch < HttpTokens.SPACE) {
347: // HTTP/0.9
348: _handler.startRequest(HttpMethods.CACHE
349: .lookup(_tok0), _tok1, null);
350: _state = STATE_END;
351: _handler.headerComplete();
352: _handler.messageComplete(_contentPosition);
353: return total_filled;
354: }
355: break;
356:
357: case STATE_FIELD2:
358: if (ch == HttpTokens.CARRIAGE_RETURN
359: || ch == HttpTokens.LINE_FEED) {
360: if (_response)
361: _handler.startResponse(HttpVersions.CACHE
362: .lookup(_tok0),
363: BufferUtil.toInt(_tok1), _buffer
364: .sliceFromMark());
365: else
366: _handler.startRequest(HttpMethods.CACHE
367: .lookup(_tok0), _tok1,
368: HttpVersions.CACHE.lookup(_buffer
369: .sliceFromMark()));
370: _eol = ch;
371: _state = STATE_HEADER;
372: _tok0.setPutIndex(_tok0.getIndex());
373: _tok1.setPutIndex(_tok1.getIndex());
374: _multiLineValue = null;
375: return total_filled;
376: }
377: break;
378:
379: case STATE_HEADER:
380: if (ch == HttpTokens.COLON || ch == HttpTokens.SPACE
381: || ch == HttpTokens.TAB) {
382: // header value without name - continuation?
383: _length = -1;
384: _state = STATE_HEADER_VALUE;
385: } else {
386: // handler last header if any
387: if (_cached != null || _tok0.length() > 0
388: || _tok1.length() > 0
389: || _multiLineValue != null) {
390:
391: Buffer header = _cached != null ? _cached
392: : HttpHeaders.CACHE.lookup(_tok0);
393: _cached = null;
394: Buffer value = _multiLineValue == null ? (Buffer) _tok1
395: : (Buffer) new ByteArrayBuffer(
396: _multiLineValue);
397:
398: int ho = HttpHeaders.CACHE.getOrdinal(header);
399: if (ho >= 0) {
400: int vo = -1;
401:
402: switch (ho) {
403: case HttpHeaders.CONTENT_LENGTH_ORDINAL:
404: if (_contentLength != HttpTokens.CHUNKED_CONTENT) {
405: _contentLength = BufferUtil
406: .toLong(value);
407: if (_contentLength <= 0)
408: _contentLength = HttpTokens.NO_CONTENT;
409: }
410: break;
411:
412: case HttpHeaders.TRANSFER_ENCODING_ORDINAL:
413: value = HttpHeaderValues.CACHE
414: .lookup(value);
415: vo = HttpHeaderValues.CACHE
416: .getOrdinal(value);
417: if (HttpHeaderValues.CHUNKED_ORDINAL == vo)
418: _contentLength = HttpTokens.CHUNKED_CONTENT;
419: else {
420: String c = value.toString();
421: if (c
422: .endsWith(HttpHeaderValues.CHUNKED))
423: _contentLength = HttpTokens.CHUNKED_CONTENT;
424:
425: else if (c
426: .indexOf(HttpHeaderValues.CHUNKED) >= 0)
427: throw new HttpException(400,
428: null);
429: }
430: break;
431: }
432: }
433:
434: _handler.parsedHeader(header, value);
435: _tok0.setPutIndex(_tok0.getIndex());
436: _tok1.setPutIndex(_tok1.getIndex());
437: _multiLineValue = null;
438: }
439:
440: // now handle ch
441: if (ch == HttpTokens.CARRIAGE_RETURN
442: || ch == HttpTokens.LINE_FEED) {
443: // End of header
444:
445: // work out the _content demarcation
446: if (_contentLength == HttpTokens.UNKNOWN_CONTENT)
447: _contentLength = _response ? HttpTokens.EOF_CONTENT
448: : HttpTokens.NO_CONTENT;
449:
450: _contentPosition = 0;
451: _eol = ch;
452: // We convert _contentLength to an int for this switch statement because
453: // we don't care about the amount of data available just whether there is some.
454: switch (_contentLength > Integer.MAX_VALUE ? Integer.MAX_VALUE
455: : (int) _contentLength) {
456: case HttpTokens.EOF_CONTENT:
457: _state = STATE_EOF_CONTENT;
458: if (_body == null && _buffers != null)
459: _body = _buffers
460: .getBuffer(_contentBufferSize);
461:
462: _handler.headerComplete(); // May recurse here !
463: break;
464:
465: case HttpTokens.CHUNKED_CONTENT:
466: _state = STATE_CHUNKED_CONTENT;
467: if (_body == null && _buffers != null)
468: _body = _buffers
469: .getBuffer(_contentBufferSize);
470: _handler.headerComplete(); // May recurse here !
471: break;
472:
473: case HttpTokens.NO_CONTENT:
474: _state = STATE_END;
475: _handler.headerComplete();
476: _handler.messageComplete(_contentPosition);
477: break;
478:
479: default:
480: _state = STATE_CONTENT;
481:
482: if (_buffers != null
483: && _body == null
484: && _buffer == _header
485: && _contentLength > (_header
486: .capacity() - _header
487: .getIndex()))
488: _body = _buffers
489: .getBuffer(_contentBufferSize);
490: _handler.headerComplete(); // May recurse here !
491: break;
492: }
493: return total_filled;
494: } else {
495: // New header
496: _length = 1;
497: _buffer.mark();
498: _state = STATE_HEADER_NAME;
499:
500: // try cached name!
501: if (array != null)
502: _cached = HttpHeaders.CACHE.getBest(array,
503: _buffer.markIndex(), length + 1);
504: if (_cached != null) {
505: _length = _cached.length();
506: _buffer.setGetIndex(_buffer.markIndex()
507: + _length);
508: length = _buffer.length();
509: }
510: }
511: }
512: break;
513:
514: case STATE_HEADER_NAME:
515: if (ch == HttpTokens.CARRIAGE_RETURN
516: || ch == HttpTokens.LINE_FEED) {
517: if (_length > 0)
518: _tok0.update(_buffer.markIndex(), _buffer
519: .markIndex()
520: + _length);
521: _eol = ch;
522: _state = STATE_HEADER;
523: } else if (ch == HttpTokens.COLON) {
524: if (_length > 0 && _cached == null)
525: _tok0.update(_buffer.markIndex(), _buffer
526: .markIndex()
527: + _length);
528: _length = -1;
529: _state = STATE_HEADER_VALUE;
530: } else if (ch != HttpTokens.SPACE
531: && ch != HttpTokens.TAB) {
532: // Drag the mark
533: if (_length == -1)
534: _buffer.mark();
535: _length = _buffer.getIndex() - _buffer.markIndex();
536: }
537: break;
538:
539: case STATE_HEADER_VALUE:
540: if (ch == HttpTokens.CARRIAGE_RETURN
541: || ch == HttpTokens.LINE_FEED) {
542: if (_length > 0) {
543: if (_tok1.length() == 0)
544: _tok1.update(_buffer.markIndex(), _buffer
545: .markIndex()
546: + _length);
547: else {
548: // Continuation line!
549: if (_multiLineValue == null)
550: _multiLineValue = _tok1.toString();
551: _tok1.update(_buffer.markIndex(), _buffer
552: .markIndex()
553: + _length);
554: _multiLineValue += " " + _tok1.toString();
555: }
556: }
557: _eol = ch;
558: _state = STATE_HEADER;
559: } else if (ch != HttpTokens.SPACE
560: && ch != HttpTokens.TAB) {
561: if (_length == -1)
562: _buffer.mark();
563: _length = _buffer.getIndex() - _buffer.markIndex();
564: }
565: break;
566: }
567: } // end of HEADER states loop
568:
569: // ==========================
570:
571: // Handle _content
572: length = _buffer.length();
573: Buffer chunk;
574: while (_state > STATE_END && length > 0) {
575: if (_eol == HttpTokens.CARRIAGE_RETURN
576: && _buffer.peek() == HttpTokens.LINE_FEED) {
577: _eol = _buffer.get();
578: length = _buffer.length();
579: continue;
580: }
581: _eol = 0;
582: switch (_state) {
583: case STATE_EOF_CONTENT:
584: chunk = _buffer.get(_buffer.length());
585: _contentPosition += chunk.length();
586: _contentView.update(chunk);
587: _handler.content(chunk);
588: // TODO adjust the _buffer to keep unconsumed content
589: return total_filled;
590:
591: case STATE_CONTENT: {
592: long remaining = _contentLength - _contentPosition;
593: if (remaining == 0) {
594: _state = STATE_END;
595: _handler.messageComplete(_contentPosition);
596: return total_filled;
597: } else if (length >= remaining) {
598: // We can cast reamining to an int as we know that it is smaller than
599: // or equal to length which is already an int.
600: length = (int) remaining;
601: _state = STATE_END;
602: }
603: chunk = _buffer.get(length);
604: _contentPosition += chunk.length();
605: _contentView.update(chunk);
606: _handler.content(chunk);
607: // TODO adjust the _buffer to keep unconsumed content
608: return total_filled;
609: }
610:
611: case STATE_CHUNKED_CONTENT: {
612: ch = _buffer.peek();
613: if (ch == HttpTokens.CARRIAGE_RETURN
614: || ch == HttpTokens.LINE_FEED)
615: _eol = _buffer.get();
616: else if (ch <= HttpTokens.SPACE)
617: _buffer.get();
618: else {
619: _chunkLength = 0;
620: _chunkPosition = 0;
621: _state = STATE_CHUNK_SIZE;
622: }
623: break;
624: }
625:
626: case STATE_CHUNK_SIZE: {
627: ch = _buffer.get();
628: if (ch == HttpTokens.CARRIAGE_RETURN
629: || ch == HttpTokens.LINE_FEED) {
630: _eol = ch;
631: if (_chunkLength == 0) {
632: _state = STATE_END;
633: _handler.messageComplete(_contentPosition);
634: return total_filled;
635: } else
636: _state = STATE_CHUNK;
637: } else if (ch <= HttpTokens.SPACE
638: || ch == HttpTokens.SEMI_COLON)
639: _state = STATE_CHUNK_PARAMS;
640: else if (ch >= '0' && ch <= '9')
641: _chunkLength = _chunkLength * 16 + (ch - '0');
642: else if (ch >= 'a' && ch <= 'f')
643: _chunkLength = _chunkLength * 16 + (10 + ch - 'a');
644: else if (ch >= 'A' && ch <= 'F')
645: _chunkLength = _chunkLength * 16 + (10 + ch - 'A');
646: else
647: throw new IOException("bad chunk char: " + ch);
648: break;
649: }
650:
651: case STATE_CHUNK_PARAMS: {
652: ch = _buffer.get();
653: if (ch == HttpTokens.CARRIAGE_RETURN
654: || ch == HttpTokens.LINE_FEED) {
655: _eol = ch;
656: if (_chunkLength == 0) {
657: _state = STATE_END;
658: _handler.messageComplete(_contentPosition);
659: return total_filled;
660: } else
661: _state = STATE_CHUNK;
662: }
663: break;
664: }
665:
666: case STATE_CHUNK: {
667: int remaining = _chunkLength - _chunkPosition;
668: if (remaining == 0) {
669: _state = STATE_CHUNKED_CONTENT;
670: break;
671: } else if (length > remaining)
672: length = remaining;
673: chunk = _buffer.get(length);
674: _contentPosition += chunk.length();
675: _chunkPosition += chunk.length();
676: _contentView.update(chunk);
677: _handler.content(chunk);
678: // TODO adjust the _buffer to keep unconsumed content
679: return total_filled;
680: }
681: }
682:
683: length = _buffer.length();
684: }
685: return total_filled;
686: }
687:
688: /* ------------------------------------------------------------------------------- */
689: public void reset(boolean returnBuffers) {
690: synchronized (this ) // prevent dual reset.
691: {
692: _state = STATE_START;
693: _contentLength = HttpTokens.UNKNOWN_CONTENT;
694: _contentPosition = 0;
695: _length = 0;
696: _response = false;
697:
698: if (_buffer != null && _buffer.length() > 0
699: && _eol == HttpTokens.CARRIAGE_RETURN
700: && _buffer.peek() == HttpTokens.LINE_FEED) {
701: _buffer.skip(1);
702: _eol = HttpTokens.LINE_FEED;
703: }
704:
705: if (_body != null) {
706: if (_body.hasContent()) {
707: _header.setMarkIndex(-1);
708: _header.compact();
709: // TODO if pipelined requests received after big input - maybe this is not good?.
710: _body.skip(_header.put(_body));
711:
712: }
713:
714: if (_body.length() == 0) {
715: if (_buffers != null && returnBuffers)
716: _buffers.returnBuffer(_body);
717: _body = null;
718: } else {
719: _body.setMarkIndex(-1);
720: _body.compact();
721: }
722: }
723:
724: if (_header != null) {
725: _header.setMarkIndex(-1);
726: if (!_header.hasContent() && _buffers != null
727: && returnBuffers) {
728: _buffers.returnBuffer(_header);
729: _header = null;
730: _buffer = null;
731: } else {
732: _header.compact();
733: _tok0.update(_header);
734: _tok0.update(0, 0);
735: _tok1.update(_header);
736: _tok1.update(0, 0);
737: }
738: }
739:
740: _buffer = _header;
741: }
742: }
743:
744: /* ------------------------------------------------------------------------------- */
745: public void setState(int state) {
746: this ._state = state;
747: _contentLength = HttpTokens.UNKNOWN_CONTENT;
748: }
749:
750: /* ------------------------------------------------------------------------------- */
751: public String toString(Buffer buf) {
752: return "state=" + _state + " length=" + _length + " buf="
753: + buf.hashCode();
754: }
755:
756: public Buffer getHeaderBuffer() {
757: if (_header == null) {
758: _header = _buffers.getBuffer(_headerBufferSize);
759: }
760: return _header;
761: }
762:
763: /* ------------------------------------------------------------ */
764: /* ------------------------------------------------------------ */
765: /* ------------------------------------------------------------ */
766: public static abstract class EventHandler {
767: public abstract void content(Buffer ref) throws IOException;
768:
769: public void headerComplete() throws IOException {
770: }
771:
772: public void messageComplete(long contextLength)
773: throws IOException {
774: }
775:
776: /**
777: * This is the method called by parser when a HTTP Header name and value is found
778: */
779: public void parsedHeader(Buffer name, Buffer value)
780: throws IOException {
781: }
782:
783: /**
784: * This is the method called by parser when the HTTP request line is parsed
785: */
786: public abstract void startRequest(Buffer method, Buffer url,
787: Buffer version) throws IOException;
788:
789: /**
790: * This is the method called by parser when the HTTP request line is parsed
791: */
792: public abstract void startResponse(Buffer version, int status,
793: Buffer reason) throws IOException;
794: }
795:
796: /* ------------------------------------------------------------ */
797: /* ------------------------------------------------------------ */
798: /* ------------------------------------------------------------ */
799: public static class Input extends ServletInputStream {
800: protected HttpParser _parser;
801: protected EndPoint _endp;
802: protected long _maxIdleTime;
803: protected View _content;
804:
805: /* ------------------------------------------------------------ */
806: public Input(HttpParser parser, long maxIdleTime) {
807: _parser = parser;
808: _endp = parser._endp;
809: _maxIdleTime = maxIdleTime;
810: _content = _parser._contentView;
811: }
812:
813: /* ------------------------------------------------------------ */
814: /*
815: * @see java.io.InputStream#read()
816: */
817: public int read() throws IOException {
818: int c = -1;
819: if (blockForContent())
820: c = 0xff & _content.get();
821: return c;
822: }
823:
824: /* ------------------------------------------------------------ */
825: /*
826: * @see java.io.InputStream#read(byte[], int, int)
827: */
828: public int read(byte[] b, int off, int len) throws IOException {
829: int l = -1;
830: if (blockForContent())
831: l = _content.get(b, off, len);
832: return l;
833: }
834:
835: /* ------------------------------------------------------------ */
836: private boolean blockForContent() throws IOException {
837: if (_content.length() > 0)
838: return true;
839: if (_parser.isState(HttpParser.STATE_END))
840: return false;
841:
842: // Handle simple end points.
843: if (_endp == null)
844: _parser.parseNext();
845:
846: // Handle blocking end points
847: else if (_endp.isBlocking()) {
848: try {
849: long filled = _parser.parseNext();
850:
851: // parse until some progress is made (or IOException thrown for timeout)
852: while (_content.length() == 0 && filled != 0
853: && !_parser.isState(HttpParser.STATE_END)) {
854: // Try to get more _parser._content
855: filled = _parser.parseNext();
856: }
857: } catch (IOException e) {
858: _endp.close();
859: throw e;
860: }
861: }
862: // Handle non-blocking end point
863: else {
864: _parser.parseNext();
865:
866: // parse until some progress is made (or IOException thrown for timeout)
867: while (_content.length() == 0
868: && !_parser.isState(HttpParser.STATE_END)) {
869: if (!_endp.blockReadable(_maxIdleTime)) {
870: _endp.close();
871: throw new EofException("timeout");
872: }
873:
874: // Try to get more _parser._content
875: _parser.parseNext();
876: }
877: }
878:
879: return _content.length() > 0;
880: }
881: }
882:
883: }
|