You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by co...@apache.org on 2001/12/18 23:16:37 UTC

cvs commit: jakarta-tomcat-connectors/jk/java/org/apache/jk Ajp13.java Ajp13Packet.java AjpHandler.java NegociationHandler.java RequestHandler.java

costin      01/12/18 14:16:37

  Added:       jk/java/org/apache/jk Ajp13.java Ajp13Packet.java
                        AjpHandler.java NegociationHandler.java
                        RequestHandler.java
  Log:
  Adding the files from org.apache.ajp.
  
  This is the same code, as startup point for jk2 and refactoring.
  
  Revision  Changes    Path
  1.1                  jakarta-tomcat-connectors/jk/java/org/apache/jk/Ajp13.java
  
  Index: Ajp13.java
  ===================================================================
  /*
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * [Additional notices, if required by prior licensing conditions]
   *
   */
  
  package org.apache.jk;
  
  import java.io.IOException;
  import java.io.UnsupportedEncodingException;
  import java.io.InputStream;
  import java.io.OutputStream;
  import java.io.ByteArrayInputStream;
  
  import java.net.Socket;
  import java.util.Enumeration;
  import java.security.cert.X509Certificate;
  import java.security.cert.CertificateFactory;
  
  import org.apache.tomcat.util.buf.MessageBytes;
  import org.apache.tomcat.util.buf.ByteChunk;
  import org.apache.tomcat.util.http.MimeHeaders;
  import org.apache.tomcat.util.http.HttpMessages;
  import org.apache.tomcat.util.http.BaseRequest;
  
  /**
   * Represents a single, persistent connection between the web server and
   * the servlet container.  Uses the Apache JServ Protocol version 1.3 for
   * communication.  Because this protocal does not multiplex requests, this
   * connection can only be associated with a single request-handling cycle
   * at a time.<P>
   *
   * This class contains knowledge about how an individual packet is laid out
   * (via the <CODE>Ajp13Packet</CODE> class), and also about the
   * stages of communicaton between the server and the servlet container.  It
   * translates from Tomcat's internal servlet support methods
   * (e.g. doWrite) to the correct packets to send to the web server.
   *
   * @see Ajp13Interceptor 
   *
   * @author Dan Milstein [danmil@shore.net]
   * @author Keith Wannamaker [Keith@Wannamaker.org]
   * @author Kevin Seguin [seguin@apache.org]
   * @author Henri Gomez [hgomez@slib.fr]
   * @author Costin Manolache
   */
  public class Ajp13 {
  
      public static final int MAX_PACKET_SIZE=8192;
      public static final int H_SIZE=4;  // Size of basic packet header
  
      public static final int  MAX_READ_SIZE = MAX_PACKET_SIZE - H_SIZE - 2;
      public static final int  MAX_SEND_SIZE = MAX_PACKET_SIZE - H_SIZE - 4;
  
      // Prefix codes for message types from server to container
      public static final byte JK_AJP13_SHUTDOWN          = 7;
  	
      // Error code for Ajp13
      public static final int  JK_AJP13_BAD_HEADER        = -100;
      public static final int  JK_AJP13_NO_HEADER         = -101;
      public static final int  JK_AJP13_COMM_CLOSED       = -102;
      public static final int  JK_AJP13_COMM_BROKEN       = -103;
      public static final int  JK_AJP13_BAD_BODY          = -104;
      public static final int  JK_AJP13_INCOMPLETE_BODY   = -105;
  
      // ============ Instance Properties ====================
  
      OutputStream out;
      InputStream in;
  
      // Buffer used of output body and headers
      public Ajp13Packet outBuf = new Ajp13Packet( MAX_PACKET_SIZE );
      // Buffer used for input body
      Ajp13Packet inBuf  = new Ajp13Packet( MAX_PACKET_SIZE );
      // Buffer used for request head ( and headers )
      Ajp13Packet hBuf=new Ajp13Packet( MAX_PACKET_SIZE );
  
      // Holds incoming reads of request body data (*not* header data)
      byte []bodyBuff = new byte[MAX_READ_SIZE];
      
      int blen;  // Length of current chunk of body data in buffer
      int pos;   // Current read position within that buffer
  
      boolean end_of_stream;  // true if we've received an empty packet
      
      // Required handler - essential request processing
      public RequestHandler reqHandler;
      // AJP14 - detect protocol,set communication parameters, login
      // If no password is set, use only Ajp13 messaging
      boolean backwardCompat=true;
      boolean logged=false;
      
      public Ajp13() {
  	super();
  	initBuf();
          reqHandler=new RequestHandler();
  	reqHandler.init( this );
      }
  
      public Ajp13(RequestHandler reqHandler ) {
  	super();
  	initBuf();
          this.reqHandler=reqHandler;
  	reqHandler.init( this );
      }
  
      /** Will be overriden
       */
      public void initBuf() {
  	outBuf = new Ajp13Packet( MAX_PACKET_SIZE );
  	inBuf  = new Ajp13Packet( MAX_PACKET_SIZE );
  	hBuf=new Ajp13Packet( MAX_PACKET_SIZE );
      }
      
      public void recycle() {
          if (debug > 0) {
              logger.log("recycle()");
          }
  
          // This is a touch cargo-cultish, but I think wise.
          blen = 0; 
          pos = 0;
          end_of_stream = false;
          logged=false;
      }
      
      /**
       * Associate an open socket with this instance.
       */
      public void setSocket( Socket socket ) throws IOException {
          if (debug > 0) {
              logger.log("setSocket()");
          }
          
  	socket.setSoLinger( true, 100);
  	out = socket.getOutputStream();
  	in  = socket.getInputStream();
  	pos = 0;
      }
  
      /**
       * Backward compat mode, no login  needed
       */
      public void setBackward(boolean b) 
      {
          backwardCompat=b;
      }
  
      public boolean isLogged() {
  	return logged;
      }
  
      void setLogged( boolean b ) {
          logged=b;
      }
  
      // -------------------- Handlers registry --------------------
  
      static final int MAX_HANDLERS=32;
      static final int RESERVED=16;  // reserved names, backward compat
  
      // Note that we don't make distinction between in and out
      // messages ( i.e. one id is used only in one direction )
      AjpHandler handlers[]=new AjpHandler[MAX_HANDLERS];
      String handlerName[]=new String[MAX_HANDLERS];
      int currentId=RESERVED;
  
      public int registerMessageType( int id, String name, AjpHandler h,
  				    String sig[] )
      {
  	if( id < 0 ) {
  	    // try to find it by name
  	    for( int i=0; i< handlerName.length; i++ )
  		if( name.equals( handlerName[i] ) ) return i;
  	    handlerName[currentId]=name;
  	    handlers[currentId]=h;
  	    currentId++;
  	    return currentId;
  	}
  	// fixed id
  	handlerName[id]=name;
  	handlers[id]=h;
  	return id;
      }
      
      // -------------------- Main dispatch --------------------
      
      /**
       * Read a new packet from the web server and decode it.  If it's a
       * forwarded request, store its properties in the passed-in AjpRequest
       * object.
       *
       * @param req An empty (newly-recycled) request object.
       * 
       * @return 200 in case of a successful read of a forwarded request, 500
       * if there were errors in the reading of the request, and -2 if the
       * server is asking the container to shut itself down.  
       */
      public int receiveNextRequest(BaseRequest req) throws IOException {
          if (debug > 0) {
              logger.log("receiveNextRequest()");
          }
          
          // XXX The return values are awful.
  
          int err = 0;
  
          // if we receive an IOException here, it must be because
          // the remote just closed the ajp13 connection, and it's not
          // an error, we just need to close the AJP13 connection
          try {
              err = receive(hBuf);
          } catch (IOException ioe) {
              if(debug >0 ) logger.log( "IOException receiving message ");
              return -1;  // Indicate it's a disconnection from the remote end
          }
          
  	if(err < 0) {
  	    if(debug >0 ) logger.log( "Error receiving message ");
  	    return 500;
  	}
  	
  	int type = (int)hBuf.getByte();
          //        System.out.println("XXX " + this );
          return handleMessage( type, hBuf, req );
      }
  
      /** Override for ajp14, temporary
       */
      public int handleMessage( int type, Ajp13Packet hBuf, BaseRequest req )
          throws IOException
      {
          if( type > handlers.length ) {
  	    logger.log( "Invalid handler " + type );
  	    return 500;
  	}
  
          if( debug > 0 )
              logger.log( "Received " + type + " " + handlerName[type]);
          
          // Ajp14, unlogged
  	if( ! backwardCompat && ! isLogged() ) {
  	    if( type != NegociationHandler.JK_AJP14_LOGINIT_CMD &&
  		type != NegociationHandler.JK_AJP14_LOGCOMP_CMD ) {
  
                  logger.log( "Ajp14 error: not logged " +
                              type + " " + handlerName[type]);
  
  		return 300;
  	    }
  	    // else continue
  	}
  
          // Ajp13 messages
  	switch(type) {
  	case RequestHandler.JK_AJP13_FORWARD_REQUEST:
  	    return reqHandler.decodeRequest(this, hBuf, req);
  	    
  	case JK_AJP13_SHUTDOWN:
  	    return -2;
  	}
  
  	// logged || loging message
  	AjpHandler handler=handlers[type];
  	if( handler==null ) {
  	    logger.log( "Unknown message " + type + handlerName[type] );
  	    return 200;
  	}
  
          if( debug > 0 )
              logger.log( "Ajp14 handler " + handler );
  	return handler.handleAjpMessage( type, this, hBuf, req );
      }
  
      // ==================== Servlet Input Support =================
  
      /** @deprecated -- Will use reqHandler, make sure nobody else
       calls this */
  
      
      public int available() throws IOException {
          return reqHandler.available(this);
      }
  
      public int doRead() throws IOException 
      {
          return reqHandler.doRead( this );
      }
      
      public int doRead(byte[] b, int off, int len) throws IOException 
      {
          return reqHandler.doRead( this, b, off, len );
      }
      
      private boolean refillReadBuffer() throws IOException 
      {
          return reqHandler.refillReadBuffer(this);
      }    
  
      public void beginSendHeaders(int status,
                                   String statusMessage,
                                   int numHeaders) throws IOException {
          reqHandler.beginSendHeaders( this, outBuf,
                                           status, statusMessage,
                                           numHeaders);
      }        
  
      public void sendHeader(String name, String value) throws IOException {
          reqHandler.sendHeader(  outBuf, name, value );
      }
  
      public void endSendHeaders() throws IOException {
          reqHandler.endSendHeaders(this, outBuf);
      }
  
      public void sendHeaders(int status, MimeHeaders headers)
          throws IOException
      {
          reqHandler.sendHeaders(this, outBuf, status,
                                     HttpMessages.getMessage(status),
                                     headers);
      }
  
      public void sendHeaders(int status, String statusMessage,
                              MimeHeaders headers)
          throws IOException
      {
          reqHandler.sendHeaders( this, outBuf, status,
                                      statusMessage, headers );
      }
  
      public void finish() throws IOException {
          reqHandler.finish(this, outBuf );
      }
  
      public void doWrite(byte b[], int off, int len) throws IOException {
          reqHandler.doWrite( this, outBuf, b, off, len );
      }
      
  
      // ========= Internal Packet-Handling Methods =================
  
      /**
       * Read N bytes from the InputStream, and ensure we got them all
       * Under heavy load we could experience many fragmented packets
       * just read Unix Network Programming to recall that a call to
       * read didn't ensure you got all the data you want
       *
       * from read() Linux manual
       *
       * On success, the number of bytes read is returned (zero indicates end of file),
       * and the file position is advanced by this number.
       * It is not an error if this number is smaller than the number of bytes requested;
       * this may happen for example because fewer bytes
       * are actually available right now (maybe because we were close to end-of-file,
       * or because we are reading from a pipe, or  from  a
       * terminal),  or  because  read()  was interrupted by a signal.
       * On error, -1 is returned, and errno is set appropriately. In this
       * case it is left unspecified whether the file position (if any) changes.
       *
       **/
      private int readN(InputStream in, byte[] b, int offset, int len) throws IOException {
          int pos = 0;
          int got;
  
          while(pos < len) {
              got = in.read(b, pos + offset, len - pos);
  
              if (debug > 10) {
                  logger.log("read got # " + got);
              }
  
              // connection just closed by remote. 
              if (got <= 0) {
                  // This happens periodically, as apache restarts
                  // periodically.
                  // It should be more gracefull ! - another feature for Ajp14 
                  return JK_AJP13_COMM_BROKEN;
              }
  
              pos += got;
          }
          return pos;
       }
  
      /**
       * Read in a packet from the web server and store it in the passed-in
       * <CODE>Ajp13Packet</CODE> object.
       *
       * @param msg The object into which to store the incoming packet -- any
       * current contents will be overwritten.
       *
       * @return The number of bytes read on a successful read or -1 if there 
       * was an error.
       **/
      public int receive(Ajp13Packet msg) throws IOException {
          if (debug > 0) {
              logger.log("receive()");
          }
  
  	// XXX If the length in the packet header doesn't agree with the
  	// actual number of bytes read, it should probably return an error
  	// value.  Also, callers of this method never use the length
  	// returned -- should probably return true/false instead.
  	byte b[] = msg.getBuff();
  	
          int rd = readN(in, b, 0, H_SIZE );
          
          // XXX - connection closed (JK_AJP13_COMM_CLOSED)
          //     - connection broken (JK_AJP13_COMM_BROKEN)
          //
          if(rd < 0) {
              // Most likely normal apache restart.
              return rd;
          }
          
  	int len = msg.checkIn();
  	if( debug > 5 )
              logger.log( "Received " + rd + " " + len + " " + b[0] );
          
  	// XXX check if enough space - it's assert()-ed !!!
          
   	int total_read = 0;
          
          total_read = readN(in, b, H_SIZE, len);
          
          if (total_read <= 0) {
              logger.log("can't read body, waited #" + len);
              return JK_AJP13_BAD_BODY;
          }
          
          if (total_read != len) {
              logger.log( "incomplete read, waited #" + len +
                          " got only " + total_read);
              return JK_AJP13_INCOMPLETE_BODY;
          }
          
          if (debug > 0)
              logger.log("receive:  total read = " + total_read);
  	return total_read;
      }
      
      /**
       * Send a packet to the web server.  Works for any type of message.
       *
       * @param msg A packet with accumulated data to send to the server --
       * this method will write out the length in the header.  
       */
      public void send( Ajp13Packet msg ) throws IOException {
          if (debug > 0) {
              logger.log("send()");
          }
  
  	msg.end(); // Write the packet header
  	byte b[] = msg.getBuff();
  	int len  = msg.getLen();
  
          if (debug > 5 )
              logger.log("send() " + len + " " + b[0] );
  
  	out.write( b, 0, len );
      }
  	
      /**
       * Close the socket connection to the web server.  In general, sockets
       * are maintained across many requests, so this will not be called
       * after finish().  
       *
       * @see Ajp13Interceptor#processConnection
       */
      public void close() throws IOException {
          if (debug > 0) {
              logger.log("close()");
          }
  
  	if(null != out) {        
  	    out.close();
  	}
  	if(null !=in) {
  	    in.close();
  	}
          setLogged( false );	// no more logged now 
      }
  
      // -------------------- Debug --------------------
      protected int debug = 0;
      
      public void setDebug(int debug) {
          this.debug = debug;
      }
  
      public void setLogger(Logger l) {
          this.logger = l;
      }
  
      /**
       * XXX place holder...
       */
      Logger logger = new Logger();
      public static class Logger {
          void log(String msg) {
              System.out.println("[Ajp13] " + msg);
          }
          
          void log(String msg, Throwable t) {
              System.out.println("[Ajp13] " + msg);
              t.printStackTrace(System.out);
          }
      }
  }
  
  
  
  1.1                  jakarta-tomcat-connectors/jk/java/org/apache/jk/Ajp13Packet.java
  
  Index: Ajp13Packet.java
  ===================================================================
  /*
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * [Additional notices, if required by prior licensing conditions]
   *
   */
  package org.apache.jk;
  
  import java.io.IOException;
  import java.io.UnsupportedEncodingException;
  import java.io.InputStream;
  import java.io.OutputStream;
  import java.net.Socket;
  import java.util.Enumeration;
  import java.security.*;
  
  import org.apache.tomcat.util.http.MimeHeaders;
  import org.apache.tomcat.util.buf.MessageBytes;
  import org.apache.tomcat.util.http.HttpMessages;
  import org.apache.tomcat.util.buf.HexUtils;
  
  
  /**
   * A single packet for communication between the web server and the
   * container.  Designed to be reused many times with no creation of
   * garbage.  Understands the format of data types for these packets.
   * Can be used (somewhat confusingly) for both incoming and outgoing
   * packets.  
   *
   * @see Ajp14/Ajp13Packet 
   *
   * @author Henri Gomez [hgomez@slib.fr]
   * @author Dan Milstein [danmil@shore.net]
   * @author Keith Wannamaker [Keith@Wannamaker.org]
   * @author Kevin Seguin
   * @author Costin Manolache
   */
  public class Ajp13Packet {
  
      public static final String DEFAULT_CHAR_ENCODING = "8859_1";
      public static final int    AJP13_WS_HEADER       = 0x1234;
      public static final int    AJP13_SW_HEADER       = 0x4142;  // 'AB'
  
      /**
       * encoding to use when converting byte[] <-> string
       */
      String encoding = DEFAULT_CHAR_ENCODING;
  
      /**
       * Holds the bytes of the packet
       */
      byte buff[];
  
      /**
       * The current read or write position in the buffer
       */
      int pos;    
  
      /**
       * This actually means different things depending on whether the
       * packet is read or write.  For read, it's the length of the
       * payload (excluding the header).  For write, it's the length of
       * the packet as a whole (counting the header).  Oh, well.
       */
      int len; 
  
      /**
       * Create a new packet with an internal buffer of given size.
       * @param size packet size
       */
      public Ajp13Packet( int size ) {
          buff = new byte[size];
      }
  
      /**
       * Create a new packet with given bytes
       * @param b this packet's bytes.
       */
      public Ajp13Packet( byte b[] ) {
          buff = b;
      }
  
      /**
       * Set the encoding to use for byte[] <-> string
       * conversions.
       * @param encoding the encoding to use.
       */
      public void setEncoding(String encoding) {
          this.encoding = encoding;
      }
  
      /**
       * Get the encoding used for byte[] <-> string
       * conversions.
       * @return the encoding used.
       */
      public String getEncoding() {
          return encoding;
      }
  
      /**
       * Get the internal buffer
       * @return internal buffer
       */
      public byte[] getBuff() {
          return buff;
      }
  
      /**
       * Get length.
       * @return length -- This actually means different things depending on whether the
       *                   packet is read or write.  For read, it's the length of the
       *                   payload (excluding the header).  For write, it's the length of
       *                   the packet as a whole (counting the header).  Oh, well.
       */
      public int getLen() {
          return len;
      }
  
      /**
       * Get offset into internal buffer.
       * @return offset
       */
      public int getByteOff() {
          return pos;
      }
  
      /**
       * Set offset into internal buffer.
       * @param c new offset
       */
      public void setByteOff(int c) {
          pos=c;
      }
  
      /** 
       * Parse the packet header for a packet sent from the web server to
       * the container.  Set the read position to immediately after
       * the header.
       *
       * @return The length of the packet payload, as encoded in the
       * header, or -1 if the packet doesn't have a valid header.  
       */
      public int checkIn() {
          pos = 0;
          int mark = getInt();
          len      = getInt();
  	    
          if( mark != AJP13_WS_HEADER ) {
              // XXX Logging
              System.out.println("BAD packet " + mark);
              dump( "In: " );
              return -1;
          }
          return len;
      }
      
      /**
       * Prepare this packet for accumulating a message from the container to
       * the web server.  Set the write position to just after the header
       * (but leave the length unwritten, because it is as yet unknown).
       */
      public void reset() {
          len = 4;
          pos = 4;
          buff[0] = (byte)(AJP13_SW_HEADER >> 8);
          buff[1] = (byte)(AJP13_SW_HEADER & 0xFF);
      }
  	
      /**
       * For a packet to be sent to the web server, finish the process of
       * accumulating data and write the length of the data payload into
       * the header.  
       */
      public void end() {
          len = pos;
          setInt( 2, len-4 );
      }
  	
      // ============ Data Writing Methods ===================
  
      /**
       * Write an 32 bit integer at an arbitrary position in the packet,but don't
       * change the write position.
       *
       * @param bpos The 0-indexed position within the buffer at which to
       * write the integer (where 0 is the beginning of the header).
       * @param val The integer to write.
       */
      private void setInt( int bPos, int val ) {
          buff[bPos]   = (byte) ((val >>>  8) & 0xFF);
          buff[bPos+1] = (byte) (val & 0xFF);
      }
  
      public void appendInt( int val ) {
          setInt( pos, val );
          pos += 2;
      }
  	
      public void appendByte( byte val ) {
          buff[pos++] = val;
      }
  	
      public void appendBool( boolean val) {
          buff[pos++] = (byte) (val ? 1 : 0);
      }
  
      /**
       * Write a String out at the current write position.  Strings are
       * encoded with the length in two bytes first, then the string, and
       * then a terminating \0 (which is <B>not</B> included in the
       * encoded length).  The terminator is for the convenience of the C
       * code, where it saves a round of copying.  A null string is
       * encoded as a string with length 0.  
       */
      public void appendString(String str) throws UnsupportedEncodingException {
          // Dual use of the buffer - as Ajp13Packet and as OutputBuffer
          // The idea is simple - fewer buffers, smaller footprint and less
          // memcpy. The code is a bit tricky, but only local to this
          // function.
          if(str == null) {
              setInt( pos, 0);
              buff[pos + 2] = 0;
              pos += 3;
              return;
          }
  
          //
          // XXX i don't have OutputBuffer in tc4... ks.
          // fix this later...
          //
          byte[] bytes = str.getBytes(encoding);
          appendBytes(bytes, 0, bytes.length);
          
          /* XXX XXX XXX XXX Try to add it back.
          int strStart=pos;
  
          // This replaces the old ( buggy and slow ) str.length()
          // and str.getBytes(). str.length() is chars, may be != bytes
          // and getBytes is _very_ slow.
          // XXX setEncoding !!!
  
          ob.setByteOff( pos+2 ); 
          try {
              ob.write( str );
              ob.flushChars();
          } catch( IOException ex ) {
              ex.printStackTrace();
          }
          int strEnd=ob.getByteOff();
          
          buff[strEnd]=0; // The \0 terminator
          int strLen=strEnd-strStart;
          setInt( pos, strEnd - strStart );
          pos += strLen + 3; 
          */
      }
  
      /** 
       * Copy a chunk of bytes into the packet, starting at the current
       * write position.  The chunk of bytes is encoded with the length
       * in two bytes first, then the data itself, and finally a
       * terminating \0 (which is <B>not</B> included in the encoded
       * length).
       *
       * @param b The array from which to copy bytes.
       * @param off The offset into the array at which to start copying
       * @param len The number of bytes to copy.  
       */
      public void appendBytes( byte b[], int off, int numBytes ) {
          appendInt( numBytes );
          if( pos + numBytes >= buff.length ) {
              System.out.println("Buffer overflow " + buff.length + " " + pos + " " + numBytes );
              // XXX Log
          }
          System.arraycopy( b, off, buff, pos, numBytes);
          buff[pos + numBytes] = 0; // Terminating \0
          pos += numBytes + 1;
      }
  
          /**
       * Write a 32 bits integer at an arbitrary position in the packet, but don't
       * change the write position.
       *
       * @param bpos The 0-indexed position within the buffer at which to
       * write the integer (where 0 is the beginning of the header).
       * @param val The integer to write.
       */
      private void setLongInt( int bPos, int val ) {
          buff[bPos]   = (byte) ((val >>>  24) & 0xFF);
          buff[bPos+1] = (byte) ((val >>>  16) & 0xFF);
          buff[bPos+2] = (byte) ((val >>>   8) & 0xFF);
          buff[bPos+3] = (byte) (val & 0xFF);
      }
  
      public void appendLongInt( int val ) {
          setLongInt( pos, val );
          pos += 4;
      }
  
      /**
       * Copy a chunk of bytes into the packet, starting at the current
       * write position.  The chunk of bytes IS NOT ENCODED with ANY length
       * header.
       *
       * @param b The array from which to copy bytes.
       * @param off The offset into the array at which to start copying
       * @param len The number of bytes to copy.
       */
      public void appendXBytes(byte[] b, int off, int numBytes) {
          if( pos + numBytes > buff.length ) {
          System.out.println("appendXBytes - Buffer overflow " + buff.length + " " + pos + " " + numBytes );
          // XXX Log
          }
          System.arraycopy( b, off, buff, pos, numBytes);
          pos += numBytes;
      }
  	
      
      // ============ Data Reading Methods ===================
  
      /**
       * Read an integer from packet, and advance the read position past
       * it.  Integers are encoded as two unsigned bytes with the
       * high-order byte first, and, as far as I can tell, in
       * little-endian order within each byte.  
       */
      public int getInt() {
          int result = peekInt();
          pos += 2;
          return result;
      }
  
      /**
       * Read an integer from the packet, but don't advance the read
       * position past it.  
       */
      public int peekInt() {
          int b1 = buff[pos] & 0xFF;  // No swap, Java order
          int b2 = buff[pos + 1] & 0xFF;
  
          return  (b1<<8) + b2;
      }
  
      public byte getByte() {
          byte res = buff[pos];
          pos++;
          return res;
      }
  
      public byte peekByte() {
          return buff[pos];
      }
  
      public boolean getBool() {
          return (getByte() == (byte) 1);
      }
  
      public void getMessageBytes(MessageBytes mb) {
          int length = getInt();
          if( (length == 0xFFFF) || (length == -1) ) {
              mb.setString( null );
              return;
          }
          mb.setBytes( buff, pos, length );
          pos += length;
          pos++; // Skip the terminating \0
      }
      
      public MessageBytes addHeader(MimeHeaders headers) {
          int length = getInt();
          if( (length == 0xFFFF) || (length == -1) ) {
              return null;
          }
          MessageBytes vMB=headers.addValue( buff, pos, length );
          pos += length;
          pos++; // Skip the terminating \0
  	    
          return vMB;
      }
  	
      /**
       * Read a String from the packet, and advance the read position
       * past it.  See appendString for details on string encoding.
       **/
      public String getString() throws java.io.UnsupportedEncodingException {
          int length = getInt();
          if( (length == 0xFFFF) || (length == -1) ) {
              //	    System.out.println("null string " + length);
              return null;
          }
          String s = new String(buff, pos, length, encoding);
  
          pos += length;
          pos++; // Skip the terminating \0
          return s;
      }
  
      /**
       * Copy a chunk of bytes from the packet into an array and advance
       * the read position past the chunk.  See appendBytes() for details
       * on the encoding.
       *
       * @return The number of bytes copied.
       */
      public int getBytes(byte dest[]) {
          int length = getInt();
          if( length > buff.length ) {
              // XXX Should be if(pos + length > buff.legth)?
              System.out.println("XXX Assert failed, buff too small ");
          }
  	
          if( (length == 0xFFFF) || (length == -1) ) {
              System.out.println("null string " + length);
              return 0;
          }
  
          System.arraycopy( buff, pos,  dest, 0, length );
          pos += length;
          pos++; // Skip terminating \0  XXX I believe this is wrong but harmless
          return length;
      }
  
          /**
       * Read a 32 bits integer from packet, and advance the read position past
       * it.  Integers are encoded as four unsigned bytes with the
       * high-order byte first, and, as far as I can tell, in
       * little-endian order within each byte.
       */
      public int getLongInt() {
          int result = peekLongInt();
          pos += 4;
          return result;
      }
  
      /**
       * Copy a chunk of bytes from the packet into an array and advance
       * the read position past the chunk.  See appendXBytes() for details
       * on the encoding.
       *
       * @return The number of bytes copied.
       */
      public int getXBytes(byte[] dest, int length) {
          if( length > buff.length ) {
          // XXX Should be if(pos + length > buff.legth)?
          System.out.println("XXX Assert failed, buff too small ");
          }
  
          System.arraycopy( buff, pos,  dest, 0, length );
          pos += length;
          return length;
      }
  
      /**
       * Read a 32 bits integer from the packet, but don't advance the read
       * position past it.
       */
      public int peekLongInt() {
          int b1 = buff[pos] & 0xFF;  // No swap, Java order
          b1 <<= 8;
          b1 |= (buff[pos + 1] & 0xFF);
          b1 <<= 8;
          b1 |= (buff[pos + 2] & 0xFF);
          b1 <<=8;
          b1 |= (buff[pos + 3] & 0xFF);
          return  b1;
      }
  
      // ============== Debugging code =========================
      private String hex( int x ) {
          //	    if( x < 0) x=256 + x;
          String h=Integer.toHexString( x );
          if( h.length() == 1 ) h = "0" + h;
          return h.substring( h.length() - 2 );
      }
  
      private void hexLine( int start ) {
          for( int i=start; i< start+16 ; i++ ) {
              if( i < len + 4)
                  System.out.print( hex( buff[i] ) + " ");
              else 
                  System.out.print( "   " );
          }
          System.out.print(" | ");
          for( int i=start; i < start+16 && i < len + 4; i++ ) {
              if( Character.isLetterOrDigit( (char)buff[i] ))
                  System.out.print( new Character((char)buff[i]) );
              else
                  System.out.print( "." );
          }
          System.out.println();
      }
      
      public void dump(String msg) {
          System.out.println( msg + ": " + buff + " " + pos +"/" + (len + 4));
  
          for( int j=0; j < len + 4; j+=16 )
              hexLine( j );
  	
          System.out.println();
      }
  }
  
  
  
  1.1                  jakarta-tomcat-connectors/jk/java/org/apache/jk/AjpHandler.java
  
  Index: AjpHandler.java
  ===================================================================
  /*
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * [Additional notices, if required by prior licensing conditions]
   *
   */
  
  package org.apache.jk;
  
  import java.io.IOException;
  import java.io.UnsupportedEncodingException;
  import java.io.InputStream;
  import java.io.OutputStream;
  import java.net.Socket;
  import java.util.Enumeration;
  import java.security.*;
  
  import org.apache.tomcat.util.http.*;
  import org.apache.tomcat.util.buf.*;
  
  /**
   * Base class for handlers of Ajp messages. Jk provide a simple bidirectional 
   * communication mechanism between the web server and a servlet container. It is
   * based on messages that are passed between the 2 server, using a single
   * thread on each side.
   *
   * The container side must be able to deal with at least the "REQUEST FORWARD",
   * the server side must be able to deal with at least "HEADERS", "BODY",
   * "END" messages.
   *
   * @author Henri Gomez
   * @author Costin Manolache
   */
  public class AjpHandler
  {
      public static final int UNKNOWN=-1;
      Ajp13 channel;
      
      public void init( Ajp13 channel ) {
          this.channel=channel;
      }
      
      /** Execute the callback 
       */
      public int handleAjpMessage( int type, Ajp13 channel,
  				 Ajp13Packet ajp, BaseRequest req )
  	throws IOException
      {
  	return UNKNOWN;
      }
  }    
  
  
  
  1.1                  jakarta-tomcat-connectors/jk/java/org/apache/jk/NegociationHandler.java
  
  Index: NegociationHandler.java
  ===================================================================
  /*
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * [Additional notices, if required by prior licensing conditions]
   *
   */
  
  package org.apache.jk;
  
  import java.io.IOException;
  import java.io.UnsupportedEncodingException;
  import java.io.InputStream;
  import java.io.OutputStream;
  import java.net.Socket;
  import java.util.Enumeration;
  import java.security.*;
  
  import org.apache.tomcat.util.http.*;
  import org.apache.tomcat.util.buf.*;
  
  
  /**
   * Handler for the protocol negotiation. It will authenticate and 
   * exchange information about supported messages on each end.
   * 
   * 
   * @author Henri Gomez [hgomez@slib.fr]
   * @author Dan Milstein [danmil@shore.net]
   * @author Keith Wannamaker [Keith@Wannamaker.org]
   * @author Costin Manolache
   */
  public class NegociationHandler extends AjpHandler
  {
      public static final byte JK_AJP13_SHUTDOWN=7;
  	
      // Initial Login Phase (web server -> servlet engine)
      public static final byte JK_AJP14_LOGINIT_CMD=0x10;
      
      // Second Login Phase (servlet engine -> web server), md5 seed is received
      public static final byte JK_AJP14_LOGSEED_CMD=0x11;
      
      // Third Login Phase (web server -> servlet engine),
      // md5 of seed + secret is sent
      public static final byte JK_AJP14_LOGCOMP_CMD=0x12;
      
      // Login Accepted (servlet engine -> web server)
      public static final byte JK_AJP14_LOGOK_CMD=0x13;
  
      // Login Rejected (servlet engine -> web server), will be logged
      public static final byte JK_AJP14_LOGNOK_CMD=0x14;
      
      // Context Query (web server -> servlet engine),
      // which URI are handled by servlet engine ?
      public static final byte JK_AJP14_CONTEXT_QRY_CMD=0x15;
      
      // Context Info (servlet engine -> web server), URI handled response
      public static final byte JK_AJP14_CONTEXT_INFO_CMD= 0x16;
      
      // Context Update (servlet engine -> web server), status of context changed
      public static final byte JK_AJP14_CONTEXT_UPDATE_CMD= 0x17;
      
      // Servlet Engine Status (web server -> servlet engine),
      // what's the status of the servlet engine ?
      public static final byte JK_AJP14_STATUS_CMD= 0x18;
      
      // Secure Shutdown command (web server -> servlet engine),
      //please servlet stop yourself.
      public static final byte JK_AJP14_SHUTDOWN_CMD= 0x19;
      
      // Secure Shutdown command Accepted (servlet engine -> web server)
      public static final byte JK_AJP14_SHUTOK_CMD= 0x1A;
      
      // Secure Shutdown Rejected (servlet engine -> web server)
      public static final byte JK_AJP14_SHUTNOK_CMD= 0x1B;
      
      // Context Status (web server -> servlet engine),
      //what's the status of the context ?
      public static final byte JK_AJP14_CONTEXT_STATE_CMD= 0x1C;
      
      // Context Status Reply (servlet engine -> web server), status of context
      public static final byte JK_AJP14_CONTEXT_STATE_REP_CMD = 0x1D;
      
      // Unknown Packet Reply (web server <-> servlet engine),
      //when a packet couldn't be decoded
      public static final byte JK_AJP14_UNKNOW_PACKET_CMD = 0x1E;
  
  
      // -------------------- Other constants -------------------- 
  
      // Entropy Packet Size
      public static final int AJP14_ENTROPY_SEED_LEN= 32;
      public static final int AJP14_COMPUTED_KEY_LEN= 32;
      
      // web-server want context info after login
      public static final int AJP14_CONTEXT_INFO_NEG= 0x80000000;
      
      // web-server want context updates
      public static final int AJP14_CONTEXT_UPDATE_NEG= 0x40000000;
      
      // web-server want compressed stream
      public static final int AJP14_GZIP_STREAM_NEG= 0x20000000;
      
      // web-server want crypted DES56 stream with secret key
      public static final int AJP14_DES56_STREAM_NEG= 0x10000000;
      
      // Extended info on server SSL vars
      public static final int AJP14_SSL_VSERVER_NEG= 0x08000000;
      
      // Extended info on client SSL vars
      public static final int AJP14_SSL_VCLIENT_NEG= 0x04000000;
      
      // Extended info on crypto SSL vars
      public static final int AJP14_SSL_VCRYPTO_NEG= 0x02000000;
      
      // Extended info on misc SSL vars
      public static final int  AJP14_SSL_VMISC_NEG= 0x01000000;
      
      // mask of protocol supported
      public static final int  AJP14_PROTO_SUPPORT_AJPXX_NEG=0x00FF0000;
      
      // communication could use AJP14
      public static final int  AJP14_PROTO_SUPPORT_AJP14_NEG=0x00010000;
      
      // communication could use AJP15
      public static final int  AJP14_PROTO_SUPPORT_AJP15_NEG=0x00020000;
      
      // communication could use AJP16
      public static final int  AJP14_PROTO_SUPPORT_AJP16_NEG=0x00040000;
      
      // Some failure codes
      public static final int AJP14_BAD_KEY_ERR= 0xFFFFFFFF;
      public static final int AJP14_ENGINE_DOWN_ERR = 0xFFFFFFFE;
      public static final int AJP14_RETRY_LATER_ERR = 0xFFFFFFFD;
      public static final int AJP14_SHUT_AUTHOR_FAILED_ERR = 0xFFFFFFFC;
      
      // Some status codes
      public static final byte AJP14_CONTEXT_DOWN= 0x01;
      public static final byte AJP14_CONTEXT_UP= 0x02;
      public static final byte AJP14_CONTEXT_OK= 0x03;
  
  
      // -------------------- Parameters --------------------
      String  containerSignature="Ajp14-based container";
      String  seed="seed";// will use random
      String  password;
  
      int	webserverNegociation=0;
      //    String  webserverName;
      
      public NegociationHandler() {
          setSeed("myveryrandomentropy");
  	setPassword("myverysecretkey");
      }
  
      public void setContainerSignature( String s ) {
  	containerSignature=s;
      }
  
      // -------------------- State --------------------
  
      //     public String getWebserverName() {
      // 	return webserverName;
      //     }
      
      // -------------------- Parameters --------------------
      
      /**
       * Set the original entropy seed
       */
      public void setSeed(String pseed) 
      {
  	String[] credentials = new String[1];
  	credentials[0] = pseed;
  	seed = digest(credentials, "md5");
      }
  
      /**
       * Get the original entropy seed
       */
      public String getSeed()
      {
  	return seed;
      }
  
      /**
       * Set the secret password
       */
      public void setPassword(String ppwd) 
      {
  	password = ppwd;
      }
      
      /**
       * Get the secret password
       */
      public String getPassword()
      {
  	return password;
      }
  
      // -------------------- Initialization --------------------
  
      public void init( Ajp13 ajp14 ) {
          super.init(ajp14);
  	// register incoming message handlers
  	ajp14.registerMessageType( JK_AJP14_LOGINIT_CMD,"JK_AJP14_LOGINIT_CMD",
  				   this, null); //
  	ajp14.registerMessageType( JK_AJP14_LOGCOMP_CMD,"JK_AJP14_LOGCOMP_CMD",
  				   this, null); //
  	ajp14.registerMessageType( JK_AJP13_SHUTDOWN,"JK_AJP13_SHUTDOWN",
  				   this, null); //
  	ajp14.registerMessageType( JK_AJP14_CONTEXT_QRY_CMD,
  				   "JK_AJP14_CONTEXT_QRY_CMD",
  				   this, null); //
  	ajp14.registerMessageType( JK_AJP14_STATUS_CMD,"JK_AJP14_STATUS_CMD",
  				   this, null); //
  	ajp14.registerMessageType( JK_AJP14_SHUTDOWN_CMD,
                                     "JK_AJP14_SHUTDOWN_CMD",
  				   this, null); //
  	ajp14.registerMessageType( JK_AJP14_CONTEXT_STATE_CMD,
  				   "JK_AJP14_CONTEXT_STATE_CMD",
  				   this, null); //
  	ajp14.registerMessageType( JK_AJP14_UNKNOW_PACKET_CMD,
  				   "JK_AJP14_UNKNOW_PACKET_CMD",
  				   this, null); //
  	
  	// register outgoing messages handler
  	ajp14.registerMessageType( JK_AJP14_LOGNOK_CMD,"JK_AJP14_LOGNOK_CMD",
  				   this,null );
  	
      }
      
  
      
      // -------------------- Dispatch --------------------
  
      public int handleAjpMessage( int type, Ajp13 ch, Ajp13Packet hBuf,
  				 BaseRequest req )
  	throws IOException
      {
          System.out.println("handleAjpMessage: " + type );
  	Ajp13Packet outBuf=ch.outBuf;
  	// Valid requests when not logged:
  	switch( type ) {
  	case JK_AJP14_LOGINIT_CMD :
  	    return handleLogInit(ch, hBuf, outBuf);
  	case JK_AJP14_LOGCOMP_CMD :
  	    return handleLogComp(ch, hBuf, outBuf);
  	case JK_AJP13_SHUTDOWN:
  	    return -2;
  	case JK_AJP14_CONTEXT_QRY_CMD :
  	    return handleContextQuery(ch, hBuf, outBuf);
  	case JK_AJP14_STATUS_CMD :
  	    return handleStatus(hBuf, outBuf);
  	case JK_AJP14_SHUTDOWN_CMD :
  	    return handleShutdown(hBuf, outBuf);
  	case JK_AJP14_CONTEXT_STATE_CMD :
  	    return handleContextState(hBuf, outBuf);
  	case JK_AJP14_UNKNOW_PACKET_CMD :
  	    return handleUnknowPacket(hBuf, outBuf);
  	default:
  	    log("unknown command " + type + " received");
  	    return 200; // XXX This is actually an error condition
  	}
  	//return UNKNOWN;
      }
      
      //----------- Implementation for various protocol commands -----------
  
      /**
       * Handle the Initial Login Message from Web-Server
       *
       * Get the requested Negociation Flags
       * Get also the Web-Server Name
       * 
       * Send Login Seed (MD5 of seed)
       */
      private int handleLogInit( Ajp13 ch, Ajp13Packet msg,
  			       Ajp13Packet outBuf )
  	throws IOException
      {
  	webserverNegociation = msg.getLongInt();
  	String webserverName  = msg.getString();
  	log("in handleLogInit with nego " +
  	    decodeNegociation(webserverNegociation) +
              " from webserver " + webserverName);
  	
  	outBuf.reset();
          outBuf.appendByte(JK_AJP14_LOGSEED_CMD);
  	String[] credentials = new String[1];
  	credentials[0] = getSeed();
          outBuf.appendXBytes(getSeed().getBytes(), 0, AJP14_ENTROPY_SEED_LEN);
  	log("in handleLogInit: sent entropy " + getSeed());
          outBuf.end();
          ch.send(outBuf);
  	return 304;
      }
      
      /**
       * Handle the Second Phase of Login (accreditation)
       * 
       * Get the MD5 digest of entropy + secret password
       * If the authentification is valid send back LogOk
       * If the authentification failed send back LogNok
       */
      private int handleLogComp( Ajp13 ch, Ajp13Packet msg,
  			       Ajp13Packet outBuf )
  	throws IOException
      {
  	// log("in handleLogComp :");
  	
  	byte [] rdigest = new byte[AJP14_ENTROPY_SEED_LEN];
  	
  	if (msg.getXBytes(rdigest, AJP14_ENTROPY_SEED_LEN) < 0)
  	    return 200;
  	
  	String[] credentials = new String[2];
  	credentials[0] = getSeed();
  	credentials[1] = getPassword();
  	String computed = digest(credentials, "md5");
  	String received = new String(rdigest);
  	
  	// XXX temp workaround, to test the rest of the connector.
  	
  	if ( ! computed.equalsIgnoreCase(received)) {
  	    log("in handleLogComp : authentification failure received=" +
  		received + " awaited=" + computed);
  	}
  	
  	if (false ) { // ! computed.equalsIgnoreCase(received)) {
  	    log("in handleLogComp : authentification failure received=" +
  		received + " awaited=" + computed);
  	    
  	    // we should have here a security mecanism which could maintain
  	    // a list of remote IP which failed too many times
  	    // so we could reject them quickly at next connect
  	    outBuf.reset();
  	    outBuf.appendByte(JK_AJP14_LOGNOK_CMD);
  	    outBuf.appendLongInt(AJP14_BAD_KEY_ERR);
  	    outBuf.end();
  	    ch.send(outBuf);
  	    return 200;
  	} else {
              // logged we can go process requests
  	    channel.setLogged(true);
  	    outBuf.reset();
  	    outBuf.appendByte(JK_AJP14_LOGOK_CMD);
  	    outBuf.appendLongInt(getProtocolFlags(webserverNegociation));
  	    outBuf.appendString( containerSignature );
  	    outBuf.end(); 
  	    ch.send(outBuf);
  	}
  	
  	return (304);
      }
  
      private int handleContextQuery( Ajp13 ch, Ajp13Packet msg,
  				    Ajp13Packet outBuf )
  	throws IOException
      {
  	log("in handleContextQuery :");
      String virtualHost = msg.getString();
      log("in handleContextQuery for virtual" + virtualHost); 
  
      outBuf.reset();
      outBuf.appendByte(JK_AJP14_CONTEXT_INFO_CMD);
      outBuf.appendString( virtualHost );
  
      log("in handleContextQuery for virtual " + virtualHost +
          "examples URI/MIMES");
      outBuf.appendString("examples");    // first context - examples
      outBuf.appendString("servlet/*");   // examples/servlet/*
      outBuf.appendString("*.jsp");       // examples/*.jsp
      outBuf.appendString("");            // no more URI/MIMES
  
      log("in handleContextQuery for virtual " + virtualHost +
          "send admin URI/MIMES"); 
      outBuf.appendString("admin");       // second context - admin
      outBuf.appendString("servlet/*");   // /admin//servlet/*
      outBuf.appendString("*.jsp");       // /admin/*.jsp
      outBuf.appendString("");            // no more URI/MIMES
  
      outBuf.appendString("");            // no more contexts
      outBuf.end();
      ch.send(outBuf);
      
  	return (304);
      }
      
      private int handleStatus( Ajp13Packet msg, Ajp13Packet outBuf )
          throws IOException
      {
  	log("in handleStatus :");
  	return (304);
      }
  
      private int handleShutdown( Ajp13Packet msg, Ajp13Packet outBuf )
          throws IOException
      {
  	log("in handleShutdown :");
  	return (304);
      }
      
      private int handleContextState( Ajp13Packet msg , Ajp13Packet outBuf)
          throws IOException
      {
  	log("in handleContextState :");
  	return (304);
      }
      
      private int handleUnknowPacket( Ajp13Packet msg, Ajp13Packet outBuf )
          throws IOException
      {
  	log("in handleUnknowPacket :");
  	return (304);
      }
  
      // -------------------- Utils --------------------
  
      /**
       * Compute the Protocol Negociation Flags
       * 
       * Depending the protocol fatures implemented on servet-engine,
       * we'll drop requested features which could be asked by web-server
       *
       * Hopefully this functions could be overrided by decendants
       */
      private int getProtocolFlags(int wanted)
      {
                      // no real-time context update
  	wanted &= ~(AJP14_CONTEXT_UPDATE_NEG |
                      // no gzip compression yet
  		    AJP14_GZIP_STREAM_NEG    | 
                       // no DES56 cyphering yet
  		    AJP14_DES56_STREAM_NEG   |
                      // no Extended info on server SSL vars yet
  		    AJP14_SSL_VSERVER_NEG    |
                      // no Extended info on client SSL vars yet
  		    AJP14_SSL_VCLIENT_NEG    |
                      // no Extended info on crypto SSL vars yet
  		    AJP14_SSL_VCRYPTO_NEG    |
                      // no Extended info on misc SSL vars yet
  		    AJP14_SSL_VMISC_NEG	     |
                      // Reset AJP protocol mask
  		    AJP14_PROTO_SUPPORT_AJPXX_NEG);
          
  	// Only strict AJP14 supported
  	return (wanted | AJP14_PROTO_SUPPORT_AJP14_NEG);	
      }
  
      /**
       * Compute a digest (MD5 in AJP14) for an array of String
       */
      public final String digest(String[] credentials, String algorithm) {
          try {
              // Obtain a new message digest with MD5 encryption
              MessageDigest md =
                  (MessageDigest)MessageDigest.getInstance(algorithm).clone();
              // encode the credentials items
  	    for (int i = 0; i < credentials.length; i++) {
  		if( debug > 0 )
                      log("Credentials : " + i + " " + credentials[i]);
  		if( credentials[i] != null  )
  		    md.update(credentials[i].getBytes());
  	    }
              // obtain the byte array from the digest
              byte[] dig = md.digest();
  	    return HexUtils.convert(dig);
          } catch (Exception ex) {
              ex.printStackTrace();
              return null;
          }
      }
  
      // -------------------- Debugging --------------------
      // Very usefull for develoment
  
      /**
       * Display Negociation field in human form 
       */
      private String decodeNegociation(int nego)
      {
  	StringBuffer buf = new StringBuffer(128);
  	
  	if ((nego & AJP14_CONTEXT_INFO_NEG) != 0) 
  	    buf.append(" CONTEXT-INFO");
  	
  	if ((nego & AJP14_CONTEXT_UPDATE_NEG) != 0)
  	    buf.append(" CONTEXT-UPDATE");
  	
  	if ((nego & AJP14_GZIP_STREAM_NEG) != 0)
  	    buf.append(" GZIP-STREAM");
  	
  	if ((nego & AJP14_DES56_STREAM_NEG) != 0)
  	    buf.append(" DES56-STREAM");
  	
  	if ((nego & AJP14_SSL_VSERVER_NEG) != 0)
  	    buf.append(" SSL-VSERVER");
  	
  	if ((nego & AJP14_SSL_VCLIENT_NEG) != 0)
  	    buf.append(" SSL-VCLIENT");
  	
  	if ((nego & AJP14_SSL_VCRYPTO_NEG) != 0)
  	    buf.append(" SSL-VCRYPTO");
  	
  	if ((nego & AJP14_SSL_VMISC_NEG) != 0)
  	    buf.append(" SSL-VMISC");
  	
  	if ((nego & AJP14_PROTO_SUPPORT_AJP14_NEG) != 0)
  	    buf.append(" AJP14");
  	
  	if ((nego & AJP14_PROTO_SUPPORT_AJP15_NEG) != 0)
  	    buf.append(" AJP15");
  	
  	if ((nego & AJP14_PROTO_SUPPORT_AJP16_NEG) != 0)
  	    buf.append(" AJP16");
  	
  	return (buf.toString());
      }
      
      private static int debug=10;
      void log(String s) {
  	System.out.println("Ajp14Negotiation: " + s );
      }
   }
  
  
  
  1.1                  jakarta-tomcat-connectors/jk/java/org/apache/jk/RequestHandler.java
  
  Index: RequestHandler.java
  ===================================================================
  /*
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * [Additional notices, if required by prior licensing conditions]
   *
   */
  
  package org.apache.jk;
  
  import java.io.*;
  import java.net.Socket;
  import java.util.Enumeration;
  import java.security.*;
  import java.security.cert.*;
  
  import org.apache.tomcat.util.http.*;
  import org.apache.tomcat.util.buf.*;
  
  
  /**
   * Handle messages related with basic request information.
   *
   * This object can handle the following incoming messages:
   * - "FORWARD_REQUEST" input message ( sent when a request is passed from the web server )
   * - "RECEIVE_BODY_CHUNK" input ( sent by container to pass more body, in response to GET_BODY_CHUNK )
   *
   * It can handle the following outgoing messages:
   * - SEND_HEADERS. Pass the status code and headers.
   * - SEND_BODY_CHUNK. Send a chunk of body
   * - GET_BODY_CHUNK. Request a chunk of body data
   * - END_RESPONSE. Notify the end of a request processing.
   *
   * @author Henri Gomez [hgomez@slib.fr]
   * @author Dan Milstein [danmil@shore.net]
   * @author Keith Wannamaker [Keith@Wannamaker.org]
   * @author Costin Manolache
   */
  public class RequestHandler extends AjpHandler
  {
      // XXX Will move to a registry system.
      
      // Prefix codes for message types from server to container
      public static final byte JK_AJP13_FORWARD_REQUEST   = 2;
  
      // Prefix codes for message types from container to server
      public static final byte JK_AJP13_SEND_BODY_CHUNK   = 3;
      public static final byte JK_AJP13_SEND_HEADERS      = 4;
      public static final byte JK_AJP13_END_RESPONSE      = 5;
      public static final byte JK_AJP13_GET_BODY_CHUNK    = 6;
      
      // Integer codes for common response header strings
      public static final int SC_RESP_CONTENT_TYPE        = 0xA001;
      public static final int SC_RESP_CONTENT_LANGUAGE    = 0xA002;
      public static final int SC_RESP_CONTENT_LENGTH      = 0xA003;
      public static final int SC_RESP_DATE                = 0xA004;
      public static final int SC_RESP_LAST_MODIFIED       = 0xA005;
      public static final int SC_RESP_LOCATION            = 0xA006;
      public static final int SC_RESP_SET_COOKIE          = 0xA007;
      public static final int SC_RESP_SET_COOKIE2         = 0xA008;
      public static final int SC_RESP_SERVLET_ENGINE      = 0xA009;
      public static final int SC_RESP_STATUS              = 0xA00A;
      public static final int SC_RESP_WWW_AUTHENTICATE    = 0xA00B;
  	
      // Integer codes for common (optional) request attribute names
      public static final byte SC_A_CONTEXT       = 1;  // XXX Unused
      public static final byte SC_A_SERVLET_PATH  = 2;  // XXX Unused
      public static final byte SC_A_REMOTE_USER   = 3;
      public static final byte SC_A_AUTH_TYPE     = 4;
      public static final byte SC_A_QUERY_STRING  = 5;
      public static final byte SC_A_JVM_ROUTE     = 6;
      public static final byte SC_A_SSL_CERT      = 7;
      public static final byte SC_A_SSL_CIPHER    = 8;
      public static final byte SC_A_SSL_SESSION   = 9;
      public static final byte SC_A_SSL_KEYSIZE   = 11;
  
      // Used for attributes which are not in the list above
      public static final byte SC_A_REQ_ATTRIBUTE = 10; 
  
      // Terminates list of attributes
      public static final byte SC_A_ARE_DONE      = (byte)0xFF;
      
      // Translates integer codes to names of HTTP methods
      public static final String []methodTransArray = {
          "OPTIONS",
          "GET",
          "HEAD",
          "POST",
          "PUT",
          "DELETE",
          "TRACE",
          "PROPFIND",
          "PROPPATCH",
          "MKCOL",
          "COPY",
          "MOVE",
          "LOCK",
          "UNLOCK",
          "ACL",
          "REPORT",
          "VERSION-CONTROL",
          "CHECKIN",
          "CHECKOUT",
          "UNCHECKOUT",
          "SEARCH"
      };
      
      // id's for common request headers
      public static final int SC_REQ_ACCEPT          = 1;
      public static final int SC_REQ_ACCEPT_CHARSET  = 2;
      public static final int SC_REQ_ACCEPT_ENCODING = 3;
      public static final int SC_REQ_ACCEPT_LANGUAGE = 4;
      public static final int SC_REQ_AUTHORIZATION   = 5;
      public static final int SC_REQ_CONNECTION      = 6;
      public static final int SC_REQ_CONTENT_TYPE    = 7;
      public static final int SC_REQ_CONTENT_LENGTH  = 8;
      public static final int SC_REQ_COOKIE          = 9;
      public static final int SC_REQ_COOKIE2         = 10;
      public static final int SC_REQ_HOST            = 11;
      public static final int SC_REQ_PRAGMA          = 12;
      public static final int SC_REQ_REFERER         = 13;
      public static final int SC_REQ_USER_AGENT      = 14;
      // AJP14 new header
      public static final byte SC_A_SSL_KEY_SIZE  = 11; // XXX ??? 
  
      // Translates integer codes to request header names    
      public static final String []headerTransArray = {
          "accept",
          "accept-charset",
          "accept-encoding",
          "accept-language",
          "authorization",
          "connection",
          "content-type",
          "content-length",
          "cookie",
          "cookie2",
          "host",
          "pragma",
          "referer",
          "user-agent"
      };
  
      public RequestHandler() 
      {
      }
  
      public void init( Ajp13 ajp14 ) {
  	// register incoming message handlers
  	ajp14.registerMessageType( JK_AJP13_FORWARD_REQUEST,
  				   "JK_AJP13_FORWARD_REQUEST",
  				   this, null); // 2
  	// register outgoing messages handler
  	ajp14.registerMessageType( JK_AJP13_SEND_BODY_CHUNK, // 3
  				   "JK_AJP13_SEND_BODY_CHUNK",
  				   this,null );
  	ajp14.registerMessageType( JK_AJP13_SEND_HEADERS,  // 4
  				   "JK_AJP13_SEND_HEADERS",
  				   this,null );
  	ajp14.registerMessageType( JK_AJP13_END_RESPONSE, // 5
  				   "JK_AJP13_END_RESPONSE",
  				   this,null );
  	ajp14.registerMessageType( JK_AJP13_GET_BODY_CHUNK, // 6
  				   "JK_AJP13_GET_BODY_CHUNK",
  				   this, null );
      }
      
      // -------------------- Incoming message --------------------
      public int handleAjpMessage( int type, Ajp13 channel,
  				 Ajp13Packet ajp, BaseRequest req )
  	throws IOException
      {
  	switch( type ) {
  	case RequestHandler.JK_AJP13_FORWARD_REQUEST:
  	    return decodeRequest(channel, channel.hBuf, req );
  	default:
  	    return UNKNOWN;
  	}
      }
      
      /**
       * Parse a FORWARD_REQUEST packet from the web server and store its
       * properties in the passed-in request object.
       *
       * @param req An empty (newly-recycled) request object.
       * @param msg Holds the packet which has just been sent by the web
       * server, with its read position just past the packet header (which in
       * this case includes the prefix code for FORWARD_REQUEST).
       *
       * @return 200 in case of a successful decoduing, 500 in case of error.  
       */
      protected int decodeRequest(Ajp13 ch, Ajp13Packet msg, BaseRequest req)
          throws IOException
      {
          
          if (debug > 0) {
              log("decodeRequest()");
          }
  
  	// XXX Awful return values
  
          boolean isSSL = false;
  
          // Translate the HTTP method code to a String.
          byte methodCode = msg.getByte();
          req.method().setString(methodTransArray[(int)methodCode - 1]);
  
          msg.getMessageBytes(req.protocol()); 
          msg.getMessageBytes(req.requestURI());
  
          msg.getMessageBytes(req.remoteAddr());
          msg.getMessageBytes(req.remoteHost());
          msg.getMessageBytes(req.serverName());
          req.setServerPort(msg.getInt());
  
  	isSSL = msg.getBool();
  
  	// Decode headers
  	MimeHeaders headers = req.headers();
  	int hCount = msg.getInt();
          for(int i = 0 ; i < hCount ; i++) {
              String hName = null;
  
  	    // Header names are encoded as either an integer code starting
  	    // with 0xA0, or as a normal string (in which case the first
  	    // two bytes are the length).
              int isc = msg.peekInt();
              int hId = isc & 0xFF;
  
  	    MessageBytes vMB=null;
              isc &= 0xFF00;
              if(0xA000 == isc) {
                  msg.getInt(); // To advance the read position
                  hName = headerTransArray[hId - 1];
  		vMB= headers.addValue(hName);
              } else {
  		// XXX Not very elegant
  		vMB = msg.addHeader(headers);
  		if (vMB == null) {
                      return 500; // wrong packet
                  }
              }
  
              msg.getMessageBytes(vMB);
  
              // set content length, if this is it...
              if (hId == SC_REQ_CONTENT_LENGTH) {
                  int contentLength = (vMB == null) ? -1 : vMB.getInt();
                  req.setContentLength(contentLength);
              } else if (hId == SC_REQ_CONTENT_TYPE) {
                  ByteChunk bchunk = vMB.getByteChunk();
                  req.contentType().setBytes(bchunk.getBytes(),
                                             bchunk.getOffset(),
                                             bchunk.getLength());
              }
          }
  
  	byte attributeCode;
          for(attributeCode = msg.getByte() ;
              attributeCode != SC_A_ARE_DONE ;
              attributeCode = msg.getByte()) {
              switch(attributeCode) {
  	    case SC_A_CONTEXT      :
                  break;
  		
  	    case SC_A_SERVLET_PATH :
                  break;
  		
  	    case SC_A_REMOTE_USER  :
                  msg.getMessageBytes(req.remoteUser());
                  break;
  		
  	    case SC_A_AUTH_TYPE    :
                  msg.getMessageBytes(req.authType());
                  break;
  		
  	    case SC_A_QUERY_STRING :
  		msg.getMessageBytes(req.queryString());
                  break;
  		
  	    case SC_A_JVM_ROUTE    :
                  msg.getMessageBytes(req.jvmRoute());
                  break;
  		
  	    case SC_A_SSL_CERT     :
  		isSSL = true;
                  // Transform the string into certificate.
                  String certString = msg.getString();
                  byte[] certData = certString.getBytes();
                  ByteArrayInputStream bais = new ByteArrayInputStream(certData);
   
                  // Fill the first element.
                  X509Certificate jsseCerts[] = null;
                  try {
                      CertificateFactory cf =
                          CertificateFactory.getInstance("X.509");
                      X509Certificate cert = (X509Certificate)
                          cf.generateCertificate(bais);
                      jsseCerts =  new X509Certificate[1];
                      jsseCerts[0] = cert;
                  } catch(java.security.cert.CertificateException e) {
                      log("Certificate convertion failed" + e );
                  }
   
                  req.setAttribute("javax.servlet.request.X509Certificate",
                                   jsseCerts);
                  break;
  		
  	    case SC_A_SSL_CIPHER   :
  		isSSL = true;
  		req.setAttribute("javax.servlet.request.cipher_suite",
  				 msg.getString());
                  break;
  		
  	    case SC_A_SSL_SESSION  :
  		isSSL = true;
  		req.setAttribute("javax.servlet.request.ssl_session",
  				  msg.getString());
                  break;
  		
  	    case SC_A_REQ_ATTRIBUTE :
  		req.setAttribute(msg.getString(), 
  				 msg.getString());
                  break;
  
  	    case SC_A_SSL_KEY_SIZE: // Ajp13 !
  		req.setAttribute("javax.servlet.request.key_size",
  				 Integer.toString(msg.getInt()));
  		return 200;
  	    default:
  		return 500; // Error
  	    }
          }
  
          if(isSSL) {
              req.setScheme(req.SCHEME_HTTPS);
              req.setSecure(true);
          }
  
          // set cookies on request now that we have all headers
          req.cookies().setHeaders(req.headers());
  
  	// Check to see if there should be a body packet coming along
  	// immediately after
      	if(req.getContentLength() > 0) {
  
  	    /* Read present data */
  	    int err = ch.receive(ch.inBuf);
              if(err < 0) {
              	return 500;
  	    }
  	    
  	    ch.blen = ch.inBuf.peekInt();
  	    ch.pos = 0;
  	    ch.inBuf.getBytes(ch.bodyBuff);
      	}
      
          if (debug > 5) {
              log(req.toString());
          }
  
          return 200; // Success
      }
      
  
      // -------------------- Messages from container to server ------------------
      
      /**
       * Send the HTTP headers back to the web server and on to the browser.
       *
       * @param status The HTTP status code to send.
       * @param statusMessage the HTTP status message to send.
       * @param headers The set of all headers.
       */
      public void sendHeaders(Ajp13 ch, Ajp13Packet outBuf,
  			    int status, String statusMessage,
                              MimeHeaders headers)
          throws IOException
      {
  	// XXX if more headers that MAX_SIZE, send 2 packets!
  	if( statusMessage==null ) statusMessage=HttpMessages.getMessage(status);
  	outBuf.reset();
          outBuf.appendByte(JK_AJP13_SEND_HEADERS);
          outBuf.appendInt(status);
  	
  	outBuf.appendString(statusMessage);
          
  	int numHeaders = headers.size();
          outBuf.appendInt(numHeaders);
          
  	for( int i=0 ; i < numHeaders ; i++ ) {
  	    String headerName = headers.getName(i).toString();
  	    int sc = headerNameToSc(headerName);
              if(-1 != sc) {
                  outBuf.appendInt(sc);
              } else {
                  outBuf.appendString(headerName);
              }
              outBuf.appendString(headers.getValue(i).toString() );
          }
  
          outBuf.end();
          ch.send(outBuf);
      } 
  
  
      /**
       * Signal the web server that the servlet has finished handling this
       * request, and that the connection can be reused.
       */
      public void finish(Ajp13 ch, Ajp13Packet outBuf) throws IOException {
          if (debug > 0)  log("finish()");
  
  	outBuf.reset();
          outBuf.appendByte(JK_AJP13_END_RESPONSE);
          outBuf.appendBool(true); // Reuse this connection
          outBuf.end();
          ch.send(outBuf);
      }
  
      /**
       * Send a chunk of response body data to the web server and on to the
       * browser.
       *
       * @param b A huffer of bytes to send.
       * @param off The offset into the buffer from which to start sending.
       * @param len The number of bytes to send.
       */    
      public void doWrite(Ajp13 ch, Ajp13Packet outBuf,
  			byte b[], int off, int len)
  	throws IOException
      {
          if (debug > 0) log("doWrite(byte[], " + off + ", " + len + ")");
  
  	int sent = 0;
  	while(sent < len) {
  	    int to_send = len - sent;
  	    to_send = to_send > Ajp13.MAX_SEND_SIZE ? Ajp13.MAX_SEND_SIZE : to_send;
  
  	    outBuf.reset();
  	    outBuf.appendByte(JK_AJP13_SEND_BODY_CHUNK);	        	
  	    outBuf.appendBytes(b, off + sent, to_send);	        
  	    ch.send(outBuf);
  	    sent += to_send;
  	}
      }
  
      // -------------------- Utils -------------------- 
  
      /**
       * Translate an HTTP response header name to an integer code if
       * possible.  Case is ignored.
       * 
       * @param name The name of the response header to translate.
       *
       * @return The code for that header name, or -1 if no code exists.
       */
      protected int headerNameToSc(String name)
      {       
          switch(name.charAt(0)) {
  	case 'c':
  	case 'C':
  	    if(name.equalsIgnoreCase("Content-Type")) {
  		return SC_RESP_CONTENT_TYPE;
  	    } else if(name.equalsIgnoreCase("Content-Language")) {
  		return SC_RESP_CONTENT_LANGUAGE;
  	    } else if(name.equalsIgnoreCase("Content-Length")) {
  		return SC_RESP_CONTENT_LENGTH;
  	    }
              break;
              
  	case 'd':
  	case 'D':
  	    if(name.equalsIgnoreCase("Date")) {
                  return SC_RESP_DATE;
  	    }
              break;
              
  	case 'l':
  	case 'L':
  	    if(name.equalsIgnoreCase("Last-Modified")) {
  		return SC_RESP_LAST_MODIFIED;
  	    } else if(name.equalsIgnoreCase("Location")) {
  		return SC_RESP_LOCATION;
  	    }
              break;
  
  	case 's':
  	case 'S':
  	    if(name.equalsIgnoreCase("Set-Cookie")) {
  		return SC_RESP_SET_COOKIE;
  	    } else if(name.equalsIgnoreCase("Set-Cookie2")) {
  		return SC_RESP_SET_COOKIE2;
  	    }
              break;
              
  	case 'w':
  	case 'W':
  	    if(name.equalsIgnoreCase("WWW-Authenticate")) {
  		return SC_RESP_WWW_AUTHENTICATE;
  	    }
              break;          
          }
          
          return -1;
      }
     
      private static int debug=0;
      void log(String s) {
  	System.out.println("RequestHandler: " + s );
      }
  
      // ==================== Servlet Input Support =================
      // XXX DEPRECATED
      
      public int available(Ajp13 ch) throws IOException {
          if (debug > 0) {
              log("available()");
          }
  
          if (ch.pos >= ch.blen) {
              if( ! refillReadBuffer(ch)) {
  		return 0;
  	    }
          }
          return ch.blen - ch.pos;
      }
  
      /**
       * Return the next byte of request body data (to a servlet).
       *
       * @see Request#doRead
       */
      public int doRead(Ajp13 ch) throws IOException 
      {
          if (debug > 0) {
              log("doRead()");
          }
  
          if(ch.pos >= ch.blen) {
              if( ! refillReadBuffer(ch)) {
  		return -1;
  	    }
          }
          return (char) ch.bodyBuff[ch.pos++];
      }
      
      /**
       * Store a chunk of request data into the passed-in byte buffer.
       *
       * @param b A buffer to fill with data from the request.
       * @param off The offset in the buffer at which to start filling.
       * @param len The number of bytes to copy into the buffer.
       *
       * @return The number of bytes actually copied into the buffer, or -1
       * if the end of the stream has been reached.
       *
       * @see Request#doRead
       */
      public int doRead(Ajp13 ch, byte[] b, int off, int len) throws IOException 
      {
          if (debug > 0) {
              log("doRead(byte[], int, int)");
          }
  
  	if(ch.pos >= ch.blen) {
  	    if( ! refillReadBuffer(ch)) {
  		return -1;
  	    }
  	}
  
  	if(ch.pos + len <= ch.blen) { // Fear the off by one error
  	    // Sanity check b.length > off + len?
  	    System.arraycopy(ch.bodyBuff, ch.pos, b, off, len);
  	    ch.pos += len;
  	    return len;
  	}
  
  	// Not enough data (blen < pos + len)
  	int toCopy = len;
  	while(toCopy > 0) {
  	    int bytesRemaining = ch.blen - ch.pos;
  	    if(bytesRemaining < 0) 
  		bytesRemaining = 0;
  	    int c = bytesRemaining < toCopy ? bytesRemaining : toCopy;
              
  	    System.arraycopy(ch.bodyBuff, ch.pos, b, off, c);
  
  	    toCopy    -= c;
  
  	    off       += c;
  	    ch.pos       += c; // In case we exactly consume the buffer
  
  	    if(toCopy > 0) 
  		if( ! refillReadBuffer(ch)) { // Resets blen and pos
  		    break;
  		}
  	}
  
  	return len - toCopy;
      }
      
      /**
       * Get more request body data from the web server and store it in the 
       * internal buffer.
       *
       * @return true if there is more data, false if not.    
       */
      public boolean refillReadBuffer(Ajp13 ch) throws IOException 
      {
          if (debug > 0) {
              log("refillReadBuffer()");
          }
  
  	// If the server returns an empty packet, assume that that end of
  	// the stream has been reached (yuck -- fix protocol??).
  
  	// Why not use outBuf??
  	ch.inBuf.reset();
  	ch.inBuf.appendByte(JK_AJP13_GET_BODY_CHUNK);
  	ch.inBuf.appendInt(Ajp13.MAX_READ_SIZE);
  	ch.send(ch.inBuf);
  	
  	int err = ch.receive(ch.inBuf);
          if(err < 0) {
  	    throw new IOException();
  	}
  	
      	ch.blen = ch.inBuf.peekInt();
      	ch.pos = 0;
      	ch.inBuf.getBytes(ch.bodyBuff);
  
  	return (ch.blen > 0);
      }    
  
      // ==================== Servlet Output Support =================
      
      /**
       */
      public void beginSendHeaders(Ajp13 ch, Ajp13Packet outBuf,
  				 int status,
                                   String statusMessage,
                                   int numHeaders) throws IOException {
  
          if (debug > 0) {
              log("sendHeaders()");
          }
  
  	// XXX if more headers that MAX_SIZE, send 2 packets!
  
  	outBuf.reset();
          outBuf.appendByte(JK_AJP13_SEND_HEADERS);
  
          if (debug > 0) {
              log("status is:  " + status +
                         "(" + statusMessage + ")");
          }
  
          // set status code and message
          outBuf.appendInt(status);
          outBuf.appendString(statusMessage);
  
          // write the number of headers...
          outBuf.appendInt(numHeaders);
      }
  
      public void sendHeader(Ajp13Packet outBuf,
  			   String name, String value)
  	throws IOException
      {
          int sc = headerNameToSc(name);
          if(-1 != sc) {
              outBuf.appendInt(sc);
          } else {
              outBuf.appendString(name);
          }
          outBuf.appendString(value);
      }
  
      public void endSendHeaders(Ajp13 ch, Ajp13Packet outBuf)
  	throws IOException
      {
          outBuf.end();
          ch.send(outBuf);
      }
  
      /**
       * Send the HTTP headers back to the web server and on to the browser.
       *
       * @param status The HTTP status code to send.
       * @param headers The set of all headers.
       */
      public void sendHeaders(Ajp13 ch, Ajp13Packet outBuf,
  			    int status, MimeHeaders headers)
          throws IOException
      {
          sendHeaders(ch, outBuf, status, HttpMessages.getMessage(status),
                      headers);
      }
      
  
   }
  
  
  

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>