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