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