View Javadoc

1   // ========================================================================
2   // Copyright (c) 2006-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at 
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses. 
12  // ========================================================================
13  
14  package org.eclipse.jetty.ajp;
15  
16  import java.io.IOException;
17  import java.io.InterruptedIOException;
18  
19  import javax.servlet.ServletInputStream;
20  
21  import org.eclipse.jetty.http.HttpTokens;
22  import org.eclipse.jetty.http.Parser;
23  import org.eclipse.jetty.io.Buffer;
24  import org.eclipse.jetty.io.BufferUtil;
25  import org.eclipse.jetty.io.Buffers;
26  import org.eclipse.jetty.io.EndPoint;
27  import org.eclipse.jetty.io.EofException;
28  import org.eclipse.jetty.io.View;
29  import org.eclipse.jetty.util.log.Log;
30  
31  /**
32   * 
33   */
34  public class Ajp13Parser implements Parser
35  {
36      private final static int STATE_START = -1;
37      private final static int STATE_END = 0;
38      private final static int STATE_AJP13CHUNK_START = 1;
39      private final static int STATE_AJP13CHUNK = 2;
40  
41      private int _state = STATE_START;
42      private long _contentLength;
43      private long _contentPosition;
44      private int _chunkLength;
45      private int _chunkPosition;
46      private int _headers;
47      private Buffers _buffers;
48      private EndPoint _endp;
49      private Buffer _buffer;
50      private Buffer _header; // Buffer for header data (and small _content)
51      private Buffer _body; // Buffer for large content
52      private View _contentView = new View();
53      private EventHandler _handler;
54      private Ajp13Generator _generator;
55      private View _tok0; // Saved token: header name, request method or response version
56      private View _tok1; // Saved token: header value, request URI orresponse code
57      protected int _length;
58      protected int _packetLength;
59      
60  
61      /* ------------------------------------------------------------------------------- */
62      public Ajp13Parser(Buffers buffers, EndPoint endPoint)
63      {
64          _buffers = buffers;
65          _endp = endPoint;
66      }
67      
68      /* ------------------------------------------------------------------------------- */
69      public void setEventHandler(EventHandler handler)
70      {
71          _handler=handler;
72      }
73      
74      /* ------------------------------------------------------------------------------- */
75      public void setGenerator(Ajp13Generator generator)
76      {
77          _generator=generator;
78      }
79  
80      /* ------------------------------------------------------------------------------- */
81      public long getContentLength()
82      {
83          return _contentLength;
84      }
85  
86      /* ------------------------------------------------------------------------------- */
87      public int getState()
88      {
89          return _state;
90      }
91  
92      /* ------------------------------------------------------------------------------- */
93      public boolean inContentState()
94      {
95          return _state > 0;
96      }
97  
98      /* ------------------------------------------------------------------------------- */
99      public boolean inHeaderState()
100     {
101         return _state < 0;
102     }
103 
104     /* ------------------------------------------------------------------------------- */
105     public boolean isIdle()
106     {
107         return _state == STATE_START;
108     }
109 
110     /* ------------------------------------------------------------------------------- */
111     public boolean isComplete()
112     {
113         return _state == STATE_END;
114     }
115 
116     /* ------------------------------------------------------------------------------- */
117     public boolean isMoreInBuffer()
118     {
119 
120         if (_header != null && _header.hasContent() || _body != null && _body.hasContent())
121             return true;
122 
123         return false;
124     }
125 
126     /* ------------------------------------------------------------------------------- */
127     public boolean isState(int state)
128     {
129         return _state == state;
130     }
131 
132     /* ------------------------------------------------------------------------------- */
133     public void parse() throws IOException
134     {
135         if (_state == STATE_END)
136             reset(false);
137         if (_state != STATE_START)
138             throw new IllegalStateException("!START");
139 
140         // continue parsing
141         while (!isComplete())
142         {
143             parseNext();
144         }
145     }
146 
147     /* ------------------------------------------------------------------------------- */
148     public long parseAvailable() throws IOException
149     {
150         long len = parseNext();
151         long total = len > 0 ? len : 0;
152 
153         // continue parsing
154         while (!isComplete() && _buffer != null && _buffer.length() > 0)
155         {
156             len = parseNext();
157             if (len > 0)
158                 total += len;
159             else
160                 break;
161         }
162         return total;
163     }
164 
165     /* ------------------------------------------------------------------------------- */
166     private int fill() throws IOException
167     {
168         int filled = -1;
169         if (_body != null && _buffer != _body)
170         {
171             // mod_jk implementations may have some partial data from header
172             // check if there are partial contents in the header
173             // copy it to the body if there are any
174             if(_header.length() > 0)
175             {
176                 // copy the patial data from the header to the body
177                 _body.put(_header);
178             }
179 
180             _buffer = _body;
181             
182             if (_buffer.length()>0)
183             {            
184                 filled = _buffer.length();
185                 return filled;
186             }
187         }
188 
189         if (_buffer.markIndex() == 0 && _buffer.putIndex() == _buffer.capacity())
190             throw new IOException("FULL");
191         if (_endp != null && filled <= 0)
192         {
193             // Compress buffer if handling _content buffer
194             // TODO check this is not moving data too much
195             if (_buffer == _body)
196                 _buffer.compact();
197 
198             if (_buffer.space() == 0)
199                 throw new IOException("FULL");
200 
201             try
202             {
203                 filled = _endp.fill(_buffer);
204             }
205             catch (IOException e)
206             {
207                 // This is normal in AJP since the socket closes on timeout only
208                 Log.debug(e);
209                 reset(true);
210                 throw (e instanceof EofException) ? e : new EofException(e);
211             }
212         }
213         
214         if (filled < 0)
215         {
216             if (_state > STATE_END)
217             {
218                 _state = STATE_END;
219                 _handler.messageComplete(_contentPosition);
220                 return filled;
221             }
222             reset(true);
223             throw new EofException();
224         }
225     
226         return filled;
227     }
228     
229     /* ------------------------------------------------------------------------------- */
230     public long parseNext() throws IOException
231     {
232         long total_filled = -1;
233 
234         if (_buffer == null)
235         {
236             if (_header == null)
237                 _header = _buffers.getHeader();
238            
239             _buffer = _header;
240             _tok0 = new View(_header);
241             _tok1 = new View(_header);
242             _tok0.setPutIndex(_tok0.getIndex());
243             _tok1.setPutIndex(_tok1.getIndex());
244         }
245 
246         if (_state == STATE_END)
247             throw new IllegalStateException("STATE_END");
248         if (_state > STATE_END && _contentPosition == _contentLength)
249         {
250             _state = STATE_END;
251             _handler.messageComplete(_contentPosition);
252             return total_filled;
253         }
254         
255         if (_state < 0)
256         {
257             // have we seen a packet?
258             if (_packetLength<=0)
259             {
260                 if (_buffer.length()<4)
261                 {
262                     if (total_filled<0) 
263                         total_filled=0;
264                     total_filled+=fill();
265                     if (_buffer.length()<4)
266                         return total_filled;
267                 }
268                 
269                 _contentLength = HttpTokens.UNKNOWN_CONTENT;
270                 int _magic = Ajp13RequestPacket.getInt(_buffer);
271                 if (_magic != Ajp13RequestHeaders.MAGIC)
272                     throw new IOException("Bad AJP13 rcv packet: " + "0x" + Integer.toHexString(_magic) + " expected " + "0x" + Integer.toHexString(Ajp13RequestHeaders.MAGIC) + " " + this);
273 
274 
275                 _packetLength = Ajp13RequestPacket.getInt(_buffer);
276                 if (_packetLength > Ajp13Packet.MAX_PACKET_SIZE)
277                     throw new IOException("AJP13 packet (" + _packetLength + "bytes) too large for buffer");
278                 
279             }
280             
281             if (_buffer.length() < _packetLength)
282             {
283                 if (total_filled<0) 
284                     total_filled=0;
285                 total_filled+=fill();
286                 if (_buffer.length() < _packetLength)
287                     return total_filled;
288             }
289 
290             // Parse Header
291             Buffer bufHeaderName = null;
292             Buffer bufHeaderValue = null;
293             int attr_type = 0;
294 
295             byte packetType = Ajp13RequestPacket.getByte(_buffer);
296 
297             switch (packetType)
298             {
299                 case Ajp13Packet.FORWARD_REQUEST_ORDINAL:
300                     _handler.startForwardRequest();
301                     break;
302                 case Ajp13Packet.CPING_REQUEST_ORDINAL:
303                     (_generator).sendCPong();
304                     
305                     if(_header != null)
306                     {
307                         _buffers.returnBuffer(_header);
308                         _header = null;
309                     }
310 
311                     if(_body != null)
312                     {
313                         _buffers.returnBuffer(_body);
314                         _body = null;
315                     }
316 
317                     _buffer= null;
318 
319                     reset(true);
320 
321                     return -1;
322                 case Ajp13Packet.SHUTDOWN_ORDINAL:
323                     shutdownRequest();
324 
325                     return -1;
326 
327                 default:
328                     // XXX Throw an Exception here?? Close
329                     // connection!
330                     Log.warn("AJP13 message type ({PING}: "+packetType+" ) not supported/recognized as an AJP request");
331                 throw new IllegalStateException("PING is not implemented");
332             }
333 
334 
335             _handler.parsedMethod(Ajp13RequestPacket.getMethod(_buffer));
336             _handler.parsedProtocol(Ajp13RequestPacket.getString(_buffer, _tok0));
337             _handler.parsedUri(Ajp13RequestPacket.getString(_buffer, _tok1));
338             _handler.parsedRemoteAddr(Ajp13RequestPacket.getString(_buffer, _tok1));
339             _handler.parsedRemoteHost(Ajp13RequestPacket.getString(_buffer, _tok1));
340             _handler.parsedServerName(Ajp13RequestPacket.getString(_buffer, _tok1));
341             _handler.parsedServerPort(Ajp13RequestPacket.getInt(_buffer));
342             _handler.parsedSslSecure(Ajp13RequestPacket.getBool(_buffer));
343 
344 
345             _headers = Ajp13RequestPacket.getInt(_buffer);
346 
347             for (int h=0;h<_headers;h++)
348             {
349                 bufHeaderName = Ajp13RequestPacket.getHeaderName(_buffer, _tok0);
350                 bufHeaderValue = Ajp13RequestPacket.getString(_buffer, _tok1);
351 
352                 if (bufHeaderName != null && bufHeaderName.toString().equals(Ajp13RequestHeaders.CONTENT_LENGTH))
353                 {
354                     _contentLength = BufferUtil.toLong(bufHeaderValue);
355                     if (_contentLength == 0)
356                         _contentLength = HttpTokens.NO_CONTENT;
357                 }
358 
359                 _handler.parsedHeader(bufHeaderName, bufHeaderValue);
360             }
361 
362 
363 
364             attr_type = Ajp13RequestPacket.getByte(_buffer) & 0xff;
365             while (attr_type != 0xFF)
366             {
367 
368                 switch (attr_type)
369                 {
370                     // XXX How does this plug into the web
371                     // containers
372                     // authentication?
373 
374                     case Ajp13RequestHeaders.REMOTE_USER_ATTR:
375                         _handler.parsedRemoteUser(Ajp13RequestPacket.getString(_buffer, _tok1));
376                         break;
377                     case Ajp13RequestHeaders.AUTH_TYPE_ATTR:
378                         //XXX JASPI how does this make sense?
379                         _handler.parsedAuthorizationType(Ajp13RequestPacket.getString(_buffer, _tok1));
380                         break;
381 
382                     case Ajp13RequestHeaders.QUERY_STRING_ATTR:
383                         _handler.parsedQueryString(Ajp13RequestPacket.getString(_buffer, _tok1));
384                         break;
385 
386                     case Ajp13RequestHeaders.JVM_ROUTE_ATTR:
387                         // XXX Using old Jetty 5 key,
388                         // should change!
389                         // Note used in
390                         // org.eclipse.jetty.servlet.HashSessionIdManager
391                         _handler.parsedRequestAttribute("org.eclipse.http.ajp.JVMRoute", Ajp13RequestPacket.getString(_buffer, _tok1));
392                         break;
393 
394                     case Ajp13RequestHeaders.SSL_CERT_ATTR:
395                         _handler.parsedSslCert(Ajp13RequestPacket.getString(_buffer, _tok1));
396                         break;
397 
398                     case Ajp13RequestHeaders.SSL_CIPHER_ATTR:
399                         _handler.parsedSslCipher(Ajp13RequestPacket.getString(_buffer, _tok1));
400                         // SslSocketConnector.customize()
401                         break;
402 
403                     case Ajp13RequestHeaders.SSL_SESSION_ATTR:
404                         _handler.parsedSslSession(Ajp13RequestPacket.getString(_buffer, _tok1));
405                         break;
406 
407                     case Ajp13RequestHeaders.REQUEST_ATTR:
408                         _handler.parsedRequestAttribute(Ajp13RequestPacket.getString(_buffer, _tok0).toString(), Ajp13RequestPacket.getString(_buffer, _tok1));
409                         break;
410 
411                         // New Jk API?
412                         // Check if experimental or can they
413                         // assumed to be
414                         // supported
415                         
416                     case Ajp13RequestHeaders.SSL_KEYSIZE_ATTR:
417                         
418                         // This has been implemented in AJP13 as either a string or a integer.
419                         // Servlet specs say javax.servlet.request.key_size must be an Integer
420                         
421                         // Does it look like a string containing digits?
422                         int length = Ajp13RequestPacket.getInt(_buffer);
423                         
424                         if (length>0 && length<16)
425                         {
426                             // this must be a string length rather than a key length
427                             _buffer.skip(-2);
428                             _handler.parsedSslKeySize(Integer.parseInt(Ajp13RequestPacket.getString(_buffer, _tok1).toString()));
429                         }
430                         else
431                             _handler.parsedSslKeySize(length);
432                         
433                         break;
434 
435                         
436                         // Used to lock down jk requests with a
437                         // secreate
438                         // key.
439                         
440                     case Ajp13RequestHeaders.SECRET_ATTR:
441                         // XXX Investigate safest way to
442                         // deal with
443                         // this...
444                         // should this tie into shutdown
445                         // packet?
446                         break;
447 
448                     case Ajp13RequestHeaders.STORED_METHOD_ATTR:
449                         // XXX Confirm this should
450                         // really overide
451                         // previously parsed method?
452                         // _handler.parsedMethod(Ajp13PacketMethods.CACHE.get(Ajp13RequestPacket.getString()));
453                         break;
454 
455 
456                     case Ajp13RequestHeaders.CONTEXT_ATTR:
457                         _handler.parsedContextPath(Ajp13RequestPacket.getString(_buffer, _tok1));
458                         break;
459                     case Ajp13RequestHeaders.SERVLET_PATH_ATTR:
460                         _handler.parsedServletPath(Ajp13RequestPacket.getString(_buffer, _tok1));
461 
462                         break;
463                     default:
464                         Log.warn("Unsupported Ajp13 Request Attribute {}", new Integer(attr_type));
465                     break;
466                 }
467 
468                 attr_type = Ajp13RequestPacket.getByte(_buffer) & 0xff;
469             }
470 
471 
472 
473 
474 
475 
476             _contentPosition = 0;
477             switch ((int) _contentLength)
478             {
479 
480                 case HttpTokens.NO_CONTENT:
481                     _state = STATE_END;
482                     _handler.headerComplete();
483                     _handler.messageComplete(_contentPosition);
484 
485                     break;
486 
487                 case HttpTokens.UNKNOWN_CONTENT:
488 
489                     _generator.getBodyChunk();
490                     if (_buffers != null && _body == null && _buffer == _header && _header.length() <= 0)
491                     {
492                         _body = _buffers.getBuffer();
493                         _body.clear();
494                     }
495                     _state = STATE_AJP13CHUNK_START;
496                     _handler.headerComplete(); // May recurse here!
497 
498                     return total_filled;
499 
500                 default:
501 
502                     if (_buffers != null && _body == null && _buffer == _header && _contentLength > (_header.capacity() - _header.getIndex()))
503                     {
504                         _body = _buffers.getBuffer();
505                         _body.clear();
506 
507                     }
508                 _state = STATE_AJP13CHUNK_START;
509                 _handler.headerComplete(); // May recurse here!
510                 return total_filled;
511             }
512         }
513 
514 
515         Buffer chunk;
516 
517         while (_state>STATE_END)
518         {
519             switch (_state)
520             {
521                 case STATE_AJP13CHUNK_START:
522                     if (_buffer.length()<6)
523                     {
524                         if (total_filled<0) 
525                             total_filled=0;
526                         total_filled+=fill();
527                         if (_buffer.length()<6)
528                             return total_filled;
529                     }
530                     int _magic=Ajp13RequestPacket.getInt(_buffer);
531                     if (_magic!=Ajp13RequestHeaders.MAGIC)
532                     {
533                         throw new IOException("Bad AJP13 rcv packet: "+"0x"+Integer.toHexString(_magic)+" expected "+"0x"
534                                 +Integer.toHexString(Ajp13RequestHeaders.MAGIC)+" "+this);
535                     }
536                     _chunkPosition=0;
537                     _chunkLength=Ajp13RequestPacket.getInt(_buffer)-2;
538                     Ajp13RequestPacket.getInt(_buffer);
539                     if (_chunkLength==0)
540                     {
541                         _state=STATE_END;
542                          _generator.gotBody();
543                         _handler.messageComplete(_contentPosition);
544                         return total_filled;
545                     }
546                     _state=STATE_AJP13CHUNK;
547 
548                 case STATE_AJP13CHUNK:
549                     if (_buffer.length()<_chunkLength)
550                     {
551                         if (total_filled<0) 
552                             total_filled=0;
553                         total_filled+=fill();
554                         if (_buffer.length()<_chunkLength)
555                             return total_filled;
556                     }
557 
558                     int remaining=_chunkLength-_chunkPosition;
559 
560                     if (remaining==0)
561                     {
562                         _state=STATE_AJP13CHUNK_START;
563                         if (_contentPosition<_contentLength)
564                         {
565                             _generator.getBodyChunk();
566                         }
567                         else
568                         {
569                             _generator.gotBody();
570                         }
571 
572                         return total_filled;
573                     }
574 
575                     if (_buffer.length()<remaining)
576                     {
577                         remaining=_buffer.length();
578                     }
579 
580                     chunk=Ajp13RequestPacket.get(_buffer,remaining);
581                     _contentPosition+=chunk.length();
582                     _chunkPosition+=chunk.length();
583                     _contentView.update(chunk);
584 
585                     remaining=_chunkLength-_chunkPosition;
586 
587                     if (remaining==0)
588                     {
589                         _state=STATE_AJP13CHUNK_START;
590                         if (_contentPosition<_contentLength || _contentLength == HttpTokens.UNKNOWN_CONTENT)
591                         {
592                             _generator.getBodyChunk();
593                         }
594                         else
595                         {
596                             _generator.gotBody();
597                         }
598                     }
599 
600                     _handler.content(chunk);
601 
602                 return total_filled;
603 
604             default:
605                 throw new IllegalStateException("Invalid Content State");
606 
607             }
608 
609         }
610 
611         return total_filled;
612     }
613 
614     /* ------------------------------------------------------------------------------- */
615     public void reset(boolean returnBuffers)
616     {
617         _state = STATE_START;
618         _contentLength = HttpTokens.UNKNOWN_CONTENT;
619         _contentPosition = 0;
620         _length = 0;
621         _packetLength = 0;
622 
623         if (_body != null)
624         {
625             if (_body.hasContent())
626             {
627                 _header.setMarkIndex(-1);
628                 _header.compact();
629                 // TODO if pipelined requests received after big
630                 // input - maybe this is not good?.
631                 _body.skip(_header.put(_body));
632 
633             }
634 
635             if (_body.length() == 0)
636             {
637                 if (_buffers != null && returnBuffers)
638                     _buffers.returnBuffer(_body);
639                 _body = null;
640             }
641             else
642             {
643                 _body.setMarkIndex(-1);
644                 _body.compact();
645             }
646         }
647 
648         if (_header != null)
649         {
650             _header.setMarkIndex(-1);
651             if (!_header.hasContent() && _buffers != null && returnBuffers)
652             {
653                 _buffers.returnBuffer(_header);
654                 _header = null;
655                 _buffer = null;
656             }
657             else
658             {
659                 _header.compact();
660                 _tok0.update(_header);
661                 _tok0.update(0, 0);
662                 _tok1.update(_header);
663                 _tok1.update(0, 0);
664             }
665         }
666 
667         _buffer = _header;
668     }
669 
670     /* ------------------------------------------------------------------------------- */
671     Buffer getHeaderBuffer()
672     {
673         return _buffer;
674     }
675 
676     private void shutdownRequest()
677     {
678         _state = STATE_END;
679 
680         if(!Ajp13SocketConnector.__allowShutdown)
681         {
682             Log.warn("AJP13: Shutdown Request is Denied, allowShutdown is set to false!!!");
683             return;
684         }
685 
686         if(Ajp13SocketConnector.__secretWord != null)
687         {
688             Log.warn("AJP13: Validating Secret Word");
689             try
690             {
691                 String secretWord = Ajp13RequestPacket.getString(_buffer, _tok1).toString();
692 
693                 if(!Ajp13SocketConnector.__secretWord.equals(secretWord))
694                 {
695                     Log.warn("AJP13: Shutdown Request Denied, Invalid Sercret word!!!");
696                     throw new IllegalStateException("AJP13: Secret Word is Invalid: Peer has requested shutdown but, Secret Word did not match");
697                 }
698             }
699             catch (Exception e)
700             {
701                 Log.warn("AJP13: Secret Word is Required!!!");
702                 Log.debug(e);
703                 throw new IllegalStateException("AJP13: Secret Word is Required: Peer has requested shutdown but, has not provided a Secret Word");
704             }
705 
706 
707             Log.warn("AJP13: Shutdown Request is Denied, allowShutdown is set to false!!!");
708             return;
709         }
710 
711         Log.warn("AJP13: Peer Has Requested for Shutdown!!!");
712         Log.warn("AJP13: Jetty 6 is shutting down !!!");
713         System.exit(0);
714     }
715 
716     /* ------------------------------------------------------------------------------- */
717     public interface EventHandler
718     {
719 
720         // public void shutdownRequest() throws IOException;
721         // public void cpingRequest() throws IOException;
722 
723         public void content(Buffer ref) throws IOException;
724 
725         public void headerComplete() throws IOException;
726 
727         public void messageComplete(long contextLength) throws IOException;
728 
729         public void parsedHeader(Buffer name, Buffer value) throws IOException;
730 
731         public void parsedMethod(Buffer method) throws IOException;
732 
733         public void parsedProtocol(Buffer protocol) throws IOException;
734 
735         public void parsedQueryString(Buffer value) throws IOException;
736 
737         public void parsedRemoteAddr(Buffer addr) throws IOException;
738 
739         public void parsedRemoteHost(Buffer host) throws IOException;
740 
741         public void parsedRequestAttribute(String key, Buffer value) throws IOException;
742         
743         public void parsedRequestAttribute(String key, int value) throws IOException;
744 
745         public void parsedServerName(Buffer name) throws IOException;
746 
747         public void parsedServerPort(int port) throws IOException;
748 
749         public void parsedSslSecure(boolean secure) throws IOException;
750 
751         public void parsedUri(Buffer uri) throws IOException;
752 
753         public void startForwardRequest() throws IOException;
754 
755         public void parsedAuthorizationType(Buffer authType) throws IOException;
756         
757         public void parsedRemoteUser(Buffer remoteUser) throws IOException;
758 
759         public void parsedServletPath(Buffer servletPath) throws IOException;
760         
761         public void parsedContextPath(Buffer context) throws IOException;
762 
763         public void parsedSslCert(Buffer sslCert) throws IOException;
764 
765         public void parsedSslCipher(Buffer sslCipher) throws IOException;
766 
767         public void parsedSslSession(Buffer sslSession) throws IOException;
768 
769         public void parsedSslKeySize(int keySize) throws IOException;
770 
771 
772 
773 
774 
775     }
776 
777     /* ------------------------------------------------------------ */
778     /**
779      * TODO Make this common with HttpParser
780      * 
781      */
782     public static class Input extends ServletInputStream
783     {
784         private Ajp13Parser _parser;
785         private EndPoint _endp;
786         private long _maxIdleTime;
787         private View _content;
788 
789         /* ------------------------------------------------------------ */
790         public Input(Ajp13Parser parser, long maxIdleTime)
791         {
792             _parser = parser;
793             _endp = parser._endp;
794             _maxIdleTime = maxIdleTime;
795             _content = _parser._contentView;
796         }
797 
798         /* ------------------------------------------------------------ */
799         @Override
800         public int read() throws IOException
801         {
802             int c = -1;
803             if (blockForContent())
804                 c = 0xff & _content.get();
805             return c;
806         }
807 
808         /* ------------------------------------------------------------ */
809         /*
810          * @see java.io.InputStream#read(byte[], int, int)
811          */
812         @Override
813         public int read(byte[] b, int off, int len) throws IOException
814         {
815             int l = -1;
816             if (blockForContent())
817                 l = _content.get(b, off, len);
818             return l;
819         }
820 
821         /* ------------------------------------------------------------ */
822         private boolean blockForContent() throws IOException
823         {
824             if (_content.length() > 0)
825                 return true;
826             if (_parser.isState(Ajp13Parser.STATE_END))
827                 return false;
828 
829             // Handle simple end points.
830             if (_endp == null)
831                 _parser.parseNext();
832 
833             // Handle blocking end points
834             else if (_endp.isBlocking())
835             {
836                 _parser.parseNext();
837                 
838                 // parse until some progress is made (or IOException thrown for timeout)
839                 while (_content.length() == 0 && !_parser.isState(Ajp13Parser.STATE_END))
840                 {
841                     // Try to get more _parser._content
842                     _parser.parseNext();
843                 }
844             }
845             else // Handle non-blocking end point
846             {
847                 long filled = _parser.parseNext();
848                 boolean blocked = false;
849 
850                 // parse until some progress is made (or
851                 // IOException thrown for timeout)
852                 while (_content.length() == 0 && !_parser.isState(Ajp13Parser.STATE_END))
853                 {
854                     // if fill called, but no bytes read,
855                     // then block
856                     if (filled > 0)
857                         blocked = false;
858                     else if (filled == 0)
859                     {
860                         if (blocked)
861                             throw new InterruptedIOException("timeout");
862 
863                         blocked = true;
864                         _endp.blockReadable(_maxIdleTime);
865                     }
866 
867                     // Try to get more _parser._content
868                     filled = _parser.parseNext();
869                 }
870             }
871 
872             return _content.length() > 0;
873         }
874 
875     }
876 }