1 // ========================================================================
2 // Copyright 2004-2006 Mort Bay Consulting Pty. Ltd.
3 // ------------------------------------------------------------------------
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
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 * @author gregw
34 */
35 public class HttpParser implements Parser
36 {
37 // States
38 public static final int STATE_START=-13;
39 public static final int STATE_FIELD0=-12;
40 public static final int STATE_SPACE1=-11;
41 public static final int STATE_FIELD1=-10;
42 public static final int STATE_SPACE2=-9;
43 public static final int STATE_END0=-8;
44 public static final int STATE_END1=-7;
45 public static final int STATE_FIELD2=-6;
46 public static final int STATE_HEADER=-5;
47 public static final int STATE_HEADER_NAME=-4;
48 public static final int STATE_HEADER_IN_NAME=-3;
49 public static final int STATE_HEADER_VALUE=-2;
50 public static final int STATE_HEADER_IN_VALUE=-1;
51 public static final int STATE_END=0;
52 public static final int STATE_EOF_CONTENT=1;
53 public static final int STATE_CONTENT=2;
54 public static final int STATE_CHUNKED_CONTENT=3;
55 public static final int STATE_CHUNK_SIZE=4;
56 public static final int STATE_CHUNK_PARAMS=5;
57 public static final int STATE_CHUNK=6;
58
59 private Buffers _buffers; // source of buffers
60 private EndPoint _endp;
61 private Buffer _header; // Buffer for header data (and small _content)
62 private Buffer _body; // Buffer for large content
63 private Buffer _buffer; // The current buffer in use (either _header or _content)
64 private View _contentView=new View(); // View of the content in the buffer for {@link Input}
65 private int _headerBufferSize;
66
67 private int _contentBufferSize;
68 private EventHandler _handler;
69 private CachedBuffer _cached;
70 private View.CaseInsensitive _tok0; // Saved token: header name, request method or response version
71 private View.CaseInsensitive _tok1; // Saved token: header value, request URI or response code
72 private String _multiLineValue;
73 private int _responseStatus; // If >0 then we are parsing a response
74 private boolean _forceContentBuffer;
75 private Input _input;
76
77 /* ------------------------------------------------------------------------------- */
78 protected int _state=STATE_START;
79 protected byte _eol;
80 protected int _length;
81 protected long _contentLength;
82 protected long _contentPosition;
83 protected int _chunkLength;
84 protected int _chunkPosition;
85
86 /* ------------------------------------------------------------------------------- */
87 /**
88 * Constructor.
89 */
90 public HttpParser(Buffer buffer, EventHandler handler)
91 {
92 this._header=buffer;
93 this._buffer=buffer;
94 this._handler=handler;
95
96 if (buffer != null)
97 {
98 _tok0=new View.CaseInsensitive(buffer);
99 _tok1=new View.CaseInsensitive(buffer);
100 _tok0.setPutIndex(_tok0.getIndex());
101 _tok1.setPutIndex(_tok1.getIndex());
102 }
103 }
104
105 /* ------------------------------------------------------------------------------- */
106 /**
107 * Constructor.
108 * @param headerBufferSize size in bytes of header buffer
109 * @param contentBufferSize size in bytes of content buffer
110 */
111 public HttpParser(Buffers buffers, EndPoint endp, EventHandler handler, int headerBufferSize, int contentBufferSize)
112 {
113 _buffers=buffers;
114 _endp=endp;
115 _handler=handler;
116 _headerBufferSize=headerBufferSize;
117 _contentBufferSize=contentBufferSize;
118 }
119
120 /* ------------------------------------------------------------------------------- */
121 public long getContentLength()
122 {
123 return _contentLength;
124 }
125
126 public long getContentRead()
127 {
128 return _contentPosition;
129 }
130
131 /* ------------------------------------------------------------------------------- */
132 public int getState()
133 {
134 return _state;
135 }
136
137 /* ------------------------------------------------------------------------------- */
138 public boolean inContentState()
139 {
140 return _state > 0;
141 }
142
143 /* ------------------------------------------------------------------------------- */
144 public boolean inHeaderState()
145 {
146 return _state < 0;
147 }
148
149 /* ------------------------------------------------------------------------------- */
150 public boolean isChunking()
151 {
152 return _contentLength==HttpTokens.CHUNKED_CONTENT;
153 }
154
155 /* ------------------------------------------------------------ */
156 public boolean isIdle()
157 {
158 return isState(STATE_START);
159 }
160
161 /* ------------------------------------------------------------ */
162 public boolean isComplete()
163 {
164 return isState(STATE_END);
165 }
166
167 /* ------------------------------------------------------------ */
168 public boolean isMoreInBuffer()
169 throws IOException
170 {
171 if ( _header!=null && _header.hasContent() ||
172 _body!=null && _body.hasContent())
173 return true;
174
175 return false;
176 }
177
178 /* ------------------------------------------------------------------------------- */
179 public boolean isState(int state)
180 {
181 return _state == state;
182 }
183
184 /* ------------------------------------------------------------------------------- */
185 /**
186 * Parse until {@link #STATE_END END} state.
187 * If the parser is already in the END state, then it is {@link #reset reset} and re-parsed.
188 * @throws IllegalStateException If the buffers have already been partially parsed.
189 */
190 public void parse() throws IOException
191 {
192 if (_state==STATE_END)
193 reset(false);
194 if (_state!=STATE_START)
195 throw new IllegalStateException("!START");
196
197 // continue parsing
198 while (_state != STATE_END)
199 parseNext();
200 }
201
202 /* ------------------------------------------------------------------------------- */
203 /**
204 * Parse until END state.
205 * This method will parse any remaining content in the current buffer. It does not care about the
206 * {@link #getState current state} of the parser.
207 * @see #parse
208 * @see #parseNext
209 */
210 public long parseAvailable() throws IOException
211 {
212 long len = parseNext();
213 long total=len>0?len:0;
214
215 // continue parsing
216 while (!isComplete() && _buffer!=null && _buffer.length()>0)
217 {
218 len = parseNext();
219 if (len>0)
220 total+=len;
221 }
222 return total;
223 }
224
225
226
227 /* ------------------------------------------------------------------------------- */
228 /**
229 * Parse until next Event.
230 * @returns number of bytes filled from endpoint or -1 if fill never called.
231 */
232 public long parseNext() throws IOException
233 {
234 long total_filled=-1;
235
236 if (_state == STATE_END)
237 return -1;
238
239 if (_buffer==null)
240 {
241 if (_header == null)
242 {
243 _header=_buffers.getBuffer(_headerBufferSize);
244 }
245 _buffer=_header;
246 _tok0=new View.CaseInsensitive(_header);
247 _tok1=new View.CaseInsensitive(_header);
248 _tok0.setPutIndex(_tok0.getIndex());
249 _tok1.setPutIndex(_tok1.getIndex());
250 }
251
252
253 if (_state == STATE_CONTENT && _contentPosition == _contentLength)
254 {
255 _state=STATE_END;
256 _handler.messageComplete(_contentPosition);
257 return total_filled;
258 }
259
260 int length=_buffer.length();
261
262 // Fill buffer if we can
263 if (length == 0)
264 {
265 int filled=-1;
266 if (_body!=null && _buffer!=_body)
267 {
268 _buffer=_body;
269 filled=_buffer.length();
270 }
271
272 if (_buffer.markIndex() == 0 && _buffer.putIndex() == _buffer.capacity())
273 throw new HttpException(HttpStatus.ORDINAL_413_Request_Entity_Too_Large, "FULL");
274
275 IOException ioex=null;
276
277 if (_endp != null && filled<=0)
278 {
279 // Compress buffer if handling _content buffer
280 // TODO check this is not moving data too much
281 if (_buffer == _body)
282 _buffer.compact();
283
284 if (_buffer.space() == 0)
285 throw new HttpException(HttpStatus.ORDINAL_413_Request_Entity_Too_Large, "FULL "+(_buffer==_body?"body":"head"));
286 try
287 {
288 if (total_filled<0)
289 total_filled=0;
290 filled=_endp.fill(_buffer);
291 if (filled>0)
292 total_filled+=filled;
293 }
294 catch(IOException e)
295 {
296 Log.debug(e);
297 ioex=e;
298 filled=-1;
299 }
300 }
301
302 if (filled < 0)
303 {
304 if ( _state == STATE_EOF_CONTENT)
305 {
306 if (_buffer.length()>0)
307 {
308 // TODO should we do this here or fall down to main loop?
309 Buffer chunk=_buffer.get(_buffer.length());
310 _contentPosition += chunk.length();
311 _contentView.update(chunk);
312 _handler.content(chunk); // May recurse here
313 }
314 _state=STATE_END;
315 _handler.messageComplete(_contentPosition);
316 return total_filled;
317 }
318 reset(true);
319 throw new EofException(ioex);
320 }
321 length=_buffer.length();
322 }
323
324
325 // EventHandler header
326 byte ch;
327 byte[] array=_buffer.array();
328
329 while (_state<STATE_END && length-->0)
330 {
331 ch=_buffer.get();
332
333 if (_eol == HttpTokens.CARRIAGE_RETURN && ch == HttpTokens.LINE_FEED)
334 {
335 _eol=HttpTokens.LINE_FEED;
336 continue;
337 }
338 _eol=0;
339
340 switch (_state)
341 {
342 case STATE_START:
343 _contentLength=HttpTokens.UNKNOWN_CONTENT;
344 _cached=null;
345 if (ch > HttpTokens.SPACE || ch<0)
346 {
347 _buffer.mark();
348 _state=STATE_FIELD0;
349 }
350 break;
351
352 case STATE_FIELD0:
353 if (ch == HttpTokens.SPACE)
354 {
355 _tok0.update(_buffer.markIndex(), _buffer.getIndex() - 1);
356 _state=STATE_SPACE1;
357 continue;
358 }
359 else if (ch < HttpTokens.SPACE && ch>=0)
360 {
361 throw new HttpException(HttpServletResponse.SC_BAD_REQUEST);
362 }
363 break;
364
365 case STATE_SPACE1:
366 if (ch > HttpTokens.SPACE || ch<0)
367 {
368 _buffer.mark();
369 _state=STATE_FIELD1;
370 }
371 else if (ch < HttpTokens.SPACE)
372 {
373 throw new HttpException(HttpServletResponse.SC_BAD_REQUEST);
374 }
375 break;
376
377 case STATE_FIELD1:
378 if (ch == HttpTokens.SPACE)
379 {
380 _tok1.update(_buffer.markIndex(), _buffer.getIndex() - 1);
381 _state=STATE_SPACE2;
382 continue;
383 }
384 else if (ch < HttpTokens.SPACE && ch>=0)
385 {
386 // HTTP/0.9
387 _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _buffer
388 .sliceFromMark(), null);
389 _state=STATE_END;
390 _handler.headerComplete();
391 _handler.messageComplete(_contentPosition);
392 return total_filled;
393 }
394 break;
395
396 case STATE_SPACE2:
397 if (ch > HttpTokens.SPACE || ch<0)
398 {
399 _buffer.mark();
400 _state=STATE_FIELD2;
401 }
402 else if (ch < HttpTokens.SPACE)
403 {
404 // HTTP/0.9
405 _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _tok1, null);
406 _state=STATE_END;
407 _handler.headerComplete();
408 _handler.messageComplete(_contentPosition);
409 return total_filled;
410 }
411 break;
412
413 case STATE_FIELD2:
414 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
415 {
416 // TODO - we really should know if we are parsing request or response!
417 final Buffer method = HttpMethods.CACHE.lookup(_tok0);
418 if (method==_tok0 && _tok1.length()==3 && Character.isDigit((char)_tok1.peek()))
419 {
420 _responseStatus = BufferUtil.toInt(_tok1);
421 _handler.startResponse(HttpVersions.CACHE.lookup(_tok0), _responseStatus,_buffer.sliceFromMark());
422 }
423 else
424 {
425 _handler.startRequest(method, _tok1,HttpVersions.CACHE.lookup(_buffer.sliceFromMark()));
426 }
427 _eol=ch;
428 _state=STATE_HEADER;
429 _tok0.setPutIndex(_tok0.getIndex());
430 _tok1.setPutIndex(_tok1.getIndex());
431 _multiLineValue=null;
432 continue;
433 // return total_filled;
434 }
435 break;
436
437 case STATE_HEADER:
438 switch(ch)
439 {
440 case HttpTokens.COLON:
441 case HttpTokens.SPACE:
442 case HttpTokens.TAB:
443 {
444 // header value without name - continuation?
445 _length=-1;
446 _state=STATE_HEADER_VALUE;
447 break;
448 }
449
450 default:
451 {
452 // handler last header if any
453 if (_cached!=null || _tok0.length() > 0 || _tok1.length() > 0 || _multiLineValue != null)
454 {
455
456 Buffer header=_cached!=null?_cached:HttpHeaders.CACHE.lookup(_tok0);
457 _cached=null;
458 Buffer value=_multiLineValue == null ? (Buffer) _tok1 : (Buffer) new ByteArrayBuffer(_multiLineValue);
459
460 int ho=HttpHeaders.CACHE.getOrdinal(header);
461 if (ho >= 0)
462 {
463 int vo=-1;
464
465 switch (ho)
466 {
467 case HttpHeaders.CONTENT_LENGTH_ORDINAL:
468 if (_contentLength != HttpTokens.CHUNKED_CONTENT)
469 {
470 try
471 {
472 _contentLength=BufferUtil.toLong(value);
473 }
474 catch(NumberFormatException e)
475 {
476 Log.ignore(e);
477 throw new HttpException(HttpServletResponse.SC_BAD_REQUEST);
478 }
479 if (_contentLength <= 0)
480 _contentLength=HttpTokens.NO_CONTENT;
481 }
482 break;
483
484 case HttpHeaders.TRANSFER_ENCODING_ORDINAL:
485 value=HttpHeaderValues.CACHE.lookup(value);
486 vo=HttpHeaderValues.CACHE.getOrdinal(value);
487 if (HttpHeaderValues.CHUNKED_ORDINAL == vo)
488 _contentLength=HttpTokens.CHUNKED_CONTENT;
489 else
490 {
491 String c=value.toString();
492 if (c.endsWith(HttpHeaderValues.CHUNKED))
493 _contentLength=HttpTokens.CHUNKED_CONTENT;
494
495 else if (c.indexOf(HttpHeaderValues.CHUNKED) >= 0)
496 throw new HttpException(400,null);
497 }
498 break;
499 }
500 }
501
502 _handler.parsedHeader(header, value);
503 _tok0.setPutIndex(_tok0.getIndex());
504 _tok1.setPutIndex(_tok1.getIndex());
505 _multiLineValue=null;
506 }
507
508
509 // now handle ch
510 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
511 {
512 // End of header
513
514 // work out the _content demarcation
515 if (_contentLength == HttpTokens.UNKNOWN_CONTENT)
516 {
517 if (_responseStatus == 0 // request
518 || _responseStatus == 304 // not-modified response
519 || _responseStatus == 204 // no-content response
520 || _responseStatus < 200) // 1xx response
521 _contentLength=HttpTokens.NO_CONTENT;
522 else
523 _contentLength=HttpTokens.EOF_CONTENT;
524 }
525
526 _contentPosition=0;
527 _eol=ch;
528 // We convert _contentLength to an int for this switch statement because
529 // we don't care about the amount of data available just whether there is some.
530 switch (_contentLength > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) _contentLength)
531 {
532 case HttpTokens.EOF_CONTENT:
533 _state=STATE_EOF_CONTENT;
534 if(_body==null && _buffers!=null)
535 _body=_buffers.getBuffer(_contentBufferSize);
536
537 _handler.headerComplete(); // May recurse here !
538 break;
539
540 case HttpTokens.CHUNKED_CONTENT:
541 _state=STATE_CHUNKED_CONTENT;
542 if (_body==null && _buffers!=null)
543 _body=_buffers.getBuffer(_contentBufferSize);
544 _handler.headerComplete(); // May recurse here !
545 break;
546
547 case HttpTokens.NO_CONTENT:
548 _state=STATE_END;
549 _handler.headerComplete();
550 _handler.messageComplete(_contentPosition);
551 break;
552
553 default:
554 _state=STATE_CONTENT;
555 if(_forceContentBuffer ||
556 (_buffers!=null && _body==null && _buffer==_header && _contentLength>=(_header.capacity()-_header.getIndex())))
557 _body=_buffers.getBuffer(_contentBufferSize);
558 _handler.headerComplete(); // May recurse here !
559 break;
560 }
561 return total_filled;
562 }
563 else
564 {
565 // New header
566 _length=1;
567 _buffer.mark();
568 _state=STATE_HEADER_NAME;
569
570 // try cached name!
571 if (array!=null)
572 {
573 _cached=HttpHeaders.CACHE.getBest(array, _buffer.markIndex(), length+1);
574
575 if (_cached!=null)
576 {
577 _length=_cached.length();
578 _buffer.setGetIndex(_buffer.markIndex()+_length);
579 length=_buffer.length();
580 }
581 }
582 }
583 }
584 }
585
586 break;
587
588 case STATE_HEADER_NAME:
589 switch(ch)
590 {
591 case HttpTokens.CARRIAGE_RETURN:
592 case HttpTokens.LINE_FEED:
593 if (_length > 0)
594 _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length);
595 _eol=ch;
596 _state=STATE_HEADER;
597 break;
598 case HttpTokens.COLON:
599 if (_length > 0 && _cached==null)
600 _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length);
601 _length=-1;
602 _state=STATE_HEADER_VALUE;
603 break;
604 case HttpTokens.SPACE:
605 case HttpTokens.TAB:
606 break;
607 default:
608 {
609 _cached=null;
610 if (_length == -1)
611 _buffer.mark();
612 _length=_buffer.getIndex() - _buffer.markIndex();
613 _state=STATE_HEADER_IN_NAME;
614 }
615 }
616
617 break;
618
619 case STATE_HEADER_IN_NAME:
620 switch(ch)
621 {
622 case HttpTokens.CARRIAGE_RETURN:
623 case HttpTokens.LINE_FEED:
624 if (_length > 0)
625 _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length);
626 _eol=ch;
627 _state=STATE_HEADER;
628 break;
629 case HttpTokens.COLON:
630 if (_length > 0 && _cached==null)
631 _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length);
632 _length=-1;
633 _state=STATE_HEADER_VALUE;
634 break;
635 case HttpTokens.SPACE:
636 case HttpTokens.TAB:
637 _state=STATE_HEADER_NAME;
638 break;
639 default:
640 {
641 _cached=null;
642 _length++;
643 }
644 }
645 break;
646
647 case STATE_HEADER_VALUE:
648 switch(ch)
649 {
650 case HttpTokens.CARRIAGE_RETURN:
651 case HttpTokens.LINE_FEED:
652 if (_length > 0)
653 {
654 if (_tok1.length() == 0)
655 _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length);
656 else
657 {
658 // Continuation line!
659 if (_multiLineValue == null) _multiLineValue=_tok1.toString();
660 _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length);
661 _multiLineValue += " " + _tok1.toString();
662 }
663 }
664 _eol=ch;
665 _state=STATE_HEADER;
666 break;
667 case HttpTokens.SPACE:
668 case HttpTokens.TAB:
669 break;
670 default:
671 {
672 if (_length == -1)
673 _buffer.mark();
674 _length=_buffer.getIndex() - _buffer.markIndex();
675 _state=STATE_HEADER_IN_VALUE;
676 }
677 }
678 break;
679
680 case STATE_HEADER_IN_VALUE:
681 switch(ch)
682 {
683 case HttpTokens.CARRIAGE_RETURN:
684 case HttpTokens.LINE_FEED:
685 if (_length > 0)
686 {
687 if (_tok1.length() == 0)
688 _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length);
689 else
690 {
691 // Continuation line!
692 if (_multiLineValue == null) _multiLineValue=_tok1.toString();
693 _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length);
694 _multiLineValue += " " + _tok1.toString();
695 }
696 }
697 _eol=ch;
698 _state=STATE_HEADER;
699 break;
700 case HttpTokens.SPACE:
701 case HttpTokens.TAB:
702 _state=STATE_HEADER_VALUE;
703 break;
704 default:
705 _length++;
706 }
707 break;
708 }
709 } // end of HEADER states loop
710
711 // ==========================
712
713 // Handle _content
714 length=_buffer.length();
715
716 Buffer chunk;
717 while (_state > STATE_END && length > 0)
718 {
719 if (_eol == HttpTokens.CARRIAGE_RETURN && _buffer.peek() == HttpTokens.LINE_FEED)
720 {
721 _eol=_buffer.get();
722 length=_buffer.length();
723 continue;
724 }
725 _eol=0;
726 switch (_state)
727 {
728 case STATE_EOF_CONTENT:
729 chunk=_buffer.get(_buffer.length());
730 _contentPosition += chunk.length();
731 _contentView.update(chunk);
732 _handler.content(chunk); // May recurse here
733 // TODO adjust the _buffer to keep unconsumed content
734 return total_filled;
735
736 case STATE_CONTENT:
737 {
738 long remaining=_contentLength - _contentPosition;
739 if (remaining == 0)
740 {
741 _state=STATE_END;
742 _handler.messageComplete(_contentPosition);
743 return total_filled;
744 }
745
746 if (length > remaining)
747 {
748 // We can cast reamining to an int as we know that it is smaller than
749 // or equal to length which is already an int.
750 length=(int)remaining;
751 }
752
753 chunk=_buffer.get(length);
754 _contentPosition += chunk.length();
755 _contentView.update(chunk);
756 _handler.content(chunk); // May recurse here
757
758 if(_contentPosition == _contentLength)
759 {
760 _state=STATE_END;
761 _handler.messageComplete(_contentPosition);
762 }
763 // TODO adjust the _buffer to keep unconsumed content
764 return total_filled;
765 }
766
767 case STATE_CHUNKED_CONTENT:
768 {
769 ch=_buffer.peek();
770 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
771 _eol=_buffer.get();
772 else if (ch <= HttpTokens.SPACE)
773 _buffer.get();
774 else
775 {
776 _chunkLength=0;
777 _chunkPosition=0;
778 _state=STATE_CHUNK_SIZE;
779 }
780 break;
781 }
782
783 case STATE_CHUNK_SIZE:
784 {
785 ch=_buffer.get();
786 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
787 {
788 _eol=ch;
789 if (_chunkLength == 0)
790 {
791 _state=STATE_END;
792 _handler.messageComplete(_contentPosition);
793 return total_filled;
794 }
795 else
796 _state=STATE_CHUNK;
797 }
798 else if (ch <= HttpTokens.SPACE || ch == HttpTokens.SEMI_COLON)
799 _state=STATE_CHUNK_PARAMS;
800 else if (ch >= '0' && ch <= '9')
801 _chunkLength=_chunkLength * 16 + (ch - '0');
802 else if (ch >= 'a' && ch <= 'f')
803 _chunkLength=_chunkLength * 16 + (10 + ch - 'a');
804 else if (ch >= 'A' && ch <= 'F')
805 _chunkLength=_chunkLength * 16 + (10 + ch - 'A');
806 else
807 throw new IOException("bad chunk char: " + ch);
808 break;
809 }
810
811 case STATE_CHUNK_PARAMS:
812 {
813 ch=_buffer.get();
814 if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
815 {
816 _eol=ch;
817 if (_chunkLength == 0)
818 {
819 _state=STATE_END;
820 _handler.messageComplete(_contentPosition);
821 return total_filled;
822 }
823 else
824 _state=STATE_CHUNK;
825 }
826 break;
827 }
828
829 case STATE_CHUNK:
830 {
831 int remaining=_chunkLength - _chunkPosition;
832 if (remaining == 0)
833 {
834 _state=STATE_CHUNKED_CONTENT;
835 break;
836 }
837 else if (length > remaining)
838 length=remaining;
839 chunk=_buffer.get(length);
840 _contentPosition += chunk.length();
841 _chunkPosition += chunk.length();
842 _contentView.update(chunk);
843 _handler.content(chunk); // May recurse here
844 // TODO adjust the _buffer to keep unconsumed content
845 return total_filled;
846 }
847 }
848
849 length=_buffer.length();
850 }
851 return total_filled;
852 }
853
854 /* ------------------------------------------------------------------------------- */
855 /** fill the buffers from the endpoint
856 *
857 */
858 public long fill() throws IOException
859 {
860 if (_buffer==null)
861 {
862 _buffer=_header=getHeaderBuffer();
863 _tok0=new View.CaseInsensitive(_buffer);
864 _tok1=new View.CaseInsensitive(_buffer);
865 }
866 if (_body!=null && _buffer!=_body)
867 _buffer=_body;
868 if (_buffer == _body)
869 _buffer.compact();
870
871 int space=_buffer.space();
872
873 // Fill buffer if we can
874 if (space == 0)
875 throw new HttpException(HttpStatus.ORDINAL_413_Request_Entity_Too_Large, "FULL "+(_buffer==_body?"body":"head"));
876 else
877 {
878 int filled=-1;
879
880 if (_endp != null )
881 {
882 try
883 {
884 filled=_endp.fill(_buffer);
885 }
886 catch(IOException e)
887 {
888 Log.debug(e);
889 reset(true);
890 throw (e instanceof EofException) ? e:new EofException(e);
891 }
892 }
893
894 return filled;
895 }
896 }
897
898 /* ------------------------------------------------------------------------------- */
899 /** Skip any CRLFs in buffers
900 *
901 */
902 public void skipCRLF()
903 {
904
905 while (_header!=null && _header.length()>0)
906 {
907 byte ch = _header.peek();
908 if (ch==HttpTokens.CARRIAGE_RETURN || ch==HttpTokens.LINE_FEED)
909 {
910 _eol=ch;
911 _header.skip(1);
912 }
913 else
914 break;
915 }
916
917 while (_body!=null && _body.length()>0)
918 {
919 byte ch = _body.peek();
920 if (ch==HttpTokens.CARRIAGE_RETURN || ch==HttpTokens.LINE_FEED)
921 {
922 _eol=ch;
923 _body.skip(1);
924 }
925 else
926 break;
927 }
928
929 }
930 /* ------------------------------------------------------------------------------- */
931 public void reset(boolean returnBuffers)
932 {
933 synchronized (this)
934 {
935 _contentView.setGetIndex(_contentView.putIndex());
936
937 _state=STATE_START;
938 _contentLength=HttpTokens.UNKNOWN_CONTENT;
939 _contentPosition=0;
940 _length=0;
941 _responseStatus=0;
942
943 if (_buffer!=null && _buffer.length()>0 && _eol == HttpTokens.CARRIAGE_RETURN && _buffer.peek() == HttpTokens.LINE_FEED)
944 {
945 _buffer.skip(1);
946 _eol=HttpTokens.LINE_FEED;
947 }
948
949 if (_body!=null)
950 {
951 if (_body.hasContent())
952 {
953 // There is content in the body after the end of the request.
954 // This is probably a pipelined header of the next request, so we need to
955 // copy it to the header buffer.
956 _header.setMarkIndex(-1);
957 _header.compact();
958 int take=_header.space();
959 if (take>_body.length())
960 take=_body.length();
961 _body.peek(_body.getIndex(),take);
962 _body.skip(_header.put(_body.peek(_body.getIndex(),take)));
963 }
964
965 if (_body.length()==0)
966 {
967 if (_buffers!=null && returnBuffers)
968 _buffers.returnBuffer(_body);
969 _body=null;
970 }
971 else
972 {
973 _body.setMarkIndex(-1);
974 _body.compact();
975 }
976 }
977
978
979 if (_header!=null)
980 {
981 _header.setMarkIndex(-1);
982 if (!_header.hasContent() && _buffers!=null && returnBuffers)
983 {
984 _buffers.returnBuffer(_header);
985 _header=null;
986 _buffer=null;
987 }
988 else
989 {
990 _header.compact();
991 _tok0.update(_header);
992 _tok0.update(0,0);
993 _tok1.update(_header);
994 _tok1.update(0,0);
995 }
996 }
997
998 _buffer=_header;
999 }
1000 }
1001
1002 /* ------------------------------------------------------------------------------- */
1003 public void setState(int state)
1004 {
1005 this._state=state;
1006 _contentLength=HttpTokens.UNKNOWN_CONTENT;
1007 }
1008
1009 /* ------------------------------------------------------------------------------- */
1010 public String toString(Buffer buf)
1011 {
1012 return "state=" + _state + " length=" + _length + " buf=" + buf.hashCode();
1013 }
1014
1015 /* ------------------------------------------------------------------------------- */
1016 public String toString()
1017 {
1018 return "state=" + _state + " length=" + _length + " len=" + _contentLength;
1019 }
1020
1021 /* ------------------------------------------------------------ */
1022 public Buffer getHeaderBuffer()
1023 {
1024 if (_header == null)
1025 {
1026 _header=_buffers.getBuffer(_headerBufferSize);
1027 }
1028 return _header;
1029 }
1030
1031 /* ------------------------------------------------------------ */
1032 public Buffer getBodyBuffer()
1033 {
1034 return _body;
1035 }
1036
1037 /* ------------------------------------------------------------ */
1038 /**
1039 * @param force True if a new buffer will be forced to be used for content and the header buffer will not be used.
1040 */
1041 public void setForceContentBuffer(boolean force)
1042 {
1043 _forceContentBuffer=force;
1044 }
1045
1046 /* ------------------------------------------------------------ */
1047 /* ------------------------------------------------------------ */
1048 /* ------------------------------------------------------------ */
1049 public static abstract class EventHandler
1050 {
1051 public abstract void content(Buffer ref) throws IOException;
1052
1053 public void headerComplete() throws IOException
1054 {
1055 }
1056
1057 public void messageComplete(long contentLength) throws IOException
1058 {
1059 }
1060
1061 /**
1062 * This is the method called by parser when a HTTP Header name and value is found
1063 */
1064 public void parsedHeader(Buffer name, Buffer value) throws IOException
1065 {
1066 }
1067
1068 /**
1069 * This is the method called by parser when the HTTP request line is parsed
1070 */
1071 public abstract void startRequest(Buffer method, Buffer url, Buffer version)
1072 throws IOException;
1073
1074 /**
1075 * This is the method called by parser when the HTTP request line is parsed
1076 */
1077 public abstract void startResponse(Buffer version, int status, Buffer reason)
1078 throws IOException;
1079 }
1080
1081
1082
1083 /* ------------------------------------------------------------ */
1084 /* ------------------------------------------------------------ */
1085 /* ------------------------------------------------------------ */
1086 public static class Input extends ServletInputStream
1087 {
1088 protected HttpParser _parser;
1089 protected EndPoint _endp;
1090 protected long _maxIdleTime;
1091 protected Buffer _content;
1092
1093 /* ------------------------------------------------------------ */
1094 public Input(HttpParser parser, long maxIdleTime)
1095 {
1096 _parser=parser;
1097 _endp=parser._endp;
1098 _maxIdleTime=maxIdleTime;
1099 _content=_parser._contentView;
1100 _parser._input=this;
1101 }
1102
1103 /* ------------------------------------------------------------ */
1104 /*
1105 * @see java.io.InputStream#read()
1106 */
1107 public int read() throws IOException
1108 {
1109 int c=-1;
1110 if (blockForContent())
1111 c= 0xff & _content.get();
1112 return c;
1113 }
1114
1115 /* ------------------------------------------------------------ */
1116 /*
1117 * @see java.io.InputStream#read(byte[], int, int)
1118 */
1119 public int read(byte[] b, int off, int len) throws IOException
1120 {
1121 int l=-1;
1122 if (blockForContent())
1123 l= _content.get(b, off, len);
1124 return l;
1125 }
1126
1127 /* ------------------------------------------------------------ */
1128 private boolean blockForContent() throws IOException
1129 {
1130 if (_content.length()>0)
1131 return true;
1132 if (_parser.getState() <= HttpParser.STATE_END)
1133 return false;
1134
1135 // Handle simple end points.
1136 if (_endp==null)
1137 _parser.parseNext();
1138
1139 // Handle blocking end points
1140 else if (_endp.isBlocking())
1141 {
1142 try
1143 {
1144 _parser.parseNext();
1145
1146 // parse until some progress is made (or IOException thrown for timeout)
1147 while(_content.length() == 0 && !_parser.isState(HttpParser.STATE_END) && _endp.isOpen())
1148 {
1149 // Try to get more _parser._content
1150 _parser.parseNext();
1151 }
1152 }
1153 catch(IOException e)
1154 {
1155 _endp.close();
1156 throw e;
1157 }
1158 }
1159 else // Handle non-blocking end point
1160 {
1161 _parser.parseNext();
1162
1163 // parse until some progress is made (or IOException thrown for timeout)
1164 while(_content.length() == 0 && !_parser.isState(HttpParser.STATE_END) && _endp.isOpen())
1165 {
1166 if (_endp.isBufferingInput() && _parser.parseNext()>0)
1167 continue;
1168
1169 if (!_endp.blockReadable(_maxIdleTime))
1170 {
1171 _endp.close();
1172 throw new EofException("timeout");
1173 }
1174
1175 // Try to get more _parser._content
1176 _parser.parseNext();
1177 }
1178 }
1179
1180 return _content.length()>0;
1181 }
1182
1183 /* ------------------------------------------------------------ */
1184 /* (non-Javadoc)
1185 * @see java.io.InputStream#available()
1186 */
1187 public int available() throws IOException
1188 {
1189 if (_content!=null && _content.length()>0)
1190 return _content.length();
1191 if (!_endp.isBlocking())
1192 _parser.parseNext();
1193
1194 return _content==null?0:_content.length();
1195 }
1196 }
1197
1198
1199
1200
1201 }