View Javadoc

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