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     volatile int _seq=0;
230     /* ------------------------------------------------------------------------------- */
231     public long parseNext() throws IOException
232     {
233         long total_filled = -1;
234 
235         if (_buffer == null)
236         {
237             if (_header == null)
238                 _header = _buffers.getHeader();
239            
240             _buffer = _header;
241             _tok0 = new View(_header);
242             _tok1 = new View(_header);
243             _tok0.setPutIndex(_tok0.getIndex());
244             _tok1.setPutIndex(_tok1.getIndex());
245         }
246 
247         if (_state == STATE_END)
248             throw new IllegalStateException("STATE_END");
249         if (_state > STATE_END && _contentPosition == _contentLength)
250         {
251             _state = STATE_END;
252             _handler.messageComplete(_contentPosition);
253             return total_filled;
254         }
255         
256         if (_state < 0)
257         {
258             // have we seen a packet?
259             if (_packetLength<=0)
260             {
261                 if (_buffer.length()<4)
262                 {
263                     if (total_filled<0) 
264                         total_filled=0;
265                     total_filled+=fill();
266                     if (_buffer.length()<4)
267                         return total_filled;
268                 }
269                 
270                 _contentLength = HttpTokens.UNKNOWN_CONTENT;
271                 int _magic = Ajp13RequestPacket.getInt(_buffer);
272                 if (_magic != Ajp13RequestHeaders.MAGIC)
273                     throw new IOException("Bad AJP13 rcv packet: " + "0x" + Integer.toHexString(_magic) + " expected " + "0x" + Integer.toHexString(Ajp13RequestHeaders.MAGIC) + " " + this);
274 
275 
276                 _packetLength = Ajp13RequestPacket.getInt(_buffer);
277                 if (_packetLength > Ajp13Packet.MAX_PACKET_SIZE)
278                     throw new IOException("AJP13 packet (" + _packetLength + "bytes) too large for buffer");
279                 
280             }
281             
282             if (_buffer.length() < _packetLength)
283             {
284                 if (total_filled<0) 
285                     total_filled=0;
286                 total_filled+=fill();
287                 if (_buffer.length() < _packetLength)
288                     return total_filled;
289             }
290 
291             // Parse Header
292             Buffer bufHeaderName = null;
293             Buffer bufHeaderValue = null;
294             int attr_type = 0;
295 
296             byte packetType = Ajp13RequestPacket.getByte(_buffer);
297             
298             switch (packetType)
299             {
300                 case Ajp13Packet.FORWARD_REQUEST_ORDINAL:
301                     _handler.startForwardRequest();
302                     break;
303                 case Ajp13Packet.CPING_REQUEST_ORDINAL:
304                     (_generator).sendCPong();
305                     
306                     if(_header != null)
307                     {
308                         _buffers.returnBuffer(_header);
309                         _header = null;
310                     }
311 
312                     if(_body != null)
313                     {
314                         _buffers.returnBuffer(_body);
315                         _body = null;
316                     }
317 
318                     _buffer= null;
319 
320                     reset(true);
321 
322                     return -1;
323                 case Ajp13Packet.SHUTDOWN_ORDINAL:
324                     shutdownRequest();
325 
326                     return -1;
327 
328                 default:
329                     // XXX Throw an Exception here?? Close
330                     // connection!
331                     Log.warn("AJP13 message type ({PING}: "+packetType+" ) not supported/recognized as an AJP request");
332                 throw new IllegalStateException("PING is not implemented");
333             }
334 
335 
336             _handler.parsedMethod(Ajp13RequestPacket.getMethod(_buffer));
337             _handler.parsedProtocol(Ajp13RequestPacket.getString(_buffer, _tok0));
338             _handler.parsedUri(Ajp13RequestPacket.getString(_buffer, _tok1));
339             _handler.parsedRemoteAddr(Ajp13RequestPacket.getString(_buffer, _tok1));
340             _handler.parsedRemoteHost(Ajp13RequestPacket.getString(_buffer, _tok1));
341             _handler.parsedServerName(Ajp13RequestPacket.getString(_buffer, _tok1));
342             _handler.parsedServerPort(Ajp13RequestPacket.getInt(_buffer));
343             _handler.parsedSslSecure(Ajp13RequestPacket.getBool(_buffer));
344 
345 
346             _headers = Ajp13RequestPacket.getInt(_buffer);
347 
348             for (int h=0;h<_headers;h++)
349             {
350                 bufHeaderName = Ajp13RequestPacket.getHeaderName(_buffer, _tok0);
351                 bufHeaderValue = Ajp13RequestPacket.getString(_buffer, _tok1);
352 
353                 if (bufHeaderName != null && bufHeaderName.toString().equals(Ajp13RequestHeaders.CONTENT_LENGTH))
354                 {
355                     _contentLength = BufferUtil.toLong(bufHeaderValue);
356                     if (_contentLength == 0)
357                         _contentLength = HttpTokens.NO_CONTENT;
358                 }
359 
360                 _handler.parsedHeader(bufHeaderName, bufHeaderValue);
361             }
362 
363 
364 
365             attr_type = Ajp13RequestPacket.getByte(_buffer) & 0xff;
366             while (attr_type != 0xFF)
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             _contentPosition = 0;
472             switch ((int) _contentLength)
473             {
474 
475                 case HttpTokens.NO_CONTENT:
476                     _state = STATE_END;
477                     _handler.headerComplete();
478                     _handler.messageComplete(_contentPosition);
479 
480                     break;
481 
482                 case HttpTokens.UNKNOWN_CONTENT:
483 
484                     _generator.getBodyChunk();
485                     if (_buffers != null && _body == null && _buffer == _header && _header.length() <= 0)
486                     {
487                         _body = _buffers.getBuffer();
488                         _body.clear();
489                     }
490                     _state = STATE_AJP13CHUNK_START;
491                     _handler.headerComplete(); // May recurse here!
492 
493                     return total_filled;
494 
495                 default:
496 
497                     if (_buffers != null && _body == null && _buffer == _header && _contentLength > (_header.capacity() - _header.getIndex()))
498                     {
499                         _body = _buffers.getBuffer();
500                         _body.clear();
501 
502                     }
503                 _state = STATE_AJP13CHUNK_START;
504                 _handler.headerComplete(); // May recurse here!
505                 return total_filled;
506             }
507         }
508 
509         Buffer chunk;
510 
511         while (_state>STATE_END)
512         {
513             switch (_state)
514             {
515                 case STATE_AJP13CHUNK_START:
516                     if (_buffer.length()<6)
517                     {
518                         if (total_filled<0) 
519                             total_filled=0;
520                         total_filled+=fill();
521                         if (_buffer.length()<6)
522                             return total_filled;
523                     }
524                     int _magic=Ajp13RequestPacket.getInt(_buffer);
525                     if (_magic!=Ajp13RequestHeaders.MAGIC)
526                     {
527                         throw new IOException("Bad AJP13 rcv packet: "+"0x"+Integer.toHexString(_magic)+" expected "+"0x"
528                                 +Integer.toHexString(Ajp13RequestHeaders.MAGIC)+" "+this);
529                     }
530                     _chunkPosition=0;
531                     _chunkLength=Ajp13RequestPacket.getInt(_buffer)-2;
532                     Ajp13RequestPacket.getInt(_buffer);
533                     if (_chunkLength==0)
534                     {
535                         _state=STATE_END;
536                          _generator.gotBody();
537                         _handler.messageComplete(_contentPosition);
538                         return total_filled;
539                     }
540                     _state=STATE_AJP13CHUNK;
541                     break;
542 
543                 case STATE_AJP13CHUNK:
544                     if (_buffer.length()<_chunkLength)
545                     {
546                         if (total_filled<0) 
547                             total_filled=0;
548                         total_filled+=fill();
549                         if (_buffer.length()<_chunkLength)
550                             return total_filled;
551                     }
552 
553                     int remaining=_chunkLength-_chunkPosition;
554                     
555                     if (remaining==0)
556                     {
557                         _state=STATE_AJP13CHUNK_START;
558                         if (_contentPosition<_contentLength)
559                         {
560                             _generator.getBodyChunk();
561                         }
562                         else
563                         {
564                             _generator.gotBody();
565                         }
566 
567                         return total_filled;
568                     }
569 
570                     if (_buffer.length()<remaining)
571                     {
572                         remaining=_buffer.length();
573                     }
574 
575                     chunk=Ajp13RequestPacket.get(_buffer,remaining);
576                     
577                     _contentPosition+=chunk.length();
578                     _chunkPosition+=chunk.length();
579                     _contentView.update(chunk);
580 
581                     remaining=_chunkLength-_chunkPosition;
582 
583                     if (remaining==0)
584                     {
585                         _state=STATE_AJP13CHUNK_START;
586                         if (_contentPosition<_contentLength || _contentLength == HttpTokens.UNKNOWN_CONTENT)
587                         {
588                             _generator.getBodyChunk();
589                         }
590                         else
591                         {
592                             _generator.gotBody();
593                         }
594                     }
595 
596                     _handler.content(chunk);
597 
598                 return total_filled;
599 
600             default:
601                 throw new IllegalStateException("Invalid Content State");
602 
603             }
604 
605         }
606         
607         return total_filled;
608     }
609 
610     /* ------------------------------------------------------------------------------- */
611     public void reset(boolean returnBuffers)
612     {
613         _state = STATE_START;
614         _contentLength = HttpTokens.UNKNOWN_CONTENT;
615         _contentPosition = 0;
616         _length = 0;
617         _packetLength = 0;
618 
619         if (_body != null)
620         {
621             if (_body.hasContent())
622             {
623                 _header.setMarkIndex(-1);
624                 _header.compact();
625                 // TODO if pipelined requests received after big
626                 // input - maybe this is not good?.
627                 _body.skip(_header.put(_body));
628 
629             }
630 
631             if (_body.length() == 0)
632             {
633                 if (_buffers != null && returnBuffers)
634                     _buffers.returnBuffer(_body);
635                 _body = null;
636             }
637             else
638             {
639                 _body.setMarkIndex(-1);
640                 _body.compact();
641             }
642         }
643 
644         if (_header != null)
645         {
646             _header.setMarkIndex(-1);
647             if (!_header.hasContent() && _buffers != null && returnBuffers)
648             {
649                 _buffers.returnBuffer(_header);
650                 _header = null;
651                 _buffer = null;
652             }
653             else
654             {
655                 _header.compact();
656                 _tok0.update(_header);
657                 _tok0.update(0, 0);
658                 _tok1.update(_header);
659                 _tok1.update(0, 0);
660             }
661         }
662 
663         _buffer = _header;
664     }
665 
666     /* ------------------------------------------------------------------------------- */
667     Buffer getHeaderBuffer()
668     {
669         return _buffer;
670     }
671 
672     private void shutdownRequest()
673     {
674         _state = STATE_END;
675 
676         if(!Ajp13SocketConnector.__allowShutdown)
677         {
678             Log.warn("AJP13: Shutdown Request is Denied, allowShutdown is set to false!!!");
679             return;
680         }
681 
682         if(Ajp13SocketConnector.__secretWord != null)
683         {
684             Log.warn("AJP13: Validating Secret Word");
685             try
686             {
687                 String secretWord = Ajp13RequestPacket.getString(_buffer, _tok1).toString();
688 
689                 if(!Ajp13SocketConnector.__secretWord.equals(secretWord))
690                 {
691                     Log.warn("AJP13: Shutdown Request Denied, Invalid Sercret word!!!");
692                     throw new IllegalStateException("AJP13: Secret Word is Invalid: Peer has requested shutdown but, Secret Word did not match");
693                 }
694             }
695             catch (Exception e)
696             {
697                 Log.warn("AJP13: Secret Word is Required!!!");
698                 Log.debug(e);
699                 throw new IllegalStateException("AJP13: Secret Word is Required: Peer has requested shutdown but, has not provided a Secret Word");
700             }
701 
702 
703             Log.warn("AJP13: Shutdown Request is Denied, allowShutdown is set to false!!!");
704             return;
705         }
706 
707         Log.warn("AJP13: Peer Has Requested for Shutdown!!!");
708         Log.warn("AJP13: Jetty 6 is shutting down !!!");
709         System.exit(0);
710     }
711 
712     /* ------------------------------------------------------------------------------- */
713     public interface EventHandler
714     {
715 
716         // public void shutdownRequest() throws IOException;
717         // public void cpingRequest() throws IOException;
718 
719         public void content(Buffer ref) throws IOException;
720 
721         public void headerComplete() throws IOException;
722 
723         public void messageComplete(long contextLength) throws IOException;
724 
725         public void parsedHeader(Buffer name, Buffer value) throws IOException;
726 
727         public void parsedMethod(Buffer method) throws IOException;
728 
729         public void parsedProtocol(Buffer protocol) throws IOException;
730 
731         public void parsedQueryString(Buffer value) throws IOException;
732 
733         public void parsedRemoteAddr(Buffer addr) throws IOException;
734 
735         public void parsedRemoteHost(Buffer host) throws IOException;
736 
737         public void parsedRequestAttribute(String key, Buffer value) throws IOException;
738         
739         public void parsedRequestAttribute(String key, int value) throws IOException;
740 
741         public void parsedServerName(Buffer name) throws IOException;
742 
743         public void parsedServerPort(int port) throws IOException;
744 
745         public void parsedSslSecure(boolean secure) throws IOException;
746 
747         public void parsedUri(Buffer uri) throws IOException;
748 
749         public void startForwardRequest() throws IOException;
750 
751         public void parsedAuthorizationType(Buffer authType) throws IOException;
752         
753         public void parsedRemoteUser(Buffer remoteUser) throws IOException;
754 
755         public void parsedServletPath(Buffer servletPath) throws IOException;
756         
757         public void parsedContextPath(Buffer context) throws IOException;
758 
759         public void parsedSslCert(Buffer sslCert) throws IOException;
760 
761         public void parsedSslCipher(Buffer sslCipher) throws IOException;
762 
763         public void parsedSslSession(Buffer sslSession) throws IOException;
764 
765         public void parsedSslKeySize(int keySize) throws IOException;
766 
767 
768 
769 
770 
771     }
772 
773     /* ------------------------------------------------------------ */
774     /**
775      * TODO Make this common with HttpParser
776      * 
777      */
778     public static class Input extends ServletInputStream
779     {
780         private Ajp13Parser _parser;
781         private EndPoint _endp;
782         private long _maxIdleTime;
783         private View _content;
784 
785         /* ------------------------------------------------------------ */
786         public Input(Ajp13Parser parser, long maxIdleTime)
787         {
788             _parser = parser;
789             _endp = parser._endp;
790             _maxIdleTime = maxIdleTime;
791             _content = _parser._contentView;
792         }
793 
794         /* ------------------------------------------------------------ */
795         @Override
796         public int read() throws IOException
797         {
798             int c = -1;
799             if (blockForContent())
800                 c = 0xff & _content.get();
801             return c;
802         }
803 
804         /* ------------------------------------------------------------ */
805         /*
806          * @see java.io.InputStream#read(byte[], int, int)
807          */
808         @Override
809         public int read(byte[] b, int off, int len) throws IOException
810         {
811             int l = -1;
812             if (blockForContent())
813                 l = _content.get(b, off, len);
814             return l;
815         }
816 
817         /* ------------------------------------------------------------ */
818         private boolean blockForContent() throws IOException
819         {
820             if (_content.length() > 0)
821                 return true;
822             if (_parser.isState(Ajp13Parser.STATE_END) || _parser.isState(Ajp13Parser.STATE_START))
823                 return false;
824 
825             // Handle simple end points.
826             if (_endp == null)
827                 _parser.parseNext();
828 
829             // Handle blocking end points
830             else if (_endp.isBlocking())
831             {
832                 _parser.parseNext();
833                 
834                 // parse until some progress is made (or IOException thrown for timeout)
835                 while (_content.length() == 0 && !_parser.isState(Ajp13Parser.STATE_END))
836                 {
837                     // Try to get more _parser._content
838                     _parser.parseNext();
839                 }
840             }
841             else // Handle non-blocking end point
842             {
843                 long filled = _parser.parseNext();
844                 boolean blocked = false;
845 
846                 // parse until some progress is made (or
847                 // IOException thrown for timeout)
848                 while (_content.length() == 0 && !_parser.isState(Ajp13Parser.STATE_END))
849                 {
850                     // if fill called, but no bytes read,
851                     // then block
852                     if (filled > 0)
853                         blocked = false;
854                     else if (filled == 0)
855                     {
856                         if (blocked)
857                             throw new InterruptedIOException("timeout");
858 
859                         blocked = true;
860                         _endp.blockReadable(_maxIdleTime);
861                     }
862 
863                     // Try to get more _parser._content
864                     filled = _parser.parseNext();
865                 }
866             }
867 
868             return _content.length() > 0;
869         }
870 
871     }
872 }