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/06/09 04:56:23 UTC

cvs commit: jakarta-tomcat-connectors/jk/java/org/apache/ajp/tomcat33 Ajp12.java Ajp12Interceptor.java Ajp13.java Ajp13Interceptor.java JNIConnectionHandler.java JNIEndpoint.java PoolTcpConnector.java

costin      01/06/08 19:56:23

  Modified:    jk       build.xml
               jk/java/org/apache/ajp Ajp13.java
  Added:       jk/java/org/apache/ajp/tomcat33 Ajp12.java
                        Ajp12Interceptor.java Ajp13.java
                        Ajp13Interceptor.java JNIConnectionHandler.java
                        JNIEndpoint.java PoolTcpConnector.java
  Log:
  Added the ajp implementation from tomcat33 ( will be merged with Ajp13,
  Ajp13Packet, etc ).
  
  The tomcat33 files are excluded ( will be build using the main
  build.xml ).
  
  Revision  Changes    Path
  1.9       +2 -1      jakarta-tomcat-connectors/jk/build.xml
  
  Index: build.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-connectors/jk/build.xml,v
  retrieving revision 1.8
  retrieving revision 1.9
  diff -u -r1.8 -r1.9
  --- build.xml	2001/06/08 19:48:39	1.8
  +++ build.xml	2001/06/09 02:56:22	1.9
  @@ -1,6 +1,6 @@
   <project name="jk" default="build-main" basedir=".">
   
  -    <!-- ===================== Initialize Property Values =================== -->
  +    <!-- ===================== Initialize Property Values ================ -->
       <property file="build.properties"/>
       <property file="${user.home}/build.properties"/>
   
  @@ -42,6 +42,7 @@
   	       verbose="off"
   	       excludes="**/CVS/**">
   	    <exclude name="org/apache/ajp/tomcat4/**" if="tomcat4.skip"/>
  +	    <exclude name="org/apache/ajp/tomcat33/**" />
   	    <classpath refid="build-main.classpath"/>
   	</javac>
   
  
  
  
  1.6       +3 -0      jakarta-tomcat-connectors/jk/java/org/apache/ajp/Ajp13.java
  
  Index: Ajp13.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-connectors/jk/java/org/apache/ajp/Ajp13.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- Ajp13.java	2001/05/29 23:01:46	1.5
  +++ Ajp13.java	2001/06/09 02:56:22	1.6
  @@ -227,6 +227,9 @@
           }
       }
   
  +    public Ajp13() {
  +    }
  +    
       public void recycle() {
           if (debug > 0) {
               logger.log("recycle()");
  
  
  
  1.1                  jakarta-tomcat-connectors/jk/java/org/apache/ajp/tomcat33/Ajp12.java
  
  Index: Ajp12.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]
   *
   */
  
  
  /*
    Based on Ajp11ConnectionHandler and Ajp12 implementation of JServ
  */
  package org.apache.ajp.tomcat33;
  
  import java.io.*;
  import java.net.*;
  import java.util.*;
  import org.apache.tomcat.core.*;
  import org.apache.tomcat.util.net.*;
  import org.apache.tomcat.util.*;
  
  class Ajp12 {
      Socket socket;
      InputStream sin;
      BufferedInputStream ajpin;
      private boolean tomcatAuthentication=true;
      boolean shutdown=false;
      boolean isPing=false;
      boolean doLog;
      String secret=null;
  
      public Ajp12() {
      }
  
      // Debug only - use only to debug this component
      void d( String s ) {
  	System.out.print("Ajp12: ");
  	System.out.println( s );
      }
      
      public int doRead() throws IOException {
  	return ajpin.read();
      }
  
      public  int doRead( byte b[], int off, int len ) throws IOException {
  	return ajpin.read( b,off,len);
      }
  
      public void setSocket( Socket s ) throws IOException {
  	this.socket = s;
  	sin = s.getInputStream();
  	ajpin = new BufferedInputStream(sin);
      }
  
      public void setSecret( String s ) {
  	secret=s;
      }
      
      public void readNextRequest(Request req) throws IOException {
  	String dummy,token1,token2;
  	int marker;
  	int signal;
  
  	try {
  	    boolean more=true;
              while (more) {
  		marker = ajpin.read();
  		switch (marker) {
  		case 0:       //NOP marker useful for testing if stream is OK
  		    break;
  		    
  		case 1: //beginning of request
  		    req.method().setString(  readString(ajpin, null));
  		     
  		    //Zone
  		    String contextPath = readString(ajpin, null);
  
  		    // GS, the following commented line causes the Apache +
  		    // Jserv + Tomcat combination to hang with a 404!!!
  		    // if("ROOT".equals( contextPath ) ) contextPath="";
  		    if("ROOT".equalsIgnoreCase( contextPath ) ) contextPath=null;
  
  		    // XXX Do not set the context - we need extra info.
  		    // this have to be rewritten !!!
  		    //		    if( contextPath!= null )
  		    //	req.setContext( contextM.getContext( contextPath ));
  		    
  		    //Servlet - not used
  		    dummy=readString(ajpin, null);
  		    
  		    //Server hostname
  		    req.serverName().setString(readString(ajpin, null) );
  		    //System.out.println("XXX hostname: " + req.serverName());
  		    
  		    //Apache document root
  		    dummy = readString(ajpin, null);               
  		    req.pathInfo().setString( readString(ajpin, null));               		    //Apache parsed path-translated XXX Bug in mod_jserv !!!!!
  		    dummy = readString(ajpin, null);
  		    req.queryString().setString( readString(ajpin, null));  
  		    req.remoteAddr().setString(readString(ajpin, ""));
  		    req.remoteHost().setString( readString(ajpin, ""));
                      if (isTomcatAuthentication())
                          dummy=readString(ajpin, null);
                      else req.setRemoteUser( readString(ajpin, null));
  		    req.setAuthType(readString(ajpin, null));
  		    //remote port
  		    dummy = readString(ajpin, null);
  		    //		    System.out.println("XXX rport " + dummy );
  		    
  		    req.method().setString( readString(ajpin, null));
  		    req.requestURI().setString( readString(ajpin, ""));
  
  		    // XXX don't set lookup path - problems with URL rewriting.
  		    // need to be fixed.
  		    //if(contextPath!=null && contextPath.length() >0 )
  		    //lookupPath=requestURI.substring( contextPath.length()+1 );
  
  		    //script filename
  		    dummy = readString(ajpin, null);
  		    //script name
  		    dummy = readString(ajpin, null);                   
  		    req.serverName().setString( readString(ajpin, ""));       
  
  		    int serverPort=80;
  		    try {
  			String p=readString(ajpin, null);
  			//System.out.println("XXX p " + p);
  			if(p==null ) p="80";
  			serverPort= Integer.parseInt(p);
  		    } catch (Exception any) {
  			any.printStackTrace();
  		    }
  		    req.setServerPort( serverPort );
  
                      /* Quick and dirty patch to set https scheme in ajp12
                       * but I recommand using ajp13 instead 
                       */
                      if (serverPort == 443)
                         req.scheme().setString("https");
  
  		    // System.out.println("XXX port: " + req.getServerPort());
  
  		    //server protocol
  		    String proto=readString(ajpin,null);
  		    if( proto==null ) proto="HTTP/1.0";
  		    req.protocol().setString (proto);  
  		    //server signature
  		    dummy = readString(ajpin, "");
  		    //server software
  		    dummy = readString(ajpin, "");
  		    req.setJvmRoute( readString(ajpin, null));                   
  
  		    dummy = readString(ajpin, "");
  		    dummy = readString(ajpin, "");
  		    break;
  
  
                      /**
                       * Marker = 5 will be used by mod_jserv to send environment
  		     * vars as key+value (dynamically configurable).
                       * can be considered as "reserved", and safely ignored by
  		     * other connectors.
  		     * env_vars is (above in this  code) commented out for
  		     * performance issues. so theses env vars are simply
  		     * ignored. (just here for compatibility)
                       * but it is where mod_jserv would place SSL_* env vars
  		     * (by exemple)
                       * See the new parameter for mod_jserv (version > 1.1):
                       * ApJServEnvVar localname remotename
                       *                                            - jluc
                       */
                  case 5: // Environment vars
                      token1 = readString(ajpin, null);
                      token2 = readString(ajpin, "");
                      /*
                       * Env variables should go into the request attributes
                       * table. 
  		     *
  		     * Also, if one of the request attributes is HTTPS=on
                       * assume that there is an SSL connection.
  		     */
                      req.setAttribute(token1, token2);
                      if(token1.equals("HTTPS") && token2.equals("on")) {
                          req.scheme().setString("https");
                      }
                      break;
  
  		case 3: // Header
  		    token1 = readString(ajpin, null);
  		    token2 = readString(ajpin, "");
  // 		    if( "Host".equalsIgnoreCase( token1 )) {
  // 			System.out.println("XXX Host: " + token2);
  // 		    }
  		    req.getMimeHeaders().addValue(token1).setString(token2);
  		    break;
  
  		case 254: // Signal
  		    signal = ajpin.read();
  
  		    if (signal == 0) { // PING implemented as signal
  			try {
  			    // close the socket connection after we send reply
  			    socket.getOutputStream().write(0); // PING reply
  			    sin.close();
  			} catch (IOException ignored) {
  			    req.getContextManager().log("Exception closing, ignored",  ignored);
  			}
                          isPing = true;
                          return;
  		    } else {
  			try {
  			    // close the socket connection before handling any
  			    // signal but get the addresses first so they are
  			    // not corrupted
  			    InetAddress serverAddr = socket.getLocalAddress();
  			    InetAddress clientAddr = socket.getInetAddress();
  			    if ( (signal== 15) &&
  				 isSameAddress(serverAddr, clientAddr) ) {
  				if( secret!=null ) {
  				    String stopMsg=readString(ajpin, "");
  				    if( ! secret.equals( stopMsg ) ) {
  					req.getContextManager().log("Attempt to stop with the wrong secret");
  					return;
  				    }
  				}
                                  ContextManager cm=req.getContextManager();
  				cm.shutdown();
  				cm.log("Exiting" );
  				// same behavior as in past, because it seems
  				// that stopping everything doesn't work -
  				// need to figure
  				// out what happens with the threads ( XXX )
  				System.exit(0);
  				shutdown=true;
  				return;
  			    }
  			    sin.close();
  			} catch (Exception ignored) {
  			    req.getContextManager().log("Ignored exception " +
  						      "processing signal " +
  						      signal, ignored);
  			}
  		    }
  		    return;
  		    
  		case 4:
  		case 255:
  		    more=false;
  		    break;
  		    
  		case -1:
  		    throw new java.io.IOException("Stream closed prematurely");
  		    
  		    
  		default:
  		    throw new java.io.IOException("Stream broken");
  		} // switch
              } // while
  	} catch (IOException ioe) {
  	    throw ioe;
          } catch (Exception e) {
  	    req.getContextManager().log("Uncaught exception handling request", e);
          }
  	
  	// REQUEST_URI may includes query string
  	String requestURI=req.requestURI().toString();
  	int indexQ=requestURI.indexOf("?");
  	int rLen=requestURI.length();
  	if ( (indexQ >-1) && ( indexQ  < rLen) ) {
  	    req.queryString().
  		setString( requestURI.substring(indexQ + 1,
  						requestURI.length()));
  	    req.requestURI().setString( requestURI.substring(0, indexQ));
  	} 
      }
  
      /**
       * Return <code>true</code> if the specified client and server addresses
       * are the same.  This method works around a bug in the IBM 1.1.8 JVM on
       * Linux, where the address bytes are returned reversed in some
       * circumstances.
       *
       * @param server The server's InetAddress
       * @param client The client's InetAddress
       */
      public static boolean isSameAddress(InetAddress server, InetAddress client) {
  	// Compare the byte array versions of the two addresses
  	byte serverAddr[] = server.getAddress();
  	byte clientAddr[] = client.getAddress();
  	if (serverAddr.length != clientAddr.length)
  	    return (false);
  	boolean match = true;
  	for (int i = 0; i < serverAddr.length; i++) {
  	    if (serverAddr[i] != clientAddr[i]) {
  		match = false;
  		break;
  	    }
  	}
  	if (match)
  	    return (true);
  
  	// Compare the reversed form of the two addresses
  	for (int i = 0; i < serverAddr.length; i++) {
  	    if (serverAddr[i] != clientAddr[(serverAddr.length-1)-i])
  		return (false);
  	}
  	return (true);
      }
  
      
      public static int readWord(InputStream in ) throws java.io.IOException {
          int b1 = in.read();
          if( b1 == -1)
              return -1;
  
          int b2 = in.read();
          if ( b2==-1)
              return -1;
  
          return ((int)((b1 << 8) | b2)) & 0xffff;
      }
  
      // UTF8 is a strict superset of ASCII.
      final static String CHARSET = "UTF8";
  
      public static String readString(InputStream in, String def)
  	throws java.io.IOException {
          int len = readWord(in);
  
          if( len == 0xffff)
              return def;
  
          if( len == -1)
              throw new java.io.IOException("Stream broken");
  
          byte[] b = new byte[len];
          int p = 0;
          int r;
          while(p<len) {
              r = in.read(b,p, len - p);
              if( r< 0) {
                  throw new IOException("Stream broken, couldn't " +
  				      "demarshal string :"+len+":"+p);
              }
              p = p+r;
          }
          return new String(b, CHARSET);
      }
  
      public boolean isTomcatAuthentication() {
          return tomcatAuthentication;
      }
  
      public void setTomcatAuthentication(boolean newTomcatAuthentication) {
          tomcatAuthentication = newTomcatAuthentication;
      }
  
      
  }
  
  
  
  
  1.1                  jakarta-tomcat-connectors/jk/java/org/apache/ajp/tomcat33/Ajp12Interceptor.java
  
  Index: Ajp12Interceptor.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]
   *
   */
  
  
  /*
    Based on Ajp11ConnectionHandler and Ajp12 implementation of JServ
  */
  package org.apache.ajp.tomcat33;
  
  import java.io.*;
  import java.net.*;
  import java.util.*;
  import org.apache.tomcat.core.*;
  import org.apache.tomcat.util.net.*;
  import org.apache.tomcat.util.*;
  import org.apache.tomcat.util.http.*;
  
  /* 
   */
  public class Ajp12Interceptor extends PoolTcpConnector
      implements  TcpConnectionHandler{
      private boolean tomcatAuthentication=true;
      String secret;
      
      public Ajp12Interceptor() {
  	super();
      }
      // -------------------- PoolTcpConnector --------------------
  
      protected void localInit() throws Exception {
  	ep.setConnectionHandler( this );
      }
  
      /** Enable the use of a stop secret. The secret will be
       *  randomly generated.
       */
      public void setUseSecret(boolean b ) {
  	secret=Double.toString(Math.random());
      }
  
      /** Explicitely set the stop secret
       */
      public void setSecret( String s ) {
  	secret=s;
      }
      
      public void engineState(ContextManager cm, int state )
  	throws TomcatException
      {
  	if( state!=ContextManager.STATE_START )
  	    return;
  	// the engine is now started, create the ajp12.id
  	// file that will allow us to stop the server and
  	// know that the server is started ok.
  	Ajp12Interceptor tcpCon=this;
  	int portInt=tcpCon.getPort();
  	InetAddress address=tcpCon.getAddress();
  	try {
  	    PrintWriter stopF=new PrintWriter
  		(new FileWriter(cm.getHome() + "/conf/ajp12.id"));
  	    stopF.println( portInt );
  	    if( address==null )
  		stopF.println( "" );
  	    else
  		stopF.println( address.getHostAddress() );
  	    if( secret !=null )
  		stopF.println( secret );
  	    else
  		stopF.println();
  	    stopF.close();
  	} catch( IOException ex ) {
  	    log( "Can't create ajp12.id " + ex );
  	}
      }
  
      // -------------------- Handler implementation --------------------
  
      public Object[] init() {
  	Object thData[]=new Object[2];
  	AJP12Request reqA=new AJP12Request();
  	reqA.setSecret( secret );
  	AJP12Response resA=new AJP12Response();
  	cm.initRequest( reqA, resA );
  	thData[0]=reqA;
  	thData[1]=resA;
  
  	return  thData;
      }
  
      public void setServer( Object cm ) {
  	this.cm=(ContextManager )cm;
      }
  
      public void processConnection(TcpConnection connection, Object[] thData) {
          try {
  	    // XXX - Add workarounds for the fact that the underlying
  	    // serverSocket.accept() call can now time out.  This whole
  	    // architecture needs some serious review.
  	    if (connection == null)
  		return;
  	    Socket socket=connection.getSocket();
  	    if (socket == null)
  		return;
  
  	    socket.setSoLinger( true, 100);
  	    //	    socket.setSoTimeout( 1000); // or what ?
  
  	    AJP12Request reqA=null;
  	    AJP12Response resA=null;
  
  	    if( thData != null ) {
  		reqA=(AJP12Request)thData[0];
  		resA=(AJP12Response)thData[1];
  		if( reqA!=null ) reqA.recycle();
  		if( resA!=null ) resA.recycle();
  //XXX is needed to revert the tomcat auth state? if yes put it into recycle and
  //    and uncomment here                
  //                ((AJP12Request)reqA).setTomcatAuthentication(isTomcatAuthtentication());
  	    }
  
  	    if( reqA==null || resA==null ) {
  		reqA = new AJP12Request();
  		reqA.setSecret( secret );
                  ((AJP12Request)reqA).setTomcatAuthentication(
                                          isTomcatAuthentication());
  		resA=new AJP12Response();
  		cm.initRequest( reqA, resA );
  	    }
  
  	    reqA.setSocket( socket );
  	    resA.setSocket( socket );
  
  	    reqA.readNextRequest();
  
  	    if( reqA.internalAjp() )
  		return;
  
  	    cm.service( reqA, resA );
  	    socket.close();
  	} catch (Exception e) {
  	    log("HANDLER THREAD PROBLEM", e);
  	}
      }
  
      public boolean isTomcatAuthentication() {
          return tomcatAuthentication;
      }
  
      public void setTomcatAuthentication(boolean newTomcatAuthentication) {
          tomcatAuthentication = newTomcatAuthentication;
      }
  }
  
  class AJP12Request extends Request {
      Ajp12 ajp12=new Ajp12();
  
      public AJP12Request() {
      }
  
      void setSecret( String s ) {
  	ajp12.setSecret( s );
      }
      
      public boolean internalAjp() {
  	return ajp12.isPing ||
  	    ajp12.shutdown;
      }
  
      public void readNextRequest() throws IOException {
  	ajp12.readNextRequest( this );
      }
  
      public void setSocket( Socket s ) throws IOException {
  	ajp12.setSocket( s );
      }
  
      public int doRead() throws IOException {
  	if( available <= 0 )
  	    return -1;
  	available--;
  	return ajp12.doRead();
      }
  
      public  int doRead( byte b[], int off, int len ) throws IOException {
  	if( available <= 0 )
  	    return -1;
  	int rd=ajp12.doRead( b,off,len);
  	available -= rd;
  	return rd;
      }
  
      public boolean isTomcatAuthentication() {
          return ajp12.isTomcatAuthentication();
      }
  
      public void setTomcatAuthentication(boolean newTomcatAuthentication) {
          ajp12.setTomcatAuthentication(newTomcatAuthentication);
      }
  }
  
  
  // Ajp use Status: instead of Status
  class AJP12Response extends Response {
      Http10 http=new Http10();
  
      public void recycle() {
          super.recycle();
          http.recycle();
      }
  
      public void setSocket( Socket s ) throws IOException {
  	http.setSocket( s );
      }
  
      public void endHeaders()  throws IOException {
  	super.endHeaders();
  	sendStatus( status, HttpMessages.getMessage( status ));
  	http.sendHeaders( getMimeHeaders() );
      }
  
      public void doWrite( byte buffer[], int pos, int count)
  	throws IOException
      {
  	http.doWrite( buffer, pos, count);
      }
  
      /** Override setStatus
       */
      protected void sendStatus( int status, String message)  throws IOException {
  	http.printHead("Status: " );
  	http.printHead( String.valueOf( status ));
  	http.printHead( " " );
  	http.printHead( message );
  	http.printHead("\r\n");
  
  	// Servlet Engine header will be set per/adapter - smarter adapters will
  	// not send it every time ( have it in C side ), and we may also want
  	// to add informations about the adapter used 
  	if( request.getContext() != null)
  	    setHeader("Servlet-Engine", request.getContext().getEngineHeader());
  
      }
  }
  
  
  
  1.1                  jakarta-tomcat-connectors/jk/java/org/apache/ajp/tomcat33/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.ajp.tomcat33;
  
  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 org.apache.tomcat.core.*;
  import org.apache.tomcat.util.*;
  import org.apache.tomcat.util.http.MimeHeaders;
  import org.apache.tomcat.util.buf.MessageBytes;
  import org.apache.tomcat.util.http.HttpMessages;
  
  /**
   * 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 internal <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]
   */
  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_FORWARD_REQUEST   = 2;
      public static final byte JK_AJP13_SHUTDOWN          = 7;
  	
      // 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;
  
      // 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"
      };
      
      // 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"
      };
  
  
      // ============ Instance Properties ====================
  
      OutputStream out;
      InputStream in;
  
      // Buffer used of output body and headers
      OutputBuffer headersWriter=new OutputBuffer(MAX_PACKET_SIZE);
      Ajp13Packet outBuf = new Ajp13Packet( headersWriter );
      // Buffer used for input body
      Ajp13Packet inBuf  = new Ajp13Packet( MAX_PACKET_SIZE );
      // Boffer 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
  
      public Ajp13() 
      {
          super();
      }
  
      public void recycle() 
      {
        // This is a touch cargo-cultish, but I think wise.
        blen = 0; 
        pos = 0;
        headersWriter.recycle();
      }
      
      /**
       * Associate an open socket with this instance.
       */
      public void setSocket( Socket socket ) throws IOException {
  	socket.setSoLinger( true, 100);
  	out = socket.getOutputStream();
  	in  = socket.getInputStream();
  	pos = 0;
      }
  
      /**
       * Read a new packet from the web server and decode it.  If it's a
       * forwarded request, store its properties in the passed-in Request
       * 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(Request req) throws IOException 
      {
  	// XXX The return values are awful.
  
  	int err = receive(hBuf);
  	if(err < 0) {
  	    return 500;
  	}
  	
  	int type = (int)hBuf.getByte();
  	switch(type) {
  	    
  	case JK_AJP13_FORWARD_REQUEST:
  	    return decodeRequest(req, hBuf);
  	    
  	case JK_AJP13_SHUTDOWN:
  	    return -2;
  	}
  	return 200; // XXX This is actually an error condition 
      }
  
      /**
       * 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.  
       */
      private int decodeRequest( Request req, Ajp13Packet msg ) throws IOException
      {
  	// 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.getMimeHeaders();
  	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 ); 
          }
  
  	byte attributeCode;
          for(attributeCode = msg.getByte() ;
              attributeCode != SC_A_ARE_DONE ;
              attributeCode = msg.getByte()) {
              switch(attributeCode) {
  	    case SC_A_CONTEXT      :
  		//		contextPath = msg.getString();
                  break;
  		
  	    case SC_A_SERVLET_PATH :
  		//log("SC_A_SERVLET_PATH not in use " + msg.getString());
                  break;
  		
  	    case SC_A_REMOTE_USER  :
  		req.setRemoteUser( msg.getString());
                  break;
  		
  	    case SC_A_AUTH_TYPE    :
  		req.setAuthType( msg.getString());
                  break;
  		
  	    case SC_A_QUERY_STRING :
  		msg.getMessageBytes( req.queryString());
                  break;
  		
  	    case SC_A_JVM_ROUTE    :
  		req.setJvmRoute(msg.getString());
                  break;
  		
  	    case SC_A_SSL_CERT     :
  		isSSL = true;
  		req.setAttribute("javax.servlet.request.X509Certificate",
  				 msg.getString());
                  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;
  
  	    default:
  		return 500; // Error
              }
          }
  
          if(isSSL) {
              req.scheme().setString("https");
          }
  
  	// Check to see if there should be a body packet coming along
  	// immediately after
  	MessageBytes clB=headers.getValue("content-length");
          int contentLength = (clB==null) ? -1 : clB.getInt();
      	if(contentLength > 0) {
  	    req.setContentLength( contentLength );
  	    /* Read present data */
  	    int err = receive(inBuf);
              if(err < 0) {
              	return 500;
  	    }
  	    
  	    blen = inBuf.peekInt();
  	    pos = 0;
  	    inBuf.getBytes(bodyBuff);
      	}
      
          return 200; // Success
      }
  
      // ==================== Servlet Input Support =================
      
      /**
       * Return the next byte of request body data (to a servlet).
       *
       * @see Ajp13Request#doRead
       */
      public int doRead() throws IOException 
      {
          if(pos >= blen) {
              if( ! refillReadBuffer()) {
  		return -1;
  	    }
          }
          return (char) bodyBuff[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 Ajp13Request#doRead
       */
      public int doRead(byte[] b, int off, int len) throws IOException 
      {
  	if(pos >= blen) {
  	    if( ! refillReadBuffer()) {
  		return -1;
  	    }
  	}
  
  	if(pos + len <= blen) { // Fear the off by one error
  	    // Sanity check b.length > off + len?
  	    System.arraycopy(bodyBuff, pos, b, off, len);
  	    pos += len;
  	    return len;
  	}
  
  	// Not enough data (blen < pos + len)
  	int toCopy = len;
  	while(toCopy > 0) {
  	    int bytesRemaining = blen - pos;
  	    if(bytesRemaining < 0) 
  		bytesRemaining = 0;
  	    int c = bytesRemaining < toCopy ? bytesRemaining : toCopy;
  
  	    System.arraycopy(bodyBuff, pos, b, off, c);
  
  	    toCopy    -= c;
  
  	    off       += c;
  	    pos       += c; // In case we exactly consume the buffer
  
  	    if(toCopy > 0) 
  		if( ! refillReadBuffer()) { // 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.    
       */
      private boolean refillReadBuffer() throws IOException 
      {
  	// If the server returns an empty packet, assume that that end of
  	// the stream has been reached (yuck -- fix protocol??).
  
  	// Why not use outBuf??
  	inBuf.reset();
  	inBuf.appendByte(JK_AJP13_GET_BODY_CHUNK);
  	inBuf.appendInt(MAX_READ_SIZE);
  	send(inBuf);
  	
  	int err = receive(inBuf);
          if(err < 0) {
  	    throw new IOException();
  	}
  	
      	blen = inBuf.peekInt();
      	pos = 0;
      	inBuf.getBytes(bodyBuff);
  
  	return (blen > 0);
      }    
  
      // ==================== Servlet Output Support =================
      
      /**
       * 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(int status, MimeHeaders headers) throws IOException 
      {
  	// XXX if more headers that MAX_SIZE, send 2 packets!
  
  	outBuf.reset();
          outBuf.appendByte(JK_AJP13_SEND_HEADERS);
          outBuf.appendInt(status);
  	
  	outBuf.appendString(HttpMessages.getMessage( status ));
          
  	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();
          send(outBuf);
      } 
  
      /**
       * 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;
      }
  
      /**
       * Signal the web server that the servlet has finished handling this
       * request, and that the connection can be reused.
       */
      public void finish() throws IOException 
      {
  	outBuf.reset();
          outBuf.appendByte(JK_AJP13_END_RESPONSE);
          outBuf.appendBool(true); // Reuse this connection
          outBuf.end();
          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(  byte b[], int off, int len) throws IOException 
      {
  	int sent = 0;
  	while(sent < len) {
  	    int to_send = len - sent;
  	    to_send = to_send > MAX_SEND_SIZE ? MAX_SEND_SIZE : to_send;
  
  	    outBuf.reset();
  	    outBuf.appendByte(JK_AJP13_SEND_BODY_CHUNK);	        	
  	    outBuf.appendBytes(b, off + sent, to_send);	        
  	    send(outBuf);
  	    sent += to_send;
  	}
      }
      
  
      // ========= Internal Packet-Handling Methods =================
  
      /**
       * 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.
       **/
      private int receive(Ajp13Packet msg) throws IOException {
  	// 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 = in.read( b, 0, H_SIZE );
  	if(rd <= 0) {
  	    return rd;
  	}
  	
  	int len = msg.checkIn();
  	
  	// XXX check if enough space - it's assert()-ed !!!
  
   	int total_read = 0;
    	while (total_read < len) {
  	    rd = in.read( b, 4 + total_read, len - total_read);
              if (rd == -1) {
   		System.out.println( "Incomplete read, deal with it " + len + " " + rd);
                  break;
  		// XXX log
  		// XXX Return an error code?
  	    }
       	    total_read += rd;
  	}
  	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.  
       */
      private void send( Ajp13Packet msg ) throws IOException {
  	msg.end(); // Write the packet header
  	byte b[] = msg.getBuff();
  	int len  = msg.getLen();
  	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(null != out) {        
  	    out.close();
  	}
  	if(null !=in) {
  	    in.close();
  	}
      }
  
      /**
       * 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.  
       */
      public static class Ajp13Packet {
  	byte buff[]; // Holds the bytes of the packet
  	int pos;     // The current read or write position in the buffer
  	OutputBuffer ob;
  
  	int len; 
  	// 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.
  
  	/**
  	 * Create a new packet with an internal buffer of given size.
  	 */
  	public Ajp13Packet( int size ) {
  	    buff = new byte[size];
  	}
  
  	public Ajp13Packet( byte b[] ) {
  	    buff = b;
  	}
  
  	public Ajp13Packet( OutputBuffer ob ) {
  	    this.ob=ob;
  	    buff=ob.getBuffer();
  	}
  	
  	public byte[] getBuff() {
  	    return buff;
  	}
  	
  	public int getLen() {
  	    return len;
  	}
  	
  	public int getByteOff() {
  	    return pos;
  	}
  
  	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 != 0x1234 ) {
  		// 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)'A';
  	    buff[1] = (byte)'B';
  	}
  	
  	/**
  	 * 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 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 ) {
  	    // 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;
  	    }
  
  	    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;
  	}
  
  	
  	// ============ 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 static final String DEFAULT_CHAR_ENCODING = "8859_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, DEFAULT_CHAR_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;
  	}
  
  	// ============== 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/ajp/tomcat33/Ajp13Interceptor.java
  
  Index: Ajp13Interceptor.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat-connectors/jk/java/org/apache/ajp/tomcat33/Ajp13Interceptor.java,v 1.1 2001/06/09 02:56:22 costin Exp $
   * $Revision: 1.1 $
   * $Date: 2001/06/09 02:56:22 $
   *
   * ====================================================================
   *
   * 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.ajp.tomcat33;
  
  import java.io.*;
  import java.net.*;
  import java.util.*;
  import org.apache.tomcat.core.*;
  import org.apache.tomcat.util.net.*;
  import org.apache.tomcat.util.*;
  import org.apache.tomcat.util.log.*;
  
  public class Ajp13Interceptor extends PoolTcpConnector
      implements  TcpConnectionHandler
  {
      public Ajp13Interceptor()
      {
          super();
      }
  
      // -------------------- PoolTcpConnector --------------------
  
      protected void localInit() throws Exception {
  	ep.setConnectionHandler( this );
      }
  
      // -------------------- Handler implementation --------------------
      
      public Object[] init()
      {
          Object thData[]=new Object[3];
          Ajp13Request req=new Ajp13Request();
          Ajp13Response res=new Ajp13Response();
          cm.initRequest(req, res);
          thData[0]=req;
          thData[1]=res;
          thData[2]=new Ajp13();
  
          return  thData;
      }
  
      // XXX
      //    Nothing overriden, right now AJPRequest implment AJP and read
      // everything.
      //    "Shortcuts" to be added here ( Vhost and context set by Apache, etc)
      // XXX handleEndpoint( Endpoint x )
      public void processConnection(TcpConnection connection, Object thData[])
      {
          try {
              if(connection == null) {
                  return;
              }
              Socket socket = connection.getSocket();
              if(socket == null) {
                  return;
              }
  
              socket.setSoLinger( true, 100);
  
              Ajp13 con=null;
              Ajp13Request req=null;
              Ajp13Response res=null;
  
              if(thData != null) {
                  req = (Ajp13Request)thData[0];
                  res = (Ajp13Response)thData[1];
                  con = (Ajp13)thData[2];
                  if(req != null) req.recycle();
                  if(res != null) res.recycle();
                  if(con != null) con.recycle();
              }
  
              if(req == null || res == null || con == null) {
                  req = new Ajp13Request();
                  res = new Ajp13Response();
                  con = new Ajp13();
                  cm.initRequest( req, res );
              }
  	    // XXX
  	    req.ajp13=con;
  	    res.ajp13=con;
  	    
              con.setSocket(socket);
  
              boolean moreRequests = true;
              while(moreRequests) {
  		int status=req.receiveNextRequest();
  
  		if( status==-2) {
  		    // special case - shutdown
  		    // XXX need better communication, refactor it
  		    if( !doShutdown(socket.getLocalAddress(),
  				    socket.getInetAddress())) {
  			moreRequests = false;
  			continue;
  		    }                        
  		}
  		if( status != 200 )
  		    break;
  		
  		cm.service(req, res);
  		
  		req.recycle();
  		res.recycle();
              }
              log("Closing connection", Log.DEBUG);
              con.close();
  	    socket.close();
          } catch (Exception e) {
  	    log("Processing connection " + connection, e);
          }
      }
  
      public void setServer(Object contextM)
      {
          this.cm=(ContextManager)contextM;
      }
      
      protected boolean doShutdown(InetAddress serverAddr,
                                   InetAddress clientAddr)
      {
          try {
  	    // close the socket connection before handling any signal
  	    // but get the addresses first so they are not corrupted			
              if(Ajp12.isSameAddress(serverAddr, clientAddr)) {
  		cm.stop();
  		// same behavior as in past, because it seems that
  		// stopping everything doesn't work - need to figure
  		// out what happens with the threads ( XXX )
  		System.exit(0);
  	    }
  	} catch(Exception ignored) {
  	    log("Ignored " + ignored);
  	}
  	log("Shutdown command ignored");
  	return false;
      }
      
  }
  
  class Ajp13Request extends Request 
  {
      Ajp13 ajp13=new Ajp13();
      
      public Ajp13Request() 
      {
          super();
      }
      
      protected int receiveNextRequest() throws IOException 
      {
  	return ajp13.receiveNextRequest( this );
      }
      
      public int doRead() throws IOException 
      {
  	if( available <= 0 )
  	    return -1;
  	available--;
  	return ajp13.doRead();
      }
      
      public int doRead(byte[] b, int off, int len) throws IOException 
      {
  	if( available <= 0 )
  	    return -1;
  	int rd=ajp13.doRead( b,off, len );
  	available -= rd;
  	return rd;
      }
      
      public void recycle() 
      {
          super.recycle();
  	if( ajp13!=null) ajp13.recycle();
      }
  }
  
  class Ajp13Response extends Response 
  {
      Ajp13 ajp13;
      boolean finished=false;
      
      public Ajp13Response() 
      {
  	super();
      }
  
      public void recycle() {
  	super.recycle();
  	finished=false;
      }
  
      public void setSocket( Socket s ) {
  	ajp13=((Ajp13Request)request).ajp13;
      }
  
      // XXX if more headers that MAX_SIZE, send 2 packets!   
      public void endHeaders() throws IOException 
      {
          super.endHeaders();
      
          if (request.protocol().isNull()) {
              return;
          }
  
  	ajp13.sendHeaders(getStatus(), getMimeHeaders());
      } 
           
      public void finish() throws IOException 
      {
  	if(!finished) {
  	    super.finish();
  		finished = true; // Avoid END_OF_RESPONSE sent 2 times
  	    ajp13.finish();
  	}
      }
      
      public void doWrite(  byte b[], int off, int len) throws IOException 
      {
  	ajp13.doWrite(b, off, len );
      }
      
  }
  
  
  
  1.1                  jakarta-tomcat-connectors/jk/java/org/apache/ajp/tomcat33/JNIConnectionHandler.java
  
  Index: JNIConnectionHandler.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat-connectors/jk/java/org/apache/ajp/tomcat33/JNIConnectionHandler.java,v 1.1 2001/06/09 02:56:22 costin Exp $
   * $Revision: 1.1 $
   * $Date: 2001/06/09 02:56:22 $
   *
   * ====================================================================
   *
   * 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.ajp.tomcat33;
  
  import java.io.IOException;
  import org.apache.tomcat.core.*;
  import org.apache.tomcat.util.*;
  import org.apache.tomcat.util.http.*;
  import java.util.Vector;
  import java.io.File;
  
  // we'll use the system.out/err until the code is stable, then
  // try to logger. Since this is a normal interceptor everything should
  // work
  
  /**
   * Connector for a JNI connections using the API in tomcat.service.
   * You need to set a "connection.handler" property with the class name of
   * the JNI connection handler
   * <br>
   * Based on <code>TcpEndpointConnector</code>
   *
   * @author Gal Shachor <sh...@il.ibm.com>
   */
  public class JNIConnectionHandler extends BaseInterceptor {
  
      public JNIConnectionHandler() {
      }
  
      // -------------------- Config -------------------- 
      boolean nativeLibLoaded=false;
      
      /** Location of the jni library
       */
      public void setNativeLibrary(String lib) {
          // First try to load from the library path
          try {
              System.loadLibrary(lib);
  	    nativeLibLoaded=true;
              System.out.println("Library " + lib +
  			       " was loaded from the lib path");
              return;
          } catch(UnsatisfiedLinkError usl) {
              //usl.printStackTrace();
              System.err.println("Failed to loadLibrary() " + lib);
          }
          
          // Loading from the library path failed
          // Try to load assuming lib is a complete pathname.
          try {
  	    System.load(lib);
  	    nativeLibLoaded=true;
  	    System.out.println("Library " + lib + " loaded");
              return;
          } catch(UnsatisfiedLinkError usl) {
              System.err.println("Failed to load() " + lib);
              //usl.printStackTrace();
          }
          
          // OK, try to load from the default libexec 
          // directory. 
          // libexec directory = tomcat.home + / + libexec
          File f = new File(System.getProperties().getProperty("tomcat.home"),
  			  "libexec");
  
  	String os=System.getProperty( "os.name" ).toLowerCase();
          if( os.indexOf("windows")>= 0) {
              f = new File(f, "jni_connect.dll");
          } else {
              f = new File(f, "jni_connect.so");
          }
          System.load(f.toString());
  	nativeLibLoaded=true;
          System.out.println("Library " + f.toString() + " loaded");
      }
  
      // ==================== hack for server startup  ====================
  
      // JNIEndpoint was called to start tomcat
      // Hack used to set the handler in JNIEndpoint.
      // This works - if we have problems we may take the time
      // and implement a better mechanism
      static JNIEndpoint ep;
      boolean running = true;
  
      public static void setEndpoint(JNIEndpoint jniep)
      {
          ep = jniep;
      }
  
      /** Called when the ContextManger is started
       */
      public void engineInit(ContextManager cm) throws TomcatException {
  	super.engineInit( cm );
  	if(! nativeLibLoaded ) {
  	    throw new TomcatException("Missing connector native library name");
  	}
  	try {
  	    // notify the jni side that jni is set up corectly
  	    ep.setConnectionHandler(this);
  	} catch( Exception ex ) {
  	    throw new TomcatException( ex );
  	}
      }
  
      public void engineShutdown(ContextManager cm) throws TomcatException {
  	try {
  	    // notify the jni side that the jni handler is no longer
  	    // in use ( we shut down )
  	    ep.setConnectionHandler(null);
  	} catch( Exception ex ) {
  	    throw new TomcatException( ex );
  	}
      }
  
      // ==================== callbacks from web server ====================
      
      static Vector pool=new Vector();
      static boolean reuse=true;
      /** Called from the web server for each request
       */
      public void processConnection(long s, long l) {
  	JNIRequestAdapter reqA=null;
  	JNIResponseAdapter resA=null;
  
          try {
  	    
  	    if( reuse ) {
  		synchronized( this ) {
  		    if( pool.size()==0 ) {
  			reqA=new JNIRequestAdapter( cm, this);
  			resA=new JNIResponseAdapter( this );
  			cm.initRequest( reqA, resA );
  		    } else {
  			reqA = (JNIRequestAdapter)pool.lastElement();
  			resA=(JNIResponseAdapter)reqA.getResponse();
  			pool.removeElement( reqA );
  		    }
  		}
  		reqA.recycle();
  		resA.recycle();
  	    } else  {
  		reqA = new JNIRequestAdapter(cm, this);
  		resA =new JNIResponseAdapter(this);
  		cm.initRequest( reqA , resA );
  	    }
  	    
              resA.setRequestAttr(s, l);
      	    reqA.readNextRequest(s, l);
  
  	    //     	    if(reqA.shutdown )
  	    //         		return;
      	    if(resA.getStatus() >= 400) {
          		resA.finish();
      		    return;
      	    }
  
      	    cm.service( reqA, resA );
      	} catch(Exception ex) {
      	    ex.printStackTrace();
      	}
  	if( reuse ) {
  	    synchronized( this ) {
  		pool.addElement( reqA );
  	    }
  	}
      }
  
      // -------------------- Native methods --------------------
      // Calls from tomcat to the web server
      
      native int readEnvironment(long s, long l, String []env);
  
      native int getNumberOfHeaders(long s, long l);
  
      native int readHeaders(long s,
                             long l,
                             String []names,
                             String []values);
  
      native int read(long s,
                      long l,
                      byte []buf,
                      int from,
                      int cnt);
  
      native int startReasponse(long s,
                                long l,
                                int sc,
                                String msg,
                                String []headerNames,
                                String []headerValues,
                                int headerCnt);
  
      native int write(long s,
                       long l,
                       byte []buf,
                       int from,
                       int cnt);
  }
  
  // ==================== Request/Response adapters ====================
  
  class JNIRequestAdapter extends Request {
      JNIConnectionHandler h;
      long s;
      long l;
  
      public JNIRequestAdapter(ContextManager cm,
                               JNIConnectionHandler h) {
      	this.contextM = cm;
      	this.h = h;
      }
  
      public  int doRead(byte b[], int off, int len) throws IOException {
  	if( available <= 0 )
  	    return 0;
          int rc = 0;
  
          while(0 == rc) {
  	        rc = h.read(s, l, b, off, len);
  	        if(0 == rc) {
  	            Thread.currentThread().yield();
  	        }
  	    }
  	available -= rc;
  	return rc;
      }
  
      protected void readNextRequest(long s, long l) throws IOException {
          String []env = new String[15];
          int i = 0;
  
      	this.s = s;
      	this.l = l;
  
          for(i = 0 ; i < 12 ; i++) {
              env[i] = null;
          }
  
          /*
           * Read the environment
           */
          if(h.readEnvironment(s, l, env) > 0) {
      		methodMB.setString( env[0] );
      		uriMB.setString( env[1] );
      		queryMB.setString( env[2] );
      		remoteAddrMB.setString( env[3] );
      		remoteHostMB.setString( env[4] );
      		serverNameMB.setString( env[5] );
              serverPort  = Integer.parseInt(env[6]);
              authType    = env[7];
              remoteUser  = env[8];
              schemeMB.setString(env[9]);
              protoMB.setString( env[10]);
              // response.setServerHeader(env[11]);
              
              if(schemeMB.equalsIgnoreCase("https")) {
                  if(null != env[12]) {
  		            attributes.put("javax.servlet.request.X509Certificate",
  	                               env[12]);
  	            }
  	            
                  if(null != env[13]) {
  		            attributes.put("javax.servlet.request.cipher_suite",
  	                               env[13]);
  	            }
  	            
                  if(null != env[14]) {
  		            attributes.put("javax.servlet.request.ssl_session",
  	                               env[14]);
  	            }
              }
              
              
          } else {
              throw new IOException("Error: JNI implementation error");
          }
  
          /*
           * Read the headers
           */
          int nheaders = h.getNumberOfHeaders(s, l);
          if(nheaders > 0) {
              String []names = new String[nheaders];
              String []values = new String[nheaders];
              if(h.readHeaders(s, l, names, values) > 0) {
                  for(i = 0 ; i < nheaders ; i++) {
                      headers.addValue(names[i]).setString(values[i]);
                  }
              } else {
                  throw new IOException("Error: JNI implementation error");
              }
          }
  
  	// REQUEST_URI may include a query string
  	String requestURI=uriMB.toString();
  	int indexQ=requestURI.indexOf("?");
  	int rLen=requestURI.length();
  	if ( (indexQ >-1) && ( indexQ  < rLen) ) {
  	    queryMB.setString( requestURI.substring(indexQ + 1, requestURI.length()));
  	    uriMB.setString( requestURI.substring(0, indexQ));
  	} 
      }
  }
  
  
  // Ajp use Status: instead of Status
  class JNIResponseAdapter extends Response {
  
      JNIConnectionHandler h;
      long s;
      long l;
  
      public JNIResponseAdapter(JNIConnectionHandler h) {
      	this.h = h;
      }
  
      protected void setRequestAttr(long s, long l) throws IOException {
      	this.s = s;
      	this.l = l;
      }
  
      public void endHeaders() throws IOException {
  
      	if(request.protocol().isNull()) // HTTP/0.9 
  	        return;
  
          super.endHeaders();
          
  	// Servlet Engine header will be set per/adapter - smarter adapters
  	// will not send it every time ( have it in C side ), and we may also
  	// want to add informations about the adapter used 
  	// 	if( request.getContext() != null)
  	// 	    setHeader("Servlet-Engine",
  	// 		      request.getContext().getEngineHeader());
  
          int    hcnt = 0;
          String []headerNames = null;
          String []headerValues = null;
  	// Shouldn't be set - it's a bug if it is
          // headers.removeHeader("Status");
          hcnt = headers.size();
          headerNames = new String[hcnt];
          headerValues = new String[hcnt];
  
          for(int i = 0; i < hcnt; i++) {
              headerNames[i] = headers.getName(i).toString();
              headerValues[i] = headers.getValue(i).toString();
          }
  
          if(h.startReasponse(s, l, status,
  			    HttpMessages.getMessage(status),
  			    headerNames, headerValues, hcnt) <= 0) {
              throw new IOException("JNI startReasponse implementation error");
          }
      }
  
      public void doWrite(byte buf[], int pos, int count) throws IOException {
          if(h.write(s, l, buf, pos, count) <= 0) {
              throw new IOException("JNI implementation error");
          }
      }
  }
  
  
  
  
  1.1                  jakarta-tomcat-connectors/jk/java/org/apache/ajp/tomcat33/JNIEndpoint.java
  
  Index: JNIEndpoint.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat-connectors/jk/java/org/apache/ajp/tomcat33/JNIEndpoint.java,v 1.1 2001/06/09 02:56:22 costin Exp $
   * $Revision: 1.1 $
   * $Date: 2001/06/09 02:56:22 $
   *
   * ====================================================================
   *
   * 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.ajp.tomcat33;
  
  import java.util.*;
  import java.io.*;
  import org.apache.tomcat.core.*;
  import org.apache.tomcat.util.*;
  
  /**
   * Handle incoming JNI connections. This class will be called from
   * native code to start tomcat and for each request. 
   *
   * @author Gal Shachor <sh...@il.ibm.com>
   */
  public class JNIEndpoint {
  
      JNIConnectionHandler handler;
  
      boolean running = false;
  
      // Note: I don't really understand JNI and its use of output
      // streams, but this should really be changed to use
      // tomcat.logging.Logger and not merely System.out  -Alex
      
      public JNIEndpoint() {
      }
  
      // -------------------- Configuration --------------------
  
      // Called back when the server is initializing the handler
      public void setConnectionHandler(JNIConnectionHandler handler ) {
  	this.handler=handler;
  	// the handler is no longer useable
      	if( handler==null ) {
  	    running=false;
  	    notify();
  	    return;
  	}
  
  	System.out.println("Running ...");
  	running=true;
          notify();
      }
  
      // -------------------- JNI Entry points
  
      /** Called by JNI to start up tomcat.
       */
      public int startup(String cmdLine,
                         String stdout,
                         String stderr)
      {
          try {
              if(null != stdout) {
                  System.setOut(new PrintStream(new FileOutputStream(stdout)));
              }
              if(null != stderr) {
                  System.setErr(new PrintStream(new FileOutputStream(stderr)));
              }
          } catch(Throwable t) {
          }
  
  	// We need to make sure tomcat did start successfully and
  	// report this back.
          try {
              JNIConnectionHandler.setEndpoint(this);
  	    // it will call back setHandler !!
              StartupThread startup = new StartupThread(cmdLine,
                                                        this);
              startup.start();
  	    System.out.println("Starting up StartupThread");
              synchronized (this) {
                  wait(60*1000);
              }
  	    System.out.println("End waiting");
          } catch(Throwable t) {
          }
  
          if(running) {
  	    System.out.println("Running fine ");
              return 1;
          }
  	System.out.println("Error - why doesn't run ??");
          return 0;
      }
  
      /** Called by JNI when a new request is received.
       */
      public int service(long s, long l)
      {
          if(running) {
              try {
                  handler.processConnection(s, l);
                  return 1;
              } catch(Throwable t) {
                  // Throwables are not allowed into the native code !!!
              }
          }
          return 0;
      }
  
      public void shutdown()
      {
          System.out.println("JNI In shutdown");
      }
  }
  
  /** Tomcat is started in a separate thread. It may be loaded on demand,
      and we can't take up the request thread, as it may be affect the server.
  
      During startup the JNIConnectionHandler will be initialized and
      will configure JNIEndpoint ( static - need better idea )
   */
  class StartupThread extends Thread {
      String []cmdLine = null;
      JNIEndpoint jniEp = null;
  
      public StartupThread(String cmdLine,
                           JNIEndpoint jniEp) {
          this.jniEp = jniEp;
  
          if(null == cmdLine) {
          	this.cmdLine = new String[0];
          } else {
              Vector v = new Vector();
              StringTokenizer st = new StringTokenizer(cmdLine);
              while (st.hasMoreTokens()) {
                  v.addElement(st.nextToken());
              }
              this.cmdLine = new String[v.size()];
              v.copyInto(this.cmdLine);
          }
      }
  
      public void run() {
          boolean failed = true;
          try {
  	    System.out.println("Calling main" );
              org.apache.tomcat.startup.Tomcat.main(cmdLine);
  	    System.out.println("Main returned" );
              failed = false;
          } catch(Throwable t) {
              t.printStackTrace(); // OK
          } finally {
              if(failed) {
  		System.out.println("Failed ??");
  		// stopEndpoint();
              }
          }
      }
  }
  
  
  
  1.1                  jakarta-tomcat-connectors/jk/java/org/apache/ajp/tomcat33/PoolTcpConnector.java
  
  Index: PoolTcpConnector.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.ajp.tomcat33;
  
  import org.apache.tomcat.util.*;
  import org.apache.tomcat.core.*;
  import org.apache.tomcat.util.net.*;
  import java.io.*;
  import java.net.*;
  import java.util.*;
  
  
  /* Similar with MPM module in Apache2.0. Handles all the details related with
     "tcp server" functionality - thread management, accept policy, etc.
     It should do nothing more - as soon as it get a socket (
     and all socket options are set, etc), it just handle the stream
     to ConnectionHandler.processConnection. (costin)
  */
  
  
  
  /**
   * Connector for a TCP-based connector using the API in tomcat.service.
   * You need to set a "connection.handler" property with the class name of
   * the TCP connection handler
   *
   * @author costin@eng.sun.com
   * @author Gal Shachor [shachor@il.ibm.com]
   */
  public abstract class PoolTcpConnector extends BaseInterceptor
  {
      protected PoolTcpEndpoint ep;
      protected ServerSocketFactory socketFactory;
      // socket factory attriubtes ( XXX replace with normal setters ) 
      protected Hashtable attributes = new Hashtable();
      protected boolean enabled=true;
      protected boolean secure=false;
      
      public PoolTcpConnector() {
      	ep = new PoolTcpEndpoint();
      }
  
      // -------------------- Start/stop --------------------
  
      /** Called when the ContextManger is started
       */
      public void engineInit(ContextManager cm) throws TomcatException {
  	super.engineInit( cm );
  
  	try {
  	    localInit();
  	} catch( Exception ex ) {
  	    throw new TomcatException( ex );
  	}
      }
  
      /** Called when the ContextManger is started
       */
      public void engineStart(ContextManager cm) throws TomcatException {
  	try {
  	    if( socketFactory!=null ) {
  		Enumeration attE=attributes.keys();
  		while( attE.hasMoreElements() ) {
  		    String key=(String)attE.nextElement();
  		    Object v=attributes.get( key );
  		    socketFactory.setAttribute( key, v );
  		}
  	    }
  	    ep.startEndpoint();
  	    log( "Starting on " + ep.getPort() );
  	} catch( Exception ex ) {
  	    throw new TomcatException( ex );
  	}
      }
  
      public void engineShutdown(ContextManager cm) throws TomcatException {
  	try {
  	    ep.stopEndpoint();
  	} catch( Exception ex ) {
  	    throw new TomcatException( ex );
  	}
      }
  
      
      protected abstract void localInit() throws Exception;
      
      // -------------------- Pool setup --------------------
  
      public void setPools( boolean t ) {
  	ep.setPoolOn(t);
      }
  
      public void setMaxThreads( int maxThreads ) {
  	ep.setMaxThreads(maxThreads);
      }
  
      public void setMaxSpareThreads( int maxThreads ) {
  	ep.setMaxSpareThreads(maxThreads);
      }
  
      public void setMinSpareThreads( int minSpareThreads ) {
  	ep.setMinSpareThreads(minSpareThreads);
      }
  
      // -------------------- Tcp setup --------------------
  
      public void setBacklog( int i ) {
  	ep.setBacklog(i);
      }
      
      public void setPort( int port ) {
  	ep.setPort(port);
      	//this.port=port;
      }
  
      public void setAddress(InetAddress ia) {
  	ep.setAddress( ia );
      }
  
      public void setHostName( String name ) {
  	// ??? Doesn't seem to be used in existing or prev code
  	// vhost=name;
      }
  
      public void setSocketFactory( String valueS ) {
  	try {
  	    socketFactory= string2SocketFactory( valueS );
  	    ep.setServerSocketFactory( socketFactory );
  	}catch (Exception ex ) {
  	    ex.printStackTrace();
  	}
      }
  
      // -------------------- Getters --------------------
      
      public PoolTcpEndpoint getEndpoint() {
  	return ep;
      }
      
      public int getPort() {
      	return ep.getPort();
      }
  
      public InetAddress getAddress() {
  	return ep.getAddress();
      }
  
      // -------------------- SocketFactory attriubtes --------------------
      public void setKeystore( String k ) {
  	attributes.put( "keystore", k);
      }
  
      public void setKeyspass( String k ) {
  	attributes.put( "keypass", k);
      }
  
      public static final String SSL_CHECK=
  	"javax.net.ssl.SSLServerSocketFactory";
      public static final String SSL_FACT=
  	"org.apache.tomcat.util.net.SSLSocketFactory";
      
      
      public void setSecure( boolean b ) {
  	enabled=false;
  	secure=false;
  	if( b == true ) {
  	    // 	    if( keystore!=null && ! new File( keystore ).exists() ) {
  	    // 		log("Can't find keystore " + keystore );
  	    // 		return;
  	    // 	    }
  	    try {
  		Class c1=Class.forName( SSL_CHECK );
  	    } catch( Exception ex ) {
  		log( "Can't find JSSE, HTTPS will not be enabled");
  		return;
  	    }
  	    try {
  		Class chC=Class.forName( SSL_FACT );
  		socketFactory=(ServerSocketFactory)chC.newInstance();
  		ep.setServerSocketFactory( socketFactory );
  		log( "Setting ssl socket factory ");
  	    } catch(Exception ex ) {
  		log( "Error loading SSL socket factory ", ex);
  		return;
  	    }
  	}
      	secure=b;
  	enabled=true;
      }
      
      public void setAttribute( String prop, Object value) {
  	attributes.put( prop, value );
      }
  
      private static ServerSocketFactory string2SocketFactory( String val)
  	throws ClassNotFoundException, IllegalAccessException,
  	InstantiationException
      {
  	Class chC=Class.forName( val );
  	return (ServerSocketFactory)chC.newInstance();
      }
  
  //     public static String getExtension( String classN ) {
  // 	int lidot=classN.lastIndexOf( "." );
  // 	if( lidot >0 ) classN=classN.substring( lidot + 1 );
  // 	return classN;
  //     }
  
  }