View Javadoc

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 }