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 IOException ioex=null;
269
270 if (_endp != null && filled<=0)
271 {
272
273
274 if (_buffer == _body)
275 _buffer.compact();
276
277 if (_buffer.space() == 0)
278 throw new HttpException(HttpStatus.ORDINAL_413_Request_Entity_Too_Large, "FULL "+(_buffer==_body?"body":"head"));
279 try
280 {
281 if (total_filled<0)
282 total_filled=0;
283 filled=_endp.fill(_buffer);
284 if (filled>0)
285 total_filled+=filled;
286 }
287 catch(IOException e)
288 {
289 Log.debug(e);
290 ioex=e;
291 filled=-1;
292 }
293 }
294
295 if (filled < 0)
296 {
297 if ( _state == STATE_EOF_CONTENT)
298 {
299 if (_buffer.length()>0)
300 {
301
302 Buffer chunk=_buffer.get(_buffer.length());
303 _contentPosition += chunk.length();
304 _contentView.update(chunk);
305 _handler.content(chunk);
306 }
307 _state=STATE_END;
308 _handler.messageComplete(_contentPosition);
309 return total_filled;
310 }
311 reset(true);
312 throw new EofException(ioex);
313 }
314 length=_buffer.length();
315 }
316
317
318
319 byte ch;
320 byte[] array=_buffer.array();
321
322 while (_state<STATE_END && length-->0)
323 {
324 ch=_buffer.get();
325
326 if (_eol == HttpTokens.CARRIAGE_RETURN && ch == HttpTokens.LINE_FEED)
327 {
328 _eol=HttpTokens.LINE_FEED;
329 continue;
330 }
331 _eol=0;
332 switch (_state)
333 {
334 case STATE_START:
335 _contentLength=HttpTokens.UNKNOWN_CONTENT;
336 _cached=null;
337 if (ch > HttpTokens.SPACE || ch<0)
338 {
339 _buffer.mark();
340 _state=STATE_FIELD0;
341 }
342 break;
343
344 case STATE_FIELD0:
345 if (ch == HttpTokens.SPACE)
346 {
347 _tok0.update(_buffer.markIndex(), _buffer.getIndex() - 1);
348 _state=STATE_SPACE1;
349 continue;
350 }
351 else if (ch < HttpTokens.SPACE && ch>=0)
352 {
353 throw new HttpException(HttpServletResponse.SC_BAD_REQUEST);
354 }
355 break;
356
357 case STATE_SPACE1:
358 if (ch > HttpTokens.SPACE || ch<0)
359 {
360 _buffer.mark();
361 _state=STATE_FIELD1;
362 }
363 else if (ch < HttpTokens.SPACE)
364 {
365 throw new HttpException(HttpServletResponse.SC_BAD_REQUEST);
366 }
367 break;
368
369 case STATE_FIELD1:
370 if (ch == HttpTokens.SPACE)
371 {
372 _tok1.update(_buffer.markIndex(), _buffer.getIndex() - 1);
373 _state=STATE_SPACE2;
374 continue;
375 }
376 else if (ch < HttpTokens.SPACE && ch>=0)
377 {
378
379 _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _buffer
380 .sliceFromMark(), null);
381 _state=STATE_END;
382 _handler.headerComplete();
383 _handler.messageComplete(_contentPosition);
384 return total_filled;
385 }
386 break;
387
388 case STATE_SPACE2:
389 if (ch > HttpTokens.SPACE || ch<0)
390 {
391 _buffer.mark();
392 _state=STATE_FIELD2;
393 }
394 else if (ch < HttpTokens.SPACE)
395 {
396
397 _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _tok1, null);
398 _state=STATE_END;
399 _handler.headerComplete();
400 _handler.messageComplete(_contentPosition);
401 return total_filled;
402 }
403 break;
404
405 case STATE_FIELD2:
406 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
407 {
408 byte digit=_tok1.peek(_tok1.getIndex());
409 if (digit>='1'&&digit<='5')
410 {
411 _responseStatus = BufferUtil.toInt(_tok1);
412 _handler.startResponse(HttpVersions.CACHE.lookup(_tok0), _responseStatus,_buffer.sliceFromMark());
413 }
414 else
415 _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _tok1,HttpVersions.CACHE.lookup(_buffer.sliceFromMark()));
416 _eol=ch;
417 _state=STATE_HEADER;
418 _tok0.setPutIndex(_tok0.getIndex());
419 _tok1.setPutIndex(_tok1.getIndex());
420 _multiLineValue=null;
421 return total_filled;
422 }
423 break;
424
425 case STATE_HEADER:
426 if (ch == HttpTokens.COLON || ch == HttpTokens.SPACE || ch == HttpTokens.TAB)
427 {
428
429 _length=-1;
430 _state=STATE_HEADER_VALUE;
431 }
432 else
433 {
434
435 if (_cached!=null || _tok0.length() > 0 || _tok1.length() > 0 || _multiLineValue != null)
436 {
437
438 Buffer header=_cached!=null?_cached:HttpHeaders.CACHE.lookup(_tok0);
439 _cached=null;
440 Buffer value=_multiLineValue == null ? (Buffer) _tok1 : (Buffer) new ByteArrayBuffer(_multiLineValue);
441
442 int ho=HttpHeaders.CACHE.getOrdinal(header);
443 if (ho >= 0)
444 {
445 int vo=-1;
446
447 switch (ho)
448 {
449 case HttpHeaders.CONTENT_LENGTH_ORDINAL:
450 if (_contentLength != HttpTokens.CHUNKED_CONTENT)
451 {
452 _contentLength=BufferUtil.toLong(value);
453 if (_contentLength <= 0)
454 _contentLength=HttpTokens.NO_CONTENT;
455 }
456 break;
457
458 case HttpHeaders.TRANSFER_ENCODING_ORDINAL:
459 value=HttpHeaderValues.CACHE.lookup(value);
460 vo=HttpHeaderValues.CACHE.getOrdinal(value);
461 if (HttpHeaderValues.CHUNKED_ORDINAL == vo)
462 _contentLength=HttpTokens.CHUNKED_CONTENT;
463 else
464 {
465 String c=value.toString();
466 if (c.endsWith(HttpHeaderValues.CHUNKED))
467 _contentLength=HttpTokens.CHUNKED_CONTENT;
468
469 else if (c.indexOf(HttpHeaderValues.CHUNKED) >= 0)
470 throw new HttpException(400,null);
471 }
472 break;
473 }
474 }
475
476 _handler.parsedHeader(header, value);
477 _tok0.setPutIndex(_tok0.getIndex());
478 _tok1.setPutIndex(_tok1.getIndex());
479 _multiLineValue=null;
480 }
481
482
483
484 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
485 {
486
487
488
489 if (_contentLength == HttpTokens.UNKNOWN_CONTENT)
490 {
491 if (_responseStatus == 0
492 || _responseStatus == 304
493 || _responseStatus == 204
494 || _responseStatus < 200)
495 _contentLength=HttpTokens.NO_CONTENT;
496 else
497 _contentLength=HttpTokens.EOF_CONTENT;
498 }
499
500 _contentPosition=0;
501 _eol=ch;
502
503
504 switch (_contentLength > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) _contentLength)
505 {
506 case HttpTokens.EOF_CONTENT:
507 _state=STATE_EOF_CONTENT;
508 if(_body==null && _buffers!=null)
509 _body=_buffers.getBuffer(_contentBufferSize);
510
511 _handler.headerComplete();
512 break;
513
514 case HttpTokens.CHUNKED_CONTENT:
515 _state=STATE_CHUNKED_CONTENT;
516 if (_body==null && _buffers!=null)
517 _body=_buffers.getBuffer(_contentBufferSize);
518 _handler.headerComplete();
519 break;
520
521 case HttpTokens.NO_CONTENT:
522 _state=STATE_END;
523 _handler.headerComplete();
524 _handler.messageComplete(_contentPosition);
525 break;
526
527 default:
528 _state=STATE_CONTENT;
529 if(_forceContentBuffer ||
530 (_buffers!=null && _body==null && _buffer==_header && _contentLength>=(_header.capacity()-_header.getIndex())))
531 _body=_buffers.getBuffer(_contentBufferSize);
532 _handler.headerComplete();
533 break;
534 }
535 return total_filled;
536 }
537 else
538 {
539
540 _length=1;
541 _buffer.mark();
542 _state=STATE_HEADER_NAME;
543
544
545 if (array!=null)
546 {
547 _cached=HttpHeaders.CACHE.getBest(array, _buffer.markIndex(), length+1);
548
549 if (_cached!=null)
550 {
551 _length=_cached.length();
552 _buffer.setGetIndex(_buffer.markIndex()+_length);
553 length=_buffer.length();
554 }
555 }
556 }
557 }
558 break;
559
560 case STATE_HEADER_NAME:
561 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
562 {
563 if (_length > 0)
564 _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length);
565 _eol=ch;
566 _state=STATE_HEADER;
567 }
568 else if (ch == HttpTokens.COLON)
569 {
570 if (_length > 0 && _cached==null)
571 _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length);
572 _length=-1;
573 _state=STATE_HEADER_VALUE;
574 }
575 else if (ch != HttpTokens.SPACE && ch != HttpTokens.TAB)
576 {
577
578 _cached=null;
579 if (_length == -1) _buffer.mark();
580 _length=_buffer.getIndex() - _buffer.markIndex();
581 }
582 break;
583
584 case STATE_HEADER_VALUE:
585 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
586 {
587 if (_length > 0)
588 {
589 if (_tok1.length() == 0)
590 _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length);
591 else
592 {
593
594 if (_multiLineValue == null) _multiLineValue=_tok1.toString();
595 _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length);
596 _multiLineValue += " " + _tok1.toString();
597 }
598 }
599 _eol=ch;
600 _state=STATE_HEADER;
601 }
602 else if (ch != HttpTokens.SPACE && ch != HttpTokens.TAB)
603 {
604 if (_length == -1) _buffer.mark();
605 _length=_buffer.getIndex() - _buffer.markIndex();
606 }
607 break;
608 }
609 }
610
611
612
613
614 length=_buffer.length();
615 if (_input!=null)
616 _input._contentView=_contentView;
617 Buffer chunk;
618 while (_state > STATE_END && length > 0)
619 {
620 if (_eol == HttpTokens.CARRIAGE_RETURN && _buffer.peek() == HttpTokens.LINE_FEED)
621 {
622 _eol=_buffer.get();
623 length=_buffer.length();
624 continue;
625 }
626 _eol=0;
627 switch (_state)
628 {
629 case STATE_EOF_CONTENT:
630 chunk=_buffer.get(_buffer.length());
631 _contentPosition += chunk.length();
632 _contentView.update(chunk);
633 _handler.content(chunk);
634
635 return total_filled;
636
637 case STATE_CONTENT:
638 {
639 long remaining=_contentLength - _contentPosition;
640 if (remaining == 0)
641 {
642 _state=STATE_END;
643 _handler.messageComplete(_contentPosition);
644 return total_filled;
645 }
646
647 if (length > remaining)
648 {
649
650
651 length=(int)remaining;
652 }
653
654 chunk=_buffer.get(length);
655 _contentPosition += chunk.length();
656 _contentView.update(chunk);
657 _handler.content(chunk);
658
659 if(_contentPosition == _contentLength)
660 {
661 _state=STATE_END;
662 _handler.messageComplete(_contentPosition);
663 }
664
665 return total_filled;
666 }
667
668 case STATE_CHUNKED_CONTENT:
669 {
670 ch=_buffer.peek();
671 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
672 _eol=_buffer.get();
673 else if (ch <= HttpTokens.SPACE)
674 _buffer.get();
675 else
676 {
677 _chunkLength=0;
678 _chunkPosition=0;
679 _state=STATE_CHUNK_SIZE;
680 }
681 break;
682 }
683
684 case STATE_CHUNK_SIZE:
685 {
686 ch=_buffer.get();
687 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
688 {
689 _eol=ch;
690 if (_chunkLength == 0)
691 {
692 _state=STATE_END;
693 _handler.messageComplete(_contentPosition);
694 return total_filled;
695 }
696 else
697 _state=STATE_CHUNK;
698 }
699 else if (ch <= HttpTokens.SPACE || ch == HttpTokens.SEMI_COLON)
700 _state=STATE_CHUNK_PARAMS;
701 else if (ch >= '0' && ch <= '9')
702 _chunkLength=_chunkLength * 16 + (ch - '0');
703 else if (ch >= 'a' && ch <= 'f')
704 _chunkLength=_chunkLength * 16 + (10 + ch - 'a');
705 else if (ch >= 'A' && ch <= 'F')
706 _chunkLength=_chunkLength * 16 + (10 + ch - 'A');
707 else
708 throw new IOException("bad chunk char: " + ch);
709 break;
710 }
711
712 case STATE_CHUNK_PARAMS:
713 {
714 ch=_buffer.get();
715 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
716 {
717 _eol=ch;
718 if (_chunkLength == 0)
719 {
720 _state=STATE_END;
721 _handler.messageComplete(_contentPosition);
722 return total_filled;
723 }
724 else
725 _state=STATE_CHUNK;
726 }
727 break;
728 }
729
730 case STATE_CHUNK:
731 {
732 int remaining=_chunkLength - _chunkPosition;
733 if (remaining == 0)
734 {
735 _state=STATE_CHUNKED_CONTENT;
736 break;
737 }
738 else if (length > remaining)
739 length=remaining;
740 chunk=_buffer.get(length);
741 _contentPosition += chunk.length();
742 _chunkPosition += chunk.length();
743 _contentView.update(chunk);
744 _handler.content(chunk);
745
746 return total_filled;
747 }
748 }
749
750 length=_buffer.length();
751 }
752 return total_filled;
753 }
754
755
756
757
758
759 public long fill() throws IOException
760 {
761 if (_buffer==null)
762 {
763 _buffer=_header=getHeaderBuffer();
764 _tok0=new View.CaseInsensitive(_buffer);
765 _tok1=new View.CaseInsensitive(_buffer);
766 }
767 if (_body!=null && _buffer!=_body)
768 _buffer=_body;
769 if (_buffer == _body)
770 _buffer.compact();
771
772 int space=_buffer.space();
773
774
775 if (space == 0)
776 throw new HttpException(HttpStatus.ORDINAL_413_Request_Entity_Too_Large, "FULL "+(_buffer==_body?"body":"head"));
777 else
778 {
779 int filled=-1;
780
781 if (_endp != null )
782 {
783 try
784 {
785 filled=_endp.fill(_buffer);
786 }
787 catch(IOException e)
788 {
789 Log.debug(e);
790 reset(true);
791 throw (e instanceof EofException) ? e:new EofException(e);
792 }
793 }
794
795 return filled;
796 }
797 }
798
799
800
801
802
803 public void skipCRLF()
804 {
805
806 while (_header!=null && _header.length()>0)
807 {
808 byte ch = _header.peek();
809 if (ch==HttpTokens.CARRIAGE_RETURN || ch==HttpTokens.LINE_FEED)
810 {
811 _eol=ch;
812 _header.skip(1);
813 }
814 else
815 break;
816 }
817
818 while (_body!=null && _body.length()>0)
819 {
820 byte ch = _body.peek();
821 if (ch==HttpTokens.CARRIAGE_RETURN || ch==HttpTokens.LINE_FEED)
822 {
823 _eol=ch;
824 _body.skip(1);
825 }
826 else
827 break;
828 }
829
830 }
831
832 public void reset(boolean returnBuffers)
833 {
834 synchronized (this)
835 {
836 if (_input!=null && _contentView.length()>0)
837 _input._contentView=_contentView.duplicate(Buffer.READWRITE);
838
839 _state=STATE_START;
840 _contentLength=HttpTokens.UNKNOWN_CONTENT;
841 _contentPosition=0;
842 _length=0;
843 _responseStatus=0;
844
845 if (_buffer!=null && _buffer.length()>0 && _eol == HttpTokens.CARRIAGE_RETURN && _buffer.peek() == HttpTokens.LINE_FEED)
846 {
847 _buffer.skip(1);
848 _eol=HttpTokens.LINE_FEED;
849 }
850
851 if (_body!=null)
852 {
853 if (_body.hasContent())
854 {
855 _header.setMarkIndex(-1);
856 _header.compact();
857
858 _body.skip(_header.put(_body));
859
860 }
861
862 if (_body.length()==0)
863 {
864 if (_buffers!=null && returnBuffers)
865 _buffers.returnBuffer(_body);
866 _body=null;
867 }
868 else
869 {
870 _body.setMarkIndex(-1);
871 _body.compact();
872 }
873 }
874
875
876 if (_header!=null)
877 {
878 _header.setMarkIndex(-1);
879 if (!_header.hasContent() && _buffers!=null && returnBuffers)
880 {
881 _buffers.returnBuffer(_header);
882 _header=null;
883 _buffer=null;
884 }
885 else
886 {
887 _header.compact();
888 _tok0.update(_header);
889 _tok0.update(0,0);
890 _tok1.update(_header);
891 _tok1.update(0,0);
892 }
893 }
894
895 _buffer=_header;
896 }
897 }
898
899
900 public void setState(int state)
901 {
902 this._state=state;
903 _contentLength=HttpTokens.UNKNOWN_CONTENT;
904 }
905
906
907 public String toString(Buffer buf)
908 {
909 return "state=" + _state + " length=" + _length + " buf=" + buf.hashCode();
910 }
911
912
913 public String toString()
914 {
915 return "state=" + _state + " length=" + _length + " len=" + _contentLength;
916 }
917
918
919 public Buffer getHeaderBuffer()
920 {
921 if (_header == null)
922 {
923 _header=_buffers.getBuffer(_headerBufferSize);
924 }
925 return _header;
926 }
927
928
929 public Buffer getBodyBuffer()
930 {
931 return _body;
932 }
933
934
935
936
937
938 public void setForceContentBuffer(boolean force)
939 {
940 _forceContentBuffer=force;
941 }
942
943
944
945
946 public static abstract class EventHandler
947 {
948 public abstract void content(Buffer ref) throws IOException;
949
950 public void headerComplete() throws IOException
951 {
952 }
953
954 public void messageComplete(long contextLength) throws IOException
955 {
956 }
957
958
959
960
961 public void parsedHeader(Buffer name, Buffer value) throws IOException
962 {
963 }
964
965
966
967
968 public abstract void startRequest(Buffer method, Buffer url, Buffer version)
969 throws IOException;
970
971
972
973
974 public abstract void startResponse(Buffer version, int status, Buffer reason)
975 throws IOException;
976 }
977
978
979
980
981
982
983 public static class Input extends ServletInputStream
984 {
985 protected HttpParser _parser;
986 protected EndPoint _endp;
987 protected long _maxIdleTime;
988 protected Buffer _contentView;
989
990
991 public Input(HttpParser parser, long maxIdleTime)
992 {
993 _parser=parser;
994 _endp=parser._endp;
995 _maxIdleTime=maxIdleTime;
996 _contentView=_parser._contentView;
997 _parser._input=this;
998 }
999
1000
1001
1002
1003
1004 public int read() throws IOException
1005 {
1006 int c=-1;
1007 if (blockForContent())
1008 c= 0xff & _contentView.get();
1009 return c;
1010 }
1011
1012
1013
1014
1015
1016 public int read(byte[] b, int off, int len) throws IOException
1017 {
1018 int l=-1;
1019 if (blockForContent())
1020 l= _contentView.get(b, off, len);
1021 return l;
1022 }
1023
1024
1025 private boolean blockForContent() throws IOException
1026 {
1027 if (_contentView.length()>0)
1028 return true;
1029 if (_parser.getState() <= HttpParser.STATE_END)
1030 return false;
1031
1032
1033 if (_endp==null)
1034 _parser.parseNext();
1035
1036
1037 else if (_endp.isBlocking())
1038 {
1039 try
1040 {
1041 _parser.parseNext();
1042
1043
1044 while(_contentView.length() == 0 && !_parser.isState(HttpParser.STATE_END))
1045 {
1046
1047 _parser.parseNext();
1048 }
1049 }
1050 catch(IOException e)
1051 {
1052 _endp.close();
1053 throw e;
1054 }
1055 }
1056 else
1057 {
1058 _parser.parseNext();
1059
1060
1061 while(_contentView.length() == 0 && !_parser.isState(HttpParser.STATE_END))
1062 {
1063 if (!_endp.blockReadable(_maxIdleTime))
1064 {
1065 _endp.close();
1066 throw new EofException("timeout");
1067 }
1068
1069
1070 _parser.parseNext();
1071 }
1072 }
1073
1074 return _contentView.length()>0;
1075 }
1076
1077
1078
1079
1080
1081 public int available() throws IOException
1082 {
1083 if (_contentView!=null && _contentView.length()>0)
1084 return _contentView.length();
1085 if (!_endp.isBlocking())
1086 _parser.parseNext();
1087
1088 return _contentView==null?0:_contentView.length();
1089 }
1090 }
1091
1092
1093
1094
1095 }