View Javadoc

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