1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.mortbay.jetty;
16
17 import java.io.IOException;
18
19 import javax.servlet.ServletInputStream;
20 import javax.servlet.http.HttpServletResponse;
21
22 import org.mortbay.io.Buffer;
23 import org.mortbay.io.BufferUtil;
24 import org.mortbay.io.Buffers;
25 import org.mortbay.io.ByteArrayBuffer;
26 import org.mortbay.io.EndPoint;
27 import org.mortbay.io.View;
28 import org.mortbay.io.BufferCache.CachedBuffer;
29 import org.mortbay.log.Log;
30
31
32
33
34
35 public class HttpParser implements Parser
36 {
37
38 public static final int STATE_START=-11;
39 public static final int STATE_FIELD0=-10;
40 public static final int STATE_SPACE1=-9;
41 public static final int STATE_FIELD1=-8;
42 public static final int STATE_SPACE2=-7;
43 public static final int STATE_END0=-6;
44 public static final int STATE_END1=-5;
45 public static final int STATE_FIELD2=-4;
46 public static final int STATE_HEADER=-3;
47 public static final int STATE_HEADER_NAME=-2;
48 public static final int STATE_HEADER_VALUE=-1;
49 public static final int STATE_END=0;
50 public static final int STATE_EOF_CONTENT=1;
51 public static final int STATE_CONTENT=2;
52 public static final int STATE_CHUNKED_CONTENT=3;
53 public static final int STATE_CHUNK_SIZE=4;
54 public static final int STATE_CHUNK_PARAMS=5;
55 public static final int STATE_CHUNK=6;
56
57 private Buffers _buffers;
58 private EndPoint _endp;
59 private Buffer _header;
60 private Buffer _body;
61 private Buffer _buffer;
62 private View _contentView=new View();
63 private int _headerBufferSize;
64
65 private int _contentBufferSize;
66 private EventHandler _handler;
67 private CachedBuffer _cached;
68 private View.CaseInsensitive _tok0;
69 private View.CaseInsensitive _tok1;
70 private String _multiLineValue;
71 private int _responseStatus;
72 private boolean _forceContentBuffer;
73 private Input _input;
74
75
76 protected int _state=STATE_START;
77 protected byte _eol;
78 protected int _length;
79 protected long _contentLength;
80 protected long _contentPosition;
81 protected int _chunkLength;
82 protected int _chunkPosition;
83
84
85
86
87
88 public HttpParser(Buffer buffer, EventHandler handler)
89 {
90 this._header=buffer;
91 this._buffer=buffer;
92 this._handler=handler;
93
94 if (buffer != null)
95 {
96 _tok0=new View.CaseInsensitive(buffer);
97 _tok1=new View.CaseInsensitive(buffer);
98 _tok0.setPutIndex(_tok0.getIndex());
99 _tok1.setPutIndex(_tok1.getIndex());
100 }
101 }
102
103
104
105
106
107
108
109 public HttpParser(Buffers buffers, EndPoint endp, EventHandler handler, int headerBufferSize, int contentBufferSize)
110 {
111 _buffers=buffers;
112 _endp=endp;
113 _handler=handler;
114 _headerBufferSize=headerBufferSize;
115 _contentBufferSize=contentBufferSize;
116 }
117
118
119 public long getContentLength()
120 {
121 return _contentLength;
122 }
123
124
125 public int getState()
126 {
127 return _state;
128 }
129
130
131 public boolean inContentState()
132 {
133 return _state > 0;
134 }
135
136
137 public boolean inHeaderState()
138 {
139 return _state < 0;
140 }
141
142
143 public boolean isChunking()
144 {
145 return _contentLength==HttpTokens.CHUNKED_CONTENT;
146 }
147
148
149 public boolean isIdle()
150 {
151 return isState(STATE_START);
152 }
153
154
155 public boolean isComplete()
156 {
157 return isState(STATE_END);
158 }
159
160
161 public boolean isMoreInBuffer()
162 throws IOException
163 {
164 if ( _header!=null && _header.hasContent() ||
165 _body!=null && _body.hasContent())
166 return true;
167
168 return false;
169 }
170
171
172 public boolean isState(int state)
173 {
174 return _state == state;
175 }
176
177
178
179
180
181
182
183 public void parse() throws IOException
184 {
185 if (_state==STATE_END)
186 reset(false);
187 if (_state!=STATE_START)
188 throw new IllegalStateException("!START");
189
190
191 while (_state != STATE_END)
192 parseNext();
193 }
194
195
196
197
198
199
200
201
202
203 public long parseAvailable() throws IOException
204 {
205 long len = parseNext();
206 long total=len>0?len:0;
207
208
209 while (!isComplete() && _buffer!=null && _buffer.length()>0)
210 {
211 len = parseNext();
212 if (len>0)
213 total+=len;
214 }
215 return total;
216 }
217
218
219
220
221
222
223
224
225 public long parseNext() throws IOException
226 {
227 long total_filled=-1;
228
229 if (_state == STATE_END)
230 return -1;
231
232 if (_buffer==null)
233 {
234 if (_header == null)
235 {
236 _header=_buffers.getBuffer(_headerBufferSize);
237 }
238 _buffer=_header;
239 _tok0=new View.CaseInsensitive(_header);
240 _tok1=new View.CaseInsensitive(_header);
241 _tok0.setPutIndex(_tok0.getIndex());
242 _tok1.setPutIndex(_tok1.getIndex());
243 }
244
245
246 if (_state == STATE_CONTENT && _contentPosition == _contentLength)
247 {
248 _state=STATE_END;
249 _handler.messageComplete(_contentPosition);
250 return total_filled;
251 }
252
253 int length=_buffer.length();
254
255
256 if (length == 0)
257 {
258 int filled=-1;
259 if (_body!=null && _buffer!=_body)
260 {
261 _buffer=_body;
262 filled=_buffer.length();
263 }
264
265 if (_buffer.markIndex() == 0 && _buffer.putIndex() == _buffer.capacity())
266 throw new HttpException(HttpStatus.ORDINAL_413_Request_Entity_Too_Large, "FULL");
267
268 if (_endp != null && filled<=0)
269 {
270
271
272 if (_buffer == _body)
273 _buffer.compact();
274
275 if (_buffer.space() == 0)
276 throw new HttpException(HttpStatus.ORDINAL_413_Request_Entity_Too_Large, "FULL "+(_buffer==_body?"body":"head"));
277 try
278 {
279 if (total_filled<0)
280 total_filled=0;
281 filled=_endp.fill(_buffer);
282 if (filled>0)
283 total_filled+=filled;
284 }
285 catch(IOException e)
286 {
287 Log.debug(e);
288 reset(true);
289 throw (e instanceof EofException) ? e:new EofException(e);
290 }
291 }
292
293 if (filled < 0)
294 {
295 if ( _state == STATE_EOF_CONTENT)
296 {
297 _state=STATE_END;
298 _handler.messageComplete(_contentPosition);
299 return total_filled;
300 }
301 reset(true);
302 throw new EofException();
303 }
304 length=_buffer.length();
305 }
306
307
308
309 byte ch;
310 byte[] array=_buffer.array();
311
312 while (_state<STATE_END && length-->0)
313 {
314 ch=_buffer.get();
315
316 if (_eol == HttpTokens.CARRIAGE_RETURN && ch == HttpTokens.LINE_FEED)
317 {
318 _eol=HttpTokens.LINE_FEED;
319 continue;
320 }
321 _eol=0;
322 switch (_state)
323 {
324 case STATE_START:
325 _contentLength=HttpTokens.UNKNOWN_CONTENT;
326 _cached=null;
327 if (ch > HttpTokens.SPACE || ch<0)
328 {
329 _buffer.mark();
330 _state=STATE_FIELD0;
331 }
332 break;
333
334 case STATE_FIELD0:
335 if (ch == HttpTokens.SPACE)
336 {
337 _tok0.update(_buffer.markIndex(), _buffer.getIndex() - 1);
338 _state=STATE_SPACE1;
339 continue;
340 }
341 else if (ch < HttpTokens.SPACE && ch>=0)
342 {
343 throw new HttpException(HttpServletResponse.SC_BAD_REQUEST);
344 }
345 break;
346
347 case STATE_SPACE1:
348 if (ch > HttpTokens.SPACE || ch<0)
349 {
350 _buffer.mark();
351 _state=STATE_FIELD1;
352 }
353 else if (ch < HttpTokens.SPACE)
354 {
355 throw new HttpException(HttpServletResponse.SC_BAD_REQUEST);
356 }
357 break;
358
359 case STATE_FIELD1:
360 if (ch == HttpTokens.SPACE)
361 {
362 _tok1.update(_buffer.markIndex(), _buffer.getIndex() - 1);
363 _state=STATE_SPACE2;
364 continue;
365 }
366 else if (ch < HttpTokens.SPACE && ch>=0)
367 {
368
369 _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _buffer
370 .sliceFromMark(), null);
371 _state=STATE_END;
372 _handler.headerComplete();
373 _handler.messageComplete(_contentPosition);
374 return total_filled;
375 }
376 break;
377
378 case STATE_SPACE2:
379 if (ch > HttpTokens.SPACE || ch<0)
380 {
381 _buffer.mark();
382 _state=STATE_FIELD2;
383 }
384 else if (ch < HttpTokens.SPACE)
385 {
386
387 _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _tok1, null);
388 _state=STATE_END;
389 _handler.headerComplete();
390 _handler.messageComplete(_contentPosition);
391 return total_filled;
392 }
393 break;
394
395 case STATE_FIELD2:
396 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
397 {
398 byte digit=_tok1.peek(_tok1.getIndex());
399 if (digit>='1'&&digit<='5')
400 {
401 _responseStatus = BufferUtil.toInt(_tok1);
402 _handler.startResponse(HttpVersions.CACHE.lookup(_tok0), _responseStatus,_buffer.sliceFromMark());
403 }
404 else
405 _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _tok1,HttpVersions.CACHE.lookup(_buffer.sliceFromMark()));
406 _eol=ch;
407 _state=STATE_HEADER;
408 _tok0.setPutIndex(_tok0.getIndex());
409 _tok1.setPutIndex(_tok1.getIndex());
410 _multiLineValue=null;
411 return total_filled;
412 }
413 break;
414
415 case STATE_HEADER:
416 if (ch == HttpTokens.COLON || ch == HttpTokens.SPACE || ch == HttpTokens.TAB)
417 {
418
419 _length=-1;
420 _state=STATE_HEADER_VALUE;
421 }
422 else
423 {
424
425 if (_cached!=null || _tok0.length() > 0 || _tok1.length() > 0 || _multiLineValue != null)
426 {
427
428 Buffer header=_cached!=null?_cached:HttpHeaders.CACHE.lookup(_tok0);
429 _cached=null;
430 Buffer value=_multiLineValue == null ? (Buffer) _tok1 : (Buffer) new ByteArrayBuffer(_multiLineValue);
431
432 int ho=HttpHeaders.CACHE.getOrdinal(header);
433 if (ho >= 0)
434 {
435 int vo=-1;
436
437 switch (ho)
438 {
439 case HttpHeaders.CONTENT_LENGTH_ORDINAL:
440 if (_contentLength != HttpTokens.CHUNKED_CONTENT)
441 {
442 _contentLength=BufferUtil.toLong(value);
443 if (_contentLength <= 0)
444 _contentLength=HttpTokens.NO_CONTENT;
445 }
446 break;
447
448 case HttpHeaders.TRANSFER_ENCODING_ORDINAL:
449 value=HttpHeaderValues.CACHE.lookup(value);
450 vo=HttpHeaderValues.CACHE.getOrdinal(value);
451 if (HttpHeaderValues.CHUNKED_ORDINAL == vo)
452 _contentLength=HttpTokens.CHUNKED_CONTENT;
453 else
454 {
455 String c=value.toString();
456 if (c.endsWith(HttpHeaderValues.CHUNKED))
457 _contentLength=HttpTokens.CHUNKED_CONTENT;
458
459 else if (c.indexOf(HttpHeaderValues.CHUNKED) >= 0)
460 throw new HttpException(400,null);
461 }
462 break;
463 }
464 }
465
466 _handler.parsedHeader(header, value);
467 _tok0.setPutIndex(_tok0.getIndex());
468 _tok1.setPutIndex(_tok1.getIndex());
469 _multiLineValue=null;
470 }
471
472
473
474 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
475 {
476
477
478
479 if (_contentLength == HttpTokens.UNKNOWN_CONTENT)
480 {
481 if (_responseStatus == 0
482 || _responseStatus == 304
483 || _responseStatus == 204
484 || _responseStatus < 200)
485 _contentLength=HttpTokens.NO_CONTENT;
486 else
487 _contentLength=HttpTokens.EOF_CONTENT;
488 }
489
490 _contentPosition=0;
491 _eol=ch;
492
493
494 switch (_contentLength > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) _contentLength)
495 {
496 case HttpTokens.EOF_CONTENT:
497 _state=STATE_EOF_CONTENT;
498 if(_body==null && _buffers!=null)
499 _body=_buffers.getBuffer(_contentBufferSize);
500
501 _handler.headerComplete();
502 break;
503
504 case HttpTokens.CHUNKED_CONTENT:
505 _state=STATE_CHUNKED_CONTENT;
506 if (_body==null && _buffers!=null)
507 _body=_buffers.getBuffer(_contentBufferSize);
508 _handler.headerComplete();
509 break;
510
511 case HttpTokens.NO_CONTENT:
512 _state=STATE_END;
513 _handler.headerComplete();
514 _handler.messageComplete(_contentPosition);
515 break;
516
517 default:
518 _state=STATE_CONTENT;
519 if(_forceContentBuffer ||
520 (_buffers!=null && _body==null && _buffer==_header && _contentLength>=(_header.capacity()-_header.getIndex())))
521 _body=_buffers.getBuffer(_contentBufferSize);
522 _handler.headerComplete();
523 break;
524 }
525 return total_filled;
526 }
527 else
528 {
529
530 _length=1;
531 _buffer.mark();
532 _state=STATE_HEADER_NAME;
533
534
535 if (array!=null)
536 {
537 _cached=HttpHeaders.CACHE.getBest(array, _buffer.markIndex(), length+1);
538
539 if (_cached!=null)
540 {
541 _length=_cached.length();
542 _buffer.setGetIndex(_buffer.markIndex()+_length);
543 length=_buffer.length();
544 }
545 }
546 }
547 }
548 break;
549
550 case STATE_HEADER_NAME:
551 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
552 {
553 if (_length > 0)
554 _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length);
555 _eol=ch;
556 _state=STATE_HEADER;
557 }
558 else if (ch == HttpTokens.COLON)
559 {
560 if (_length > 0 && _cached==null)
561 _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length);
562 _length=-1;
563 _state=STATE_HEADER_VALUE;
564 }
565 else if (ch != HttpTokens.SPACE && ch != HttpTokens.TAB)
566 {
567
568 _cached=null;
569 if (_length == -1) _buffer.mark();
570 _length=_buffer.getIndex() - _buffer.markIndex();
571 }
572 break;
573
574 case STATE_HEADER_VALUE:
575 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
576 {
577 if (_length > 0)
578 {
579 if (_tok1.length() == 0)
580 _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length);
581 else
582 {
583
584 if (_multiLineValue == null) _multiLineValue=_tok1.toString();
585 _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length);
586 _multiLineValue += " " + _tok1.toString();
587 }
588 }
589 _eol=ch;
590 _state=STATE_HEADER;
591 }
592 else if (ch != HttpTokens.SPACE && ch != HttpTokens.TAB)
593 {
594 if (_length == -1) _buffer.mark();
595 _length=_buffer.getIndex() - _buffer.markIndex();
596 }
597 break;
598 }
599 }
600
601
602
603
604 length=_buffer.length();
605 if (_input!=null)
606 _input._contentView=_contentView;
607 Buffer chunk;
608 while (_state > STATE_END && length > 0)
609 {
610 if (_eol == HttpTokens.CARRIAGE_RETURN && _buffer.peek() == HttpTokens.LINE_FEED)
611 {
612 _eol=_buffer.get();
613 length=_buffer.length();
614 continue;
615 }
616 _eol=0;
617 switch (_state)
618 {
619 case STATE_EOF_CONTENT:
620 chunk=_buffer.get(_buffer.length());
621 _contentPosition += chunk.length();
622 _contentView.update(chunk);
623 _handler.content(chunk);
624
625 return total_filled;
626
627 case STATE_CONTENT:
628 {
629 long remaining=_contentLength - _contentPosition;
630 if (remaining == 0)
631 {
632 _state=STATE_END;
633 _handler.messageComplete(_contentPosition);
634 return total_filled;
635 }
636
637 if (length > remaining)
638 {
639
640
641 length=(int)remaining;
642 }
643
644 chunk=_buffer.get(length);
645 _contentPosition += chunk.length();
646 _contentView.update(chunk);
647 _handler.content(chunk);
648
649
650 return total_filled;
651 }
652
653 case STATE_CHUNKED_CONTENT:
654 {
655 ch=_buffer.peek();
656 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
657 _eol=_buffer.get();
658 else if (ch <= HttpTokens.SPACE)
659 _buffer.get();
660 else
661 {
662 _chunkLength=0;
663 _chunkPosition=0;
664 _state=STATE_CHUNK_SIZE;
665 }
666 break;
667 }
668
669 case STATE_CHUNK_SIZE:
670 {
671 ch=_buffer.get();
672 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
673 {
674 _eol=ch;
675 if (_chunkLength == 0)
676 {
677 _state=STATE_END;
678 _handler.messageComplete(_contentPosition);
679 return total_filled;
680 }
681 else
682 _state=STATE_CHUNK;
683 }
684 else if (ch <= HttpTokens.SPACE || ch == HttpTokens.SEMI_COLON)
685 _state=STATE_CHUNK_PARAMS;
686 else if (ch >= '0' && ch <= '9')
687 _chunkLength=_chunkLength * 16 + (ch - '0');
688 else if (ch >= 'a' && ch <= 'f')
689 _chunkLength=_chunkLength * 16 + (10 + ch - 'a');
690 else if (ch >= 'A' && ch <= 'F')
691 _chunkLength=_chunkLength * 16 + (10 + ch - 'A');
692 else
693 throw new IOException("bad chunk char: " + ch);
694 break;
695 }
696
697 case STATE_CHUNK_PARAMS:
698 {
699 ch=_buffer.get();
700 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
701 {
702 _eol=ch;
703 if (_chunkLength == 0)
704 {
705 _state=STATE_END;
706 _handler.messageComplete(_contentPosition);
707 return total_filled;
708 }
709 else
710 _state=STATE_CHUNK;
711 }
712 break;
713 }
714
715 case STATE_CHUNK:
716 {
717 int remaining=_chunkLength - _chunkPosition;
718 if (remaining == 0)
719 {
720 _state=STATE_CHUNKED_CONTENT;
721 break;
722 }
723 else if (length > remaining)
724 length=remaining;
725 chunk=_buffer.get(length);
726 _contentPosition += chunk.length();
727 _chunkPosition += chunk.length();
728 _contentView.update(chunk);
729 _handler.content(chunk);
730
731 return total_filled;
732 }
733 }
734
735 length=_buffer.length();
736 }
737 return total_filled;
738 }
739
740
741
742
743
744 public long fill() throws IOException
745 {
746 if (_buffer==null)
747 {
748 _buffer=_header=getHeaderBuffer();
749 _tok0=new View.CaseInsensitive(_buffer);
750 _tok1=new View.CaseInsensitive(_buffer);
751 }
752 if (_body!=null && _buffer!=_body)
753 _buffer=_body;
754 if (_buffer == _body)
755 _buffer.compact();
756
757 int space=_buffer.space();
758
759
760 if (space == 0)
761 throw new HttpException(HttpStatus.ORDINAL_413_Request_Entity_Too_Large, "FULL "+(_buffer==_body?"body":"head"));
762 else
763 {
764 int filled=-1;
765
766 if (_endp != null )
767 {
768 try
769 {
770 filled=_endp.fill(_buffer);
771 }
772 catch(IOException e)
773 {
774 Log.debug(e);
775 reset(true);
776 throw (e instanceof EofException) ? e:new EofException(e);
777 }
778 }
779
780 return filled;
781 }
782 }
783
784
785
786
787
788 public void skipCRLF()
789 {
790
791 while (_header!=null && _header.length()>0)
792 {
793 byte ch = _header.peek();
794 if (ch==HttpTokens.CARRIAGE_RETURN || ch==HttpTokens.LINE_FEED)
795 {
796 _eol=ch;
797 _header.skip(1);
798 }
799 else
800 break;
801 }
802
803 while (_body!=null && _body.length()>0)
804 {
805 byte ch = _body.peek();
806 if (ch==HttpTokens.CARRIAGE_RETURN || ch==HttpTokens.LINE_FEED)
807 {
808 _eol=ch;
809 _body.skip(1);
810 }
811 else
812 break;
813 }
814
815 }
816
817 public void reset(boolean returnBuffers)
818 {
819 synchronized (this)
820 {
821 if (_input!=null && _contentView.length()>0)
822 _input._contentView=_contentView.duplicate(Buffer.READWRITE);
823
824 _state=STATE_START;
825 _contentLength=HttpTokens.UNKNOWN_CONTENT;
826 _contentPosition=0;
827 _length=0;
828 _responseStatus=0;
829
830 if (_buffer!=null && _buffer.length()>0 && _eol == HttpTokens.CARRIAGE_RETURN && _buffer.peek() == HttpTokens.LINE_FEED)
831 {
832 _buffer.skip(1);
833 _eol=HttpTokens.LINE_FEED;
834 }
835
836 if (_body!=null)
837 {
838 if (_body.hasContent())
839 {
840 _header.setMarkIndex(-1);
841 _header.compact();
842
843 _body.skip(_header.put(_body));
844
845 }
846
847 if (_body.length()==0)
848 {
849 if (_buffers!=null && returnBuffers)
850 _buffers.returnBuffer(_body);
851 _body=null;
852 }
853 else
854 {
855 _body.setMarkIndex(-1);
856 _body.compact();
857 }
858 }
859
860
861 if (_header!=null)
862 {
863 _header.setMarkIndex(-1);
864 if (!_header.hasContent() && _buffers!=null && returnBuffers)
865 {
866 _buffers.returnBuffer(_header);
867 _header=null;
868 _buffer=null;
869 }
870 else
871 {
872 _header.compact();
873 _tok0.update(_header);
874 _tok0.update(0,0);
875 _tok1.update(_header);
876 _tok1.update(0,0);
877 }
878 }
879
880 _buffer=_header;
881 }
882 }
883
884
885 public void setState(int state)
886 {
887 this._state=state;
888 _contentLength=HttpTokens.UNKNOWN_CONTENT;
889 }
890
891
892 public String toString(Buffer buf)
893 {
894 return "state=" + _state + " length=" + _length + " buf=" + buf.hashCode();
895 }
896
897
898 public Buffer getHeaderBuffer()
899 {
900 if (_header == null)
901 {
902 _header=_buffers.getBuffer(_headerBufferSize);
903 }
904 return _header;
905 }
906
907
908 public Buffer getBodyBuffer()
909 {
910 return _body;
911 }
912
913
914
915
916
917 public void setForceContentBuffer(boolean force)
918 {
919 _forceContentBuffer=force;
920 }
921
922
923
924
925 public static abstract class EventHandler
926 {
927 public abstract void content(Buffer ref) throws IOException;
928
929 public void headerComplete() throws IOException
930 {
931 }
932
933 public void messageComplete(long contextLength) throws IOException
934 {
935 }
936
937
938
939
940 public void parsedHeader(Buffer name, Buffer value) throws IOException
941 {
942 }
943
944
945
946
947 public abstract void startRequest(Buffer method, Buffer url, Buffer version)
948 throws IOException;
949
950
951
952
953 public abstract void startResponse(Buffer version, int status, Buffer reason)
954 throws IOException;
955 }
956
957
958
959
960
961
962 public static class Input extends ServletInputStream
963 {
964 protected HttpParser _parser;
965 protected EndPoint _endp;
966 protected long _maxIdleTime;
967 protected Buffer _contentView;
968
969
970 public Input(HttpParser parser, long maxIdleTime)
971 {
972 _parser=parser;
973 _endp=parser._endp;
974 _maxIdleTime=maxIdleTime;
975 _contentView=_parser._contentView;
976 _parser._input=this;
977 }
978
979
980
981
982
983 public int read() throws IOException
984 {
985 int c=-1;
986 if (blockForContent())
987 c= 0xff & _contentView.get();
988 return c;
989 }
990
991
992
993
994
995 public int read(byte[] b, int off, int len) throws IOException
996 {
997 int l=-1;
998 if (blockForContent())
999 l= _contentView.get(b, off, len);
1000 return l;
1001 }
1002
1003
1004 private boolean blockForContent() throws IOException
1005 {
1006 if (_contentView.length()>0)
1007 return true;
1008 if (_parser.getState() <= HttpParser.STATE_END)
1009 return false;
1010
1011
1012 if (_endp==null)
1013 _parser.parseNext();
1014
1015
1016 else if (_endp.isBlocking())
1017 {
1018 try
1019 {
1020 _parser.parseNext();
1021
1022
1023 while(_contentView.length() == 0 && !_parser.isState(HttpParser.STATE_END))
1024 {
1025
1026 _parser.parseNext();
1027 }
1028 }
1029 catch(IOException e)
1030 {
1031 _endp.close();
1032 throw e;
1033 }
1034 }
1035 else
1036 {
1037 _parser.parseNext();
1038
1039
1040 while(_contentView.length() == 0 && !_parser.isState(HttpParser.STATE_END))
1041 {
1042 if (!_endp.blockReadable(_maxIdleTime))
1043 {
1044 _endp.close();
1045 throw new EofException("timeout");
1046 }
1047
1048
1049 _parser.parseNext();
1050 }
1051 }
1052
1053 return _contentView.length()>0;
1054 }
1055
1056
1057
1058
1059
1060 public int available() throws IOException
1061 {
1062 if (_contentView!=null && _contentView.length()>0)
1063 return _contentView.length();
1064 if (!_endp.isBlocking())
1065 _parser.parseNext();
1066
1067 return _contentView==null?0:_contentView.length();
1068 }
1069 }
1070
1071
1072
1073
1074 }