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