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