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