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