Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/ajp/RequestHandler.java


1   /*
2    *  Copyright 1999-2004 The Apache Software Foundation
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    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  
17  package org.apache.ajp;
18  
19  import java.io.ByteArrayInputStream;
20  import java.io.IOException;
21  import java.security.cert.CertificateFactory;
22  import java.security.cert.X509Certificate;
23  
24  import org.apache.tomcat.util.buf.ByteChunk;
25  import org.apache.tomcat.util.buf.MessageBytes;
26  import org.apache.tomcat.util.http.BaseRequest;
27  import org.apache.tomcat.util.http.HttpMessages;
28  import org.apache.tomcat.util.http.MimeHeaders;
29  
30  
31  /**
32   * Handle messages related with basic request information.
33   *
34   * This object can handle the following incoming messages:
35   * - "FORWARD_REQUEST" input message ( sent when a request is passed from the web server )
36   * - "PING REQUEST" input message (sent by the web server to determine if tomcat is not frozen,
37   *                                 a PONG REPLY will be sent back)
38   * - "RECEIVE_BODY_CHUNK" input ( sent by container to pass more body, in response to GET_BODY_CHUNK )
39   *
40   * It can handle the following outgoing messages:
41   * - SEND_HEADERS. Pass the status code and headers.
42   * - SEND_BODY_CHUNK. Send a chunk of body
43   * - GET_BODY_CHUNK. Request a chunk of body data
44   * - END_RESPONSE. Notify the end of a request processing.
45   *
46   * @author Henri Gomez [hgomez@apache.org]
47   * @author Dan Milstein [danmil@shore.net]
48   * @author Keith Wannamaker [Keith@Wannamaker.org]
49   * @author Costin Manolache
50   */
51  public class RequestHandler extends AjpHandler
52  {
53      // XXX Will move to a registry system.
54      
55      // Prefix codes for message types from server to container
56    public static final byte JK_AJP13_FORWARD_REQUEST   = 2;
57    public static final byte JK_AJP13_SHUTDOWN          = 7;
58    public static final byte JK_AJP13_PING_REQUEST      = 8;    
59    public static final byte JK_AJP13_CPING_REQUEST     = 10;    
60  
61      // Prefix codes for message types from container to server
62      public static final byte JK_AJP13_SEND_BODY_CHUNK   = 3;
63      public static final byte JK_AJP13_SEND_HEADERS      = 4;
64      public static final byte JK_AJP13_END_RESPONSE      = 5;
65    public static final byte JK_AJP13_GET_BODY_CHUNK    = 6;
66    public static final byte JK_AJP13_CPONG_REPLY       = 9;
67    
68      // Integer codes for common response header strings
69      public static final int SC_RESP_CONTENT_TYPE        = 0xA001;
70      public static final int SC_RESP_CONTENT_LANGUAGE    = 0xA002;
71      public static final int SC_RESP_CONTENT_LENGTH      = 0xA003;
72      public static final int SC_RESP_DATE                = 0xA004;
73      public static final int SC_RESP_LAST_MODIFIED       = 0xA005;
74      public static final int SC_RESP_LOCATION            = 0xA006;
75      public static final int SC_RESP_SET_COOKIE          = 0xA007;
76      public static final int SC_RESP_SET_COOKIE2         = 0xA008;
77      public static final int SC_RESP_SERVLET_ENGINE      = 0xA009;
78      public static final int SC_RESP_STATUS              = 0xA00A;
79      public static final int SC_RESP_WWW_AUTHENTICATE    = 0xA00B;
80    
81      // Integer codes for common (optional) request attribute names
82      public static final byte SC_A_CONTEXT       = 1;  // XXX Unused
83      public static final byte SC_A_SERVLET_PATH  = 2;  // XXX Unused
84      public static final byte SC_A_REMOTE_USER   = 3;
85      public static final byte SC_A_AUTH_TYPE     = 4;
86      public static final byte SC_A_QUERY_STRING  = 5;
87      public static final byte SC_A_JVM_ROUTE     = 6;
88      public static final byte SC_A_SSL_CERT      = 7;
89      public static final byte SC_A_SSL_CIPHER    = 8;
90      public static final byte SC_A_SSL_SESSION   = 9;
91      public static final byte SC_A_SSL_KEY_SIZE  = 11; // ajp14 originally, now in ajp13 with jk 1.2/2.0
92      public static final byte SC_A_SECRET        = 12;
93      public static final byte SC_A_STORED_METHOD = 13;
94  
95      // Used for attributes which are not in the list above
96      public static final byte SC_A_REQ_ATTRIBUTE = 10; 
97  
98      // Terminates list of attributes
99      public static final byte SC_A_ARE_DONE      = (byte)0xFF;
100     
101     // Translates integer codes to names of HTTP methods
102     public static final String []methodTransArray = {
103         "OPTIONS",
104         "GET",
105         "HEAD",
106         "POST",
107         "PUT",
108         "DELETE",
109         "TRACE",
110         "PROPFIND",
111         "PROPPATCH",
112         "MKCOL",
113         "COPY",
114         "MOVE",
115         "LOCK",
116         "UNLOCK",
117         "ACL",
118         "REPORT",
119         "VERSION-CONTROL",
120         "CHECKIN",
121         "CHECKOUT",
122         "UNCHECKOUT",
123         "SEARCH",
124         "MKWORKSPACE",
125         "UPDATE",
126         "LABEL",
127         "MERGE",
128         "BASELINE-CONTROL",
129         "MKACTIVITY"
130     };
131     public static final int SC_M_JK_STORED = (byte) 0xFF;
132 
133     
134     // id's for common request headers
135     public static final int SC_REQ_ACCEPT          = 1;
136     public static final int SC_REQ_ACCEPT_CHARSET  = 2;
137     public static final int SC_REQ_ACCEPT_ENCODING = 3;
138     public static final int SC_REQ_ACCEPT_LANGUAGE = 4;
139     public static final int SC_REQ_AUTHORIZATION   = 5;
140     public static final int SC_REQ_CONNECTION      = 6;
141     public static final int SC_REQ_CONTENT_TYPE    = 7;
142     public static final int SC_REQ_CONTENT_LENGTH  = 8;
143     public static final int SC_REQ_COOKIE          = 9;
144     public static final int SC_REQ_COOKIE2         = 10;
145     public static final int SC_REQ_HOST            = 11;
146     public static final int SC_REQ_PRAGMA          = 12;
147     public static final int SC_REQ_REFERER         = 13;
148     public static final int SC_REQ_USER_AGENT      = 14;
149 
150     // Translates integer codes to request header names    
151     public static final String []headerTransArray = {
152         "accept",
153         "accept-charset",
154         "accept-encoding",
155         "accept-language",
156         "authorization",
157         "connection",
158         "content-type",
159         "content-length",
160         "cookie",
161         "cookie2",
162         "host",
163         "pragma",
164         "referer",
165         "user-agent"
166     };
167 
168     public RequestHandler() 
169     {
170     }
171 
172     public void init( Ajp13 ajp14 ) {
173   // register incoming message handlers
174   ajp14.registerMessageType( JK_AJP13_FORWARD_REQUEST,
175            "JK_AJP13_FORWARD_REQUEST",
176            this, null); // 2
177   // register outgoing messages handler
178   ajp14.registerMessageType( JK_AJP13_SEND_BODY_CHUNK, // 3
179            "JK_AJP13_SEND_BODY_CHUNK",
180            this,null );
181   ajp14.registerMessageType( JK_AJP13_SEND_HEADERS,  // 4
182            "JK_AJP13_SEND_HEADERS",
183            this,null );
184   ajp14.registerMessageType( JK_AJP13_END_RESPONSE, // 5
185            "JK_AJP13_END_RESPONSE",
186            this,null );
187   ajp14.registerMessageType( JK_AJP13_GET_BODY_CHUNK, // 6
188            "JK_AJP13_GET_BODY_CHUNK",
189            this, null );
190   ajp14.registerMessageType( JK_AJP13_CPING_REQUEST,
191            "JK_AJP13_PING_REQUEST",
192            this, null); // 10
193   ajp14.registerMessageType( JK_AJP13_CPONG_REPLY,
194            "JK_AJP13_PONG_REPLY",
195            this, null); // 9
196     }
197     
198     /**
199      * Send a CPONG REPLY to web server to its CPING request
200      * 
201      * @param ch the Ajp13 channel
202      * @param outBuf the Ajp13Packet output packet to use
203      */
204     public int sendCPong(Ajp13 ch, Ajp13Packet outBuf)
205     {
206     outBuf.reset();
207     outBuf.appendByte(JK_AJP13_CPONG_REPLY);
208       
209       try
210       {
211       ch.send(outBuf);
212       }
213       catch (IOException ioe)
214       {
215         log("can't send pong reply");
216       }
217       
218       return (999);  // success but no need to process farther
219     }
220     
221     // -------------------- Incoming message --------------------
222     public int handleAjpMessage( int type, Ajp13 channel,
223          Ajp13Packet ajp, BaseRequest req )
224   throws IOException
225     {
226   switch( type ) {
227   case RequestHandler.JK_AJP13_FORWARD_REQUEST:
228       return decodeRequest(channel, channel.hBuf, req );
229     
230   default:
231       return UNKNOWN;
232   }
233     }
234     
235     /**
236      * Parse a FORWARD_REQUEST packet from the web server and store its
237      * properties in the passed-in request object.
238      *
239      * @param req An empty (newly-recycled) request object.
240      * @param msg Holds the packet which has just been sent by the web
241      * server, with its read position just past the packet header (which in
242      * this case includes the prefix code for FORWARD_REQUEST).
243      *
244      * @return 200 in case of a successful decoduing, 500 in case of error.  
245      */
246     protected int decodeRequest(Ajp13 ch, Ajp13Packet msg, BaseRequest req)
247         throws IOException
248     {
249         
250         if (debug > 0) {
251             log("decodeRequest()");
252         }
253 
254   // XXX Awful return values
255 
256         boolean isSSL = false;
257 
258         // Translate the HTTP method code to a String.
259         byte methodCode = msg.getByte();
260         if (methodCode != SC_M_JK_STORED)
261           req.method().setString(methodTransArray[(int)methodCode - 1]);
262 
263         msg.getMessageBytes(req.protocol()); 
264         msg.getMessageBytes(req.requestURI());
265 
266         msg.getMessageBytes(req.remoteAddr());
267         msg.getMessageBytes(req.remoteHost());
268         msg.getMessageBytes(req.serverName());
269         req.setServerPort(msg.getInt());
270 
271   isSSL = msg.getBool();
272 
273   // Decode headers
274   MimeHeaders headers = req.headers();
275   int hCount = msg.getInt();
276         for(int i = 0 ; i < hCount ; i++) {
277             String hName = null;
278 
279       // Header names are encoded as either an integer code starting
280       // with 0xA0, or as a normal string (in which case the first
281       // two bytes are the length).
282             int isc = msg.peekInt();
283             int hId = isc & 0xFF;
284 
285       MessageBytes vMB=null;
286             isc &= 0xFF00;
287             if(0xA000 == isc) {
288                 //
289                 // header name is encoded as an int
290                 //
291                 msg.getInt(); // To advance the read position
292                 hName = headerTransArray[hId - 1];
293     vMB= headers.addValue(hName);
294                 msg.getMessageBytes(vMB);
295 
296                 if (hId == SC_REQ_CONTENT_LENGTH) {
297                     // just read content-length header
298                     int contentLength = (vMB == null) ? -1 : vMB.getInt();
299                     req.setContentLength(contentLength);
300                 } else if (hId == SC_REQ_CONTENT_TYPE) {
301                     // just read content-type header
302                     ByteChunk bchunk = vMB.getByteChunk();
303                     req.contentType().setBytes(bchunk.getBytes(),
304                                                bchunk.getOffset(),
305                                                bchunk.getLength());
306                 } else if (hId == SC_REQ_AUTHORIZATION) {
307                     ByteChunk bchunk = vMB.getByteChunk();
308                     req.authorization().setBytes(bchunk.getBytes(),
309                                                bchunk.getOffset(),
310                                                bchunk.getLength());
311                 }
312             } else {
313                 //
314                 // header name is a string
315                 //
316     // XXX Not very elegant
317     vMB = msg.addHeader(headers);
318     if (vMB == null) {
319                     return 500; // wrong packet
320                 }
321                 msg.getMessageBytes(vMB);
322             }
323         }
324 
325   byte attributeCode;
326         for(attributeCode = msg.getByte() ;
327             attributeCode != SC_A_ARE_DONE ;
328             attributeCode = msg.getByte()) {
329             switch(attributeCode) {
330       case SC_A_CONTEXT      :
331                 break;
332     
333       case SC_A_SERVLET_PATH :
334                 break;
335     
336       case SC_A_REMOTE_USER  :
337                 msg.getMessageBytes(req.remoteUser());
338                 break;
339     
340       case SC_A_AUTH_TYPE    :
341                 msg.getMessageBytes(req.authType());
342                 break;
343     
344       case SC_A_QUERY_STRING :
345     msg.getMessageBytes(req.queryString());
346                 break;
347     
348       case SC_A_JVM_ROUTE    :
349                 msg.getMessageBytes(req.jvmRoute());
350                 break;
351     
352       case SC_A_SSL_CERT     :
353     isSSL = true;
354                 // Transform the string into certificate.
355                 String certString = msg.getString();
356                 byte[] certData = certString.getBytes();
357                 ByteArrayInputStream bais = new ByteArrayInputStream(certData);
358  
359                 // Fill the first element.
360                 X509Certificate jsseCerts[] = null;
361                 try {
362                     CertificateFactory cf =
363                         CertificateFactory.getInstance("X.509");
364                     X509Certificate cert = (X509Certificate)
365                         cf.generateCertificate(bais);
366                     jsseCerts =  new X509Certificate[1];
367                     jsseCerts[0] = cert;
368                 } catch(java.security.cert.CertificateException e) {
369                     log("Certificate convertion failed" + e );
370                 }
371  
372                 req.setAttribute("javax.servlet.request.X509Certificate",
373                                  jsseCerts);
374                 break;
375     
376       case SC_A_SSL_CIPHER   :
377     isSSL = true;
378     req.setAttribute("javax.servlet.request.cipher_suite",
379          msg.getString());
380                 break;
381     
382       case SC_A_SECRET   :
383                 // If a request has a secret attribute, set it on
384                 // channel - it'll be visible to the caller ( Interceptor,
385                 // Connector ) and it can check it against its settings before
386                 // trusting us.
387                 String secret=msg.getString();
388                 if(secret!=null) {
389                     ch.setSecret( secret );
390                 }
391                 break;
392     
393       case SC_A_SSL_SESSION  :
394     isSSL = true;
395     req.setAttribute("javax.servlet.request.ssl_session",
396           msg.getString());
397                 break;
398     
399       case SC_A_REQ_ATTRIBUTE :
400     req.setAttribute(msg.getString(), 
401          msg.getString());
402                 break;
403 
404       case SC_A_SSL_KEY_SIZE: // Ajp13 !
405                 isSSL = true;
406     req.setAttribute("javax.servlet.request.key_size",
407          Integer.toString(msg.getInt()));
408     break;
409     
410         case SC_A_STORED_METHOD:
411                 req.method().setString(msg.getString());
412                 break;
413     
414       default:
415                 // Ignore. Assume a single-string value - we shouldn't
416                 // allow anything else.
417                 msg.getString();
418                 break;
419       }
420         }
421 
422         if(isSSL) {
423             req.setScheme(req.SCHEME_HTTPS);
424             req.setSecure(true);
425         }
426 
427         // set cookies on request now that we have all headers
428         req.cookies().setHeaders(req.headers());
429 
430   // Check to see if there should be a body packet coming along
431   // immediately after
432       if(req.getContentLength() > 0) {
433 
434       /* Read present data */
435       int err = ch.receive(ch.inBuf);
436             if(err < 0) {
437               return 500;
438       }
439       
440       ch.blen = ch.inBuf.peekInt();
441       ch.pos = 0;
442       ch.inBuf.getBytes(ch.bodyBuff);
443       }
444     
445         if (debug > 5) {
446             log(req.toString());
447         }
448 
449         return 200; // Success
450     }
451     
452 
453     // -------------------- Messages from container to server ------------------
454     
455     /**
456      * Send the HTTP headers back to the web server and on to the browser.
457      *
458      * @param status The HTTP status code to send.
459      * @param statusMessage the HTTP status message to send.
460      * @param headers The set of all headers.
461      */
462     public void sendHeaders(Ajp13 ch, Ajp13Packet outBuf,
463           int status, String statusMessage,
464                             MimeHeaders headers)
465         throws IOException
466     {
467   // XXX if more headers that MAX_SIZE, send 2 packets!
468   if( statusMessage==null ) statusMessage=HttpMessages.getMessage(status);
469   outBuf.reset();
470         outBuf.appendByte(JK_AJP13_SEND_HEADERS);
471         outBuf.appendInt(status);
472   
473   outBuf.appendString(statusMessage);
474         
475   int numHeaders = headers.size();
476         outBuf.appendInt(numHeaders);
477         
478   for( int i=0 ; i < numHeaders ; i++ ) {
479       String headerName = headers.getName(i).toString();
480       int sc = headerNameToSc(headerName);
481             if(-1 != sc) {
482                 outBuf.appendInt(sc);
483             } else {
484                 outBuf.appendString(headerName);
485             }
486             outBuf.appendString(headers.getValue(i).toString() );
487         }
488 
489         outBuf.end();
490         ch.send(outBuf);
491     } 
492 
493 
494     /**
495      * Signal the web server that the servlet has finished handling this
496      * request, and that the connection can be reused.
497      */
498     public void finish(Ajp13 ch, Ajp13Packet outBuf) throws IOException {
499         if (debug > 0)  log("finish()");
500 
501   outBuf.reset();
502         outBuf.appendByte(JK_AJP13_END_RESPONSE);
503         outBuf.appendBool(true); // Reuse this connection
504         outBuf.end();
505         ch.send(outBuf);
506     }
507 
508     /**
509      * Send a chunk of response body data to the web server and on to the
510      * browser.
511      *
512      * @param b A huffer of bytes to send.
513      * @param off The offset into the buffer from which to start sending.
514      * @param len The number of bytes to send.
515      */    
516     public void doWrite(Ajp13 ch, Ajp13Packet outBuf,
517       byte b[], int off, int len)
518   throws IOException
519     {
520         if (debug > 0) log("doWrite(byte[], " + off + ", " + len + ")");
521 
522   int sent = 0;
523   while(sent < len) {
524       int to_send = len - sent;
525       to_send = to_send > Ajp13.MAX_SEND_SIZE ? Ajp13.MAX_SEND_SIZE : to_send;
526 
527       outBuf.reset();
528       outBuf.appendByte(JK_AJP13_SEND_BODY_CHUNK);            
529       outBuf.appendBytes(b, off + sent, to_send);          
530       ch.send(outBuf);
531       sent += to_send;
532   }
533     }
534 
535     // -------------------- Utils -------------------- 
536 
537     /**
538      * Translate an HTTP response header name to an integer code if
539      * possible.  Case is ignored.
540      * 
541      * @param name The name of the response header to translate.
542      *
543      * @return The code for that header name, or -1 if no code exists.
544      */
545     protected int headerNameToSc(String name)
546     {       
547         switch(name.charAt(0)) {
548   case 'c':
549   case 'C':
550       if(name.equalsIgnoreCase("Content-Type")) {
551     return SC_RESP_CONTENT_TYPE;
552       } else if(name.equalsIgnoreCase("Content-Language")) {
553     return SC_RESP_CONTENT_LANGUAGE;
554       } else if(name.equalsIgnoreCase("Content-Length")) {
555     return SC_RESP_CONTENT_LENGTH;
556       }
557             break;
558             
559   case 'd':
560   case 'D':
561       if(name.equalsIgnoreCase("Date")) {
562                 return SC_RESP_DATE;
563       }
564             break;
565             
566   case 'l':
567   case 'L':
568       if(name.equalsIgnoreCase("Last-Modified")) {
569     return SC_RESP_LAST_MODIFIED;
570       } else if(name.equalsIgnoreCase("Location")) {
571     return SC_RESP_LOCATION;
572       }
573             break;
574 
575   case 's':
576   case 'S':
577       if(name.equalsIgnoreCase("Set-Cookie")) {
578     return SC_RESP_SET_COOKIE;
579       } else if(name.equalsIgnoreCase("Set-Cookie2")) {
580     return SC_RESP_SET_COOKIE2;
581       }
582             break;
583             
584   case 'w':
585   case 'W':
586       if(name.equalsIgnoreCase("WWW-Authenticate")) {
587     return SC_RESP_WWW_AUTHENTICATE;
588       }
589             break;          
590         }
591         
592         return -1;
593     }
594    
595     private int debug=0;
596     private Logger logger = new Logger();
597     
598     public void setDebug(int debug) {
599         this.debug = debug;
600     }
601 
602     public void setLogger(Logger l) {
603         this.logger = l;
604     }
605     
606     void log(String s) {
607         logger.log("[RequestHandler] " + s );
608     }
609 
610     // ==================== Servlet Input Support =================
611     // XXX DEPRECATED
612     
613     public int available(Ajp13 ch) throws IOException {
614         if (debug > 0) {
615             log("available()");
616         }
617 
618         if (ch.pos >= ch.blen) {
619             if( ! refillReadBuffer(ch)) {
620     return 0;
621       }
622         }
623         return ch.blen - ch.pos;
624     }
625 
626     /**
627      * Return the next byte of request body data (to a servlet).
628      */
629     public int doRead(Ajp13 ch) throws IOException 
630     {
631         if (debug > 0) {
632             log("doRead()");
633         }
634 
635         if(ch.pos >= ch.blen) {
636             if( ! refillReadBuffer(ch)) {
637     return -1;
638       }
639         }
640         return ch.bodyBuff[ch.pos++] & 0xFF;
641     }
642     
643     /**
644      * Store a chunk of request data into the passed-in byte buffer.
645      *
646      * @param b A buffer to fill with data from the request.
647      * @param off The offset in the buffer at which to start filling.
648      * @param len The number of bytes to copy into the buffer.
649      *
650      * @return The number of bytes actually copied into the buffer, or -1
651      * if the end of the stream has been reached.
652      */
653     public int doRead(Ajp13 ch, byte[] b, int off, int len) throws IOException 
654     {
655         if (debug > 0) {
656             log("doRead(byte[], int, int)");
657         }
658 
659   if(ch.pos >= ch.blen) {
660       if( ! refillReadBuffer(ch)) {
661     return -1;
662       }
663   }
664 
665   if(ch.pos + len <= ch.blen) { // Fear the off by one error
666       // Sanity check b.length > off + len?
667       System.arraycopy(ch.bodyBuff, ch.pos, b, off, len);
668       ch.pos += len;
669       return len;
670   }
671 
672   // Not enough data (blen < pos + len)
673   int toCopy = len;
674   while(toCopy > 0) {
675       int bytesRemaining = ch.blen - ch.pos;
676       if(bytesRemaining < 0) 
677     bytesRemaining = 0;
678       int c = bytesRemaining < toCopy ? bytesRemaining : toCopy;
679             
680       System.arraycopy(ch.bodyBuff, ch.pos, b, off, c);
681 
682       toCopy    -= c;
683 
684       off       += c;
685       ch.pos       += c; // In case we exactly consume the buffer
686 
687       if(toCopy > 0) 
688     if( ! refillReadBuffer(ch)) { // Resets blen and pos
689         break;
690     }
691   }
692 
693   return len - toCopy;
694     }
695     
696     /**
697      * Get more request body data from the web server and store it in the 
698      * internal buffer.
699      *
700      * @return true if there is more data, false if not.    
701      */
702     public boolean refillReadBuffer(Ajp13 ch) throws IOException 
703     {
704         if (debug > 0) {
705             log("refillReadBuffer()");
706         }
707 
708   // If the server returns an empty packet, assume that that end of
709   // the stream has been reached (yuck -- fix protocol??).
710 
711   // Why not use outBuf??
712   ch.inBuf.reset();
713   ch.inBuf.appendByte(JK_AJP13_GET_BODY_CHUNK);
714   ch.inBuf.appendInt(Ajp13.MAX_READ_SIZE);
715   ch.send(ch.inBuf);
716   
717   int err = ch.receive(ch.inBuf);
718         if(err < 0) {
719       throw new IOException();
720   }
721   
722         // check for empty packet, which means end of stream
723         if (ch.inBuf.getLen() == 0) {
724             if (debug > 0) {
725                 log("refillReadBuffer():  "
726                     + "received empty packet -> end of stream");
727             }
728             ch.blen = 0;
729             ch.pos = 0;
730             return false;
731         }
732 
733       ch.blen = ch.inBuf.peekInt();
734       ch.pos = 0;
735       ch.inBuf.getBytes(ch.bodyBuff);
736 
737   return (ch.blen > 0);
738     }    
739 
740     // ==================== Servlet Output Support =================
741     
742     /**
743      */
744     public void beginSendHeaders(Ajp13 ch, Ajp13Packet outBuf,
745          int status,
746                                  String statusMessage,
747                                  int numHeaders) throws IOException {
748 
749         if (debug > 0) {
750             log("sendHeaders()");
751         }
752 
753   // XXX if more headers that MAX_SIZE, send 2 packets!
754 
755   outBuf.reset();
756         outBuf.appendByte(JK_AJP13_SEND_HEADERS);
757 
758         if (debug > 0) {
759             log("status is:  " + status +
760                        "(" + statusMessage + ")");
761         }
762 
763         // set status code and message
764         outBuf.appendInt(status);
765         outBuf.appendString(statusMessage);
766 
767         // write the number of headers...
768         outBuf.appendInt(numHeaders);
769     }
770 
771     public void sendHeader(Ajp13Packet outBuf,
772          String name, String value)
773   throws IOException
774     {
775         int sc = headerNameToSc(name);
776         if(-1 != sc) {
777             outBuf.appendInt(sc);
778         } else {
779             outBuf.appendString(name);
780         }
781         outBuf.appendString(value);
782     }
783 
784     public void endSendHeaders(Ajp13 ch, Ajp13Packet outBuf)
785   throws IOException
786     {
787         outBuf.end();
788         ch.send(outBuf);
789     }
790 
791     /**
792      * Send the HTTP headers back to the web server and on to the browser.
793      *
794      * @param status The HTTP status code to send.
795      * @param headers The set of all headers.
796      */
797     public void sendHeaders(Ajp13 ch, Ajp13Packet outBuf,
798           int status, MimeHeaders headers)
799         throws IOException
800     {
801         sendHeaders(ch, outBuf, status, HttpMessages.getMessage(status),
802                     headers);
803     }
804     
805 
806  }