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, 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 in AJP13 as either a string or a integer.
412                         // Servlet specs say javax.servlet.request.key_size must be an Integer
413                         
414                         // Does it look like a string containing digits?
415                         int length = Ajp13RequestPacket.getInt(_buffer);
416                         
417                         if (length>0 && length<16)
418                         {
419                             // this must be a string length rather than a key length
420                             _buffer.skip(-2);
421                             _handler.parsedSslKeySize(Integer.parseInt(Ajp13RequestPacket.getString(_buffer, _tok1).toString()));
422                         }
423                         else
424                             _handler.parsedSslKeySize(length);
425                         
426                         break;
427 
428                         
429                         // Used to lock down jk requests with a
430                         // secreate
431                         // key.
432                         
433                     case Ajp13RequestHeaders.SECRET_ATTR:
434                         // XXX Investigate safest way to
435                         // deal with
436                         // this...
437                         // should this tie into shutdown
438                         // packet?
439                         break;
440 
441                     case Ajp13RequestHeaders.STORED_METHOD_ATTR:
442                         // XXX Confirm this should
443                         // really overide
444                         // previously parsed method?
445                         // _handler.parsedMethod(Ajp13PacketMethods.CACHE.get(Ajp13RequestPacket.getString()));
446                         break;
447 
448 
449                     case Ajp13RequestHeaders.CONTEXT_ATTR:
450                         _handler.parsedContextPath(Ajp13RequestPacket.getString(_buffer, _tok1));
451                         break;
452                     case Ajp13RequestHeaders.SERVLET_PATH_ATTR:
453                         _handler.parsedServletPath(Ajp13RequestPacket.getString(_buffer, _tok1));
454 
455                         break;
456                     default:
457                         Log.warn("Unsupported Ajp13 Request Attribute {}", new Integer(attr_type));
458                     break;
459                 }
460 
461                 attr_type = Ajp13RequestPacket.getByte(_buffer) & 0xff;
462             }
463 
464 
465 
466 
467 
468 
469             _contentPosition = 0;
470             switch ((int) _contentLength)
471             {
472 
473                 case HttpTokens.NO_CONTENT:
474                     _state = STATE_END;
475                     _handler.headerComplete();
476                     _handler.messageComplete(_contentPosition);
477 
478                     break;
479 
480                 case HttpTokens.UNKNOWN_CONTENT:
481 
482                     _generator.getBodyChunk();
483                     if (_buffers != null && _body == null && _buffer == _header && _header.length() <= 0)
484                     {
485                         _body = _buffers.getBuffer(Ajp13Packet.MAX_PACKET_SIZE);
486                         _body.clear();
487                     }
488                     _state = STATE_AJP13CHUNK_START;
489                     _handler.headerComplete(); // May recurse here!
490 
491                     return total_filled;
492 
493                 default:
494 
495                     if (_buffers != null && _body == null && _buffer == _header && _contentLength > (_header.capacity() - _header.getIndex()))
496                     {
497                         _body = _buffers.getBuffer(Ajp13Packet.MAX_PACKET_SIZE);
498                         _body.clear();
499 
500                     }
501                 _state = STATE_AJP13CHUNK_START;
502                 _handler.headerComplete(); // May recurse here!
503                 return total_filled;
504             }
505         }
506 
507 
508         Buffer chunk;
509 
510         while (_state>STATE_END)
511         {
512             switch (_state)
513             {
514                 case STATE_AJP13CHUNK_START:
515                     if (_buffer.length()<6)
516                     {
517                         if (total_filled<0) 
518                             total_filled=0;
519                         total_filled+=fill();
520                         if (_buffer.length()<6)
521                             return total_filled;
522                     }
523                     int _magic=Ajp13RequestPacket.getInt(_buffer);
524                     if (_magic!=Ajp13RequestHeaders.MAGIC)
525                     {
526                         throw new IOException("Bad AJP13 rcv packet: "+"0x"+Integer.toHexString(_magic)+" expected "+"0x"
527                                 +Integer.toHexString(Ajp13RequestHeaders.MAGIC)+" "+this);
528                     }
529                     _chunkPosition=0;
530                     _chunkLength=Ajp13RequestPacket.getInt(_buffer)-2;
531                     Ajp13RequestPacket.getInt(_buffer);
532                     if (_chunkLength==0)
533                     {
534                         _state=STATE_END;
535                          _generator.gotBody();
536                         _handler.messageComplete(_contentPosition);
537                         return total_filled;
538                     }
539                     _state=STATE_AJP13CHUNK;
540 
541                 case STATE_AJP13CHUNK:
542                     if (_buffer.length()<_chunkLength)
543                     {
544                         if (total_filled<0) 
545                             total_filled=0;
546                         total_filled+=fill();
547                         if (_buffer.length()<_chunkLength)
548                             return total_filled;
549                     }
550 
551                     int remaining=_chunkLength-_chunkPosition;
552 
553                     if (remaining==0)
554                     {
555                         _state=STATE_AJP13CHUNK_START;
556                         if (_contentPosition<_contentLength)
557                         {
558                             _generator.getBodyChunk();
559                         }
560                         else
561                         {
562                             _generator.gotBody();
563                         }
564 
565                         return total_filled;
566                     }
567 
568                     if (_buffer.length()<remaining)
569                     {
570                         remaining=_buffer.length();
571                     }
572 
573                     chunk=Ajp13RequestPacket.get(_buffer,(int)remaining);
574                     _contentPosition+=chunk.length();
575                     _chunkPosition+=chunk.length();
576                     _contentView.update(chunk);
577 
578                     remaining=_chunkLength-_chunkPosition;
579 
580                     if (remaining==0)
581                     {
582                         _state=STATE_AJP13CHUNK_START;
583                         if (_contentPosition<_contentLength || _contentLength == HttpTokens.UNKNOWN_CONTENT)
584                         {
585                             _generator.getBodyChunk();
586                         }
587                         else
588                         {
589                             _generator.gotBody();
590                         }
591                     }
592 
593                     _handler.content(chunk);
594 
595                 return total_filled;
596 
597             default:
598                 throw new IllegalStateException("Invalid Content State");
599 
600             }
601 
602         }
603 
604         return total_filled;
605     }
606 
607     /* ------------------------------------------------------------------------------- */
608     public void reset(boolean returnBuffers)
609     {
610         _state = STATE_START;
611         _contentLength = HttpTokens.UNKNOWN_CONTENT;
612         _contentPosition = 0;
613         _length = 0;
614         _packetLength = 0;
615 
616         if (_body != null)
617         {
618             if (_body.hasContent())
619             {
620                 _header.setMarkIndex(-1);
621                 _header.compact();
622                 // TODO if pipelined requests received after big
623                 // input - maybe this is not good?.
624                 _body.skip(_header.put(_body));
625 
626             }
627 
628             if (_body.length() == 0)
629             {
630                 if (_buffers != null && returnBuffers)
631                     _buffers.returnBuffer(_body);
632                 _body = null;
633             }
634             else
635             {
636                 _body.setMarkIndex(-1);
637                 _body.compact();
638             }
639         }
640 
641         if (_header != null)
642         {
643             _header.setMarkIndex(-1);
644             if (!_header.hasContent() && _buffers != null && returnBuffers)
645             {
646                 _buffers.returnBuffer(_header);
647                 _header = null;
648                 _buffer = null;
649             }
650             else
651             {
652                 _header.compact();
653                 _tok0.update(_header);
654                 _tok0.update(0, 0);
655                 _tok1.update(_header);
656                 _tok1.update(0, 0);
657             }
658         }
659 
660         _buffer = _header;
661     }
662 
663     /* ------------------------------------------------------------------------------- */
664     Buffer getHeaderBuffer()
665     {
666         return _buffer;
667     }
668 
669     private void shutdownRequest()
670     {
671         _state = STATE_END;
672 
673         if(!Ajp13SocketConnector.__allowShutdown)
674         {
675             Log.warn("AJP13: Shutdown Request is Denied, allowShutdown is set to false!!!");
676             return;
677         }
678 
679         if(Ajp13SocketConnector.__secretWord != null)
680         {
681             Log.warn("AJP13: Validating Secret Word");
682             try
683             {
684                 String secretWord = Ajp13RequestPacket.getString(_buffer, _tok1).toString();
685 
686                 if(!Ajp13SocketConnector.__secretWord.equals(secretWord))
687                 {
688                     Log.warn("AJP13: Shutdown Request Denied, Invalid Sercret word!!!");
689                     throw new IllegalStateException("AJP13: Secret Word is Invalid: Peer has requested shutdown but, Secret Word did not match");
690                 }
691             }
692             catch (Exception e)
693             {
694                 Log.warn("AJP13: Secret Word is Required!!!");
695                 Log.debug(e);
696                 throw new IllegalStateException("AJP13: Secret Word is Required: Peer has requested shutdown but, has not provided a Secret Word");
697             }
698 
699 
700             Log.warn("AJP13: Shutdown Request is Denied, allowShutdown is set to false!!!");
701             return;
702         }
703 
704         Log.warn("AJP13: Peer Has Requested for Shutdown!!!");
705         Log.warn("AJP13: Jetty 6 is shutting down !!!");
706         System.exit(0);
707     }
708 
709     /* ------------------------------------------------------------------------------- */
710     public interface EventHandler
711     {
712 
713         // public void shutdownRequest() throws IOException;
714         // public void cpingRequest() throws IOException;
715 
716         public void content(Buffer ref) throws IOException;
717 
718         public void headerComplete() throws IOException;
719 
720         public void messageComplete(long contextLength) throws IOException;
721 
722         public void parsedHeader(Buffer name, Buffer value) throws IOException;
723 
724         public void parsedMethod(Buffer method) throws IOException;
725 
726         public void parsedProtocol(Buffer protocol) throws IOException;
727 
728         public void parsedQueryString(Buffer value) throws IOException;
729 
730         public void parsedRemoteAddr(Buffer addr) throws IOException;
731 
732         public void parsedRemoteHost(Buffer host) throws IOException;
733 
734         public void parsedRequestAttribute(String key, Buffer value) throws IOException;
735         
736         public void parsedRequestAttribute(String key, int value) throws IOException;
737 
738         public void parsedServerName(Buffer name) throws IOException;
739 
740         public void parsedServerPort(int port) throws IOException;
741 
742         public void parsedSslSecure(boolean secure) throws IOException;
743 
744         public void parsedUri(Buffer uri) throws IOException;
745 
746         public void startForwardRequest() throws IOException;
747 
748         public void parsedAuthorizationType(Buffer authType) throws IOException;
749         
750         public void parsedRemoteUser(Buffer remoteUser) throws IOException;
751 
752         public void parsedServletPath(Buffer servletPath) throws IOException;
753         
754         public void parsedContextPath(Buffer context) throws IOException;
755 
756         public void parsedSslCert(Buffer sslCert) throws IOException;
757 
758         public void parsedSslCipher(Buffer sslCipher) throws IOException;
759 
760         public void parsedSslSession(Buffer sslSession) throws IOException;
761 
762         public void parsedSslKeySize(int keySize) throws IOException;
763 
764 
765 
766 
767 
768     }
769 
770     /* ------------------------------------------------------------ */
771     /**
772      * TODO Make this common with HttpParser
773      * 
774      */
775     public static class Input extends ServletInputStream
776     {
777         private Ajp13Parser _parser;
778         private EndPoint _endp;
779         private long _maxIdleTime;
780         private View _content;
781 
782         /* ------------------------------------------------------------ */
783         public Input(Ajp13Parser parser, long maxIdleTime)
784         {
785             _parser = parser;
786             _endp = parser._endp;
787             _maxIdleTime = maxIdleTime;
788             _content = _parser._contentView;
789         }
790 
791         /* ------------------------------------------------------------ */
792         public int read() throws IOException
793         {
794             int c = -1;
795             if (blockForContent())
796                 c = 0xff & _content.get();
797             return c;
798         }
799 
800         /* ------------------------------------------------------------ */
801         /*
802          * @see java.io.InputStream#read(byte[], int, int)
803          */
804         public int read(byte[] b, int off, int len) throws IOException
805         {
806             int l = -1;
807             if (blockForContent())
808                 l = _content.get(b, off, len);
809             return l;
810         }
811 
812         /* ------------------------------------------------------------ */
813         private boolean blockForContent() throws IOException
814         {
815             if (_content.length() > 0)
816                 return true;
817             if (_parser.isState(Ajp13Parser.STATE_END))
818                 return false;
819 
820             // Handle simple end points.
821             if (_endp == null)
822                 _parser.parseNext();
823 
824             // Handle blocking end points
825             else if (_endp.isBlocking())
826             {
827                 _parser.parseNext();
828                 
829                 // parse until some progress is made (or IOException thrown for timeout)
830                 while (_content.length() == 0 && !_parser.isState(Ajp13Parser.STATE_END))
831                 {
832                     // Try to get more _parser._content
833                     _parser.parseNext();
834                 }
835             }
836             else // Handle non-blocking end point
837             {
838                 long filled = _parser.parseNext();
839                 boolean blocked = false;
840 
841                 // parse until some progress is made (or
842                 // IOException thrown for timeout)
843                 while (_content.length() == 0 && !_parser.isState(Ajp13Parser.STATE_END))
844                 {
845                     // if fill called, but no bytes read,
846                     // then block
847                     if (filled > 0)
848                         blocked = false;
849                     else if (filled == 0)
850                     {
851                         if (blocked)
852                             throw new InterruptedIOException("timeout");
853 
854                         blocked = true;
855                         _endp.blockReadable(_maxIdleTime);
856                     }
857 
858                     // Try to get more _parser._content
859                     filled = _parser.parseNext();
860                 }
861             }
862 
863             return _content.length() > 0;
864         }
865 
866     }
867 }