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