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