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...@locus.apache.org on 2000/09/19 09:02:26 UTC

cvs commit: jakarta-tomcat/src/share/org/apache/tomcat/modules/server Ajp12.java Ajp12Interceptor.java Ajp13.java Ajp13Interceptor.java Http10.java Http10Interceptor.java

costin      00/09/19 00:02:26

  Added:       src/share/org/apache/tomcat/modules/server Ajp12.java
                        Ajp12Interceptor.java Ajp13.java
                        Ajp13Interceptor.java Http10.java
                        Http10Interceptor.java
  Log:
  Server adapter refactoring.
  
  The new code makes the implementation closer to the design patterns
  we chosed ( i.e. Interceptors act as an adapter between an external API - the
  protocol implementation - and tomcat internal representation ).
  
  This is the first step in documenting Ajp13 - the protocol is now in
  a single file, easier to read and enhance. We no longer try
  to make ajp13 a "generic-protocol" - we'll stay with TCP-specific. JNI
  doors or other protocols have different requirement and we can
  deal with that after ajp13 is stable.
  
  All other files in this package will slowly disapear, and unless you find
  a design problem with the new organization I'll remove them.
  As usually that will happen after the new code is tested and stable.
  ( now it compile, but I haven't even run it )
  
  TODO:
  - make sure it runs
  - document Ajp13.
  - change Ajp13, Jni and Http to use MessageBytes and make sure they are
  optimized ( we should be now in the race for the last 10-20 object allocations
  per request)
  - enhance Ajp13 with all 2-directional calls we need
  - !!! make sure the buffers are properly used ( one important optimization
  will be to lazy-decode the buffers, but that means the buffer can't
  be reused for other callbacks. The current scheme works fine, and
  I don't expect this to be a problem ). Since this is a low-level
  protocol with a clear interface and function it's important to tune it
  very well.
  
  Revision  Changes    Path
  1.1                  jakarta-tomcat/src/share/org/apache/tomcat/modules/server/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.tomcat.modules.server;
  
  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.logging.*;
  import javax.servlet.*;
  import javax.servlet.http.*;
  
  class Ajp12 {
      Socket socket;
      InputStream sin;
      BufferedInputStream ajpin;
      boolean shutdown=false;
      boolean isPing=false;
      boolean doLog;
  
      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 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.setMethod(  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
  		    String servletName = readString(ajpin, null);
  		    
  		    //Server hostname
  		    req.setServerName( readString(ajpin, null) );
  
  		    //Apache document root
  		    dummy = readString(ajpin, null);               
  		    req.setPathInfo( readString(ajpin, null));               
  		    //Apache parsed path-translated XXX Bug in mod_jserv !!!!!
  		    req.setPathTranslated( readString(ajpin, null));
  		    req.setQueryString( readString(ajpin, null));         
  		    req.setRemoteAddr(readString(ajpin, ""));
  		    req.setRemoteHost( readString(ajpin, ""));
  		    req.setRemoteUser( readString(ajpin, null)); 
  		    req.setAuthType(readString(ajpin, null)); 
  		    //remote port
  		    dummy = readString(ajpin, null);                 
  		    req.setMethod( readString(ajpin, null));
  		    req.setRequestURI( 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.setServerName( readString(ajpin, ""));                
  
  		    int serverPort=80;
  		    try {
  			serverPort= Integer.parseInt(readString(ajpin,"80"));
  		    } catch (Exception any) {
  		    }
  		    req.setServerPort( serverPort );
  
  		    //server protocol
  		    dummy = readString(ajpin, "");  
  		    //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.setScheme("https");
                      }
                      break;
  
  		case 3: // Header
  		    token1 = readString(ajpin, null);
  		    token2 = readString(ajpin, "");
  		    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();
  			    sin.close();
  			    if ( (signal== 15) &&
  				 isSameAddress(serverAddr, clientAddr) ) {
  				// Shutdown - probably apache was stoped with
  				// apachectl stop
  				req.getContextManager().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);
  
  				shutdown=true;
  				return;
  			    }
  			} 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.getRequestURI();
  	int indexQ=requestURI.indexOf("?");
  	int rLen=requestURI.length();
  	if ( (indexQ >-1) && ( indexQ  < rLen) ) {
  	    req.setQueryString( requestURI.substring(indexQ + 1,
  						     requestURI.length()));
  	    req.setRequestURI( 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);
      }
  
      
  }
  
  
  
  
  1.1                  jakarta-tomcat/src/share/org/apache/tomcat/modules/server/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.tomcat.modules.server;
  
  import java.io.*;
  import java.net.*;
  import java.util.*;
  import org.apache.tomcat.core.*;
  import org.apache.tomcat.helper.*;
  import org.apache.tomcat.util.net.*;
  import org.apache.tomcat.util.*;
  import org.apache.tomcat.logging.*;
  import javax.servlet.*;
  import javax.servlet.http.*;
  
  /* 
   */
  public class Ajp12Interceptor extends PoolTcpConnector
      implements  TcpConnectionHandler
  {
      public Ajp12Interceptor() {
  	super();
      }
      // -------------------- PoolTcpConnector --------------------
  
      protected void localInit() throws Exception {
  	ep.setConnectionHandler( this );
      }
  
      // -------------------- Handler implementation --------------------
  
      public Object[] init() {
  	Object thData[]=new Object[2];
  	AJP12Request reqA=new AJP12Request();
  	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();
  	    }
  
  	    if( reqA==null || resA==null ) {
  		reqA = new AJP12Request();
  		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);
  	}
      }
  }
  
  class AJP12Request extends Request {
      Ajp12 ajp12=new Ajp12();
  
      public AJP12Request() {
      }
  
      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 {
  	return ajp12.doRead();
      }
  
      public  int doRead( byte b[], int off, int len ) throws IOException {
  	return ajp12.doRead( b,off,len);
      }
  }
  
  
  // Ajp use Status: instead of Status
  class AJP12Response extends Response {
      HTTP http=new HTTP();
  
      public void setSocket( Socket s ) throws IOException {
  	http.setSocket( s );
      }
      
      public void endHeaders()  throws IOException {
  	super.endHeaders();
  	sendStatus( status, RequestUtil.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/src/share/org/apache/tomcat/modules/server/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.tomcat.modules.server;
  
  import java.io.*;
  import java.net.*;
  import java.util.*;
  import org.apache.tomcat.core.*;
  import org.apache.tomcat.util.*;
  import javax.servlet.*;
  import javax.servlet.http.*;
  
  public class Ajp13
  {
      public static final int MAX_PACKET_SIZE=8192;
      public static final int H_SIZE=4;
  
      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;
  
      public static final byte JK_AJP13_FORWARD_REQUEST   = 2;
      public static final byte JK_AJP13_SHUTDOWN          = 7;
  	
      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 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;
      
      public static final byte JK_AJP13_GET_BODY_CHUNK = 6;
  	
      public static final byte SC_A_CONTEXT       = 1;
      public static final byte SC_A_SERVLET_PATH  = 2;
      public static final byte SC_A_REMOTE_USER   = 3;
      public static final byte SC_A_AUTH_TYPE     = 4;
      public static final byte SC_A_QUERY_STRING  = 5;
      public static final byte SC_A_JVM_ROUTE     = 6;
      public static final byte SC_A_SSL_CERT      = 7;
      public static final byte SC_A_SSL_CIPHER    = 8;
      public static final byte SC_A_SSL_SESSION   = 9;
      public static final byte SC_A_REQ_ATTRIBUTE = 10;
      public static final byte SC_A_ARE_DONE      = (byte)0xFF;
  
      public static final String []methodTransArray = {
          "OPTIONS",
          "GET",
          "HEAD",
          "POST",
          "PUT",
          "DELETE",
          "TRACE"
      };
      
      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"
      };
  
      OutputStream out;
      InputStream in;
  	
      Ajp13Packet outBuf=new Ajp13Packet( MAX_PACKET_SIZE );;
      Ajp13Packet inBuf=new Ajp13Packet( MAX_PACKET_SIZE );;
  
      byte []bodyBuff = new byte[MAX_READ_SIZE];
      int blen;
      int pos;
  
      public Ajp13() 
      {
          super();
      }
  
      public void setSocket( Socket socket ) throws IOException {
  	socket.setSoLinger( true, 100);
  	out = socket.getOutputStream();
  	in = socket.getInputStream();
  	pos=0;
      }
  
      public int receiveNextRequest(Request req) throws IOException 
      {
  	int err = receive(inBuf);
  	if(err < 0) {
  	    return 500;
  	}
  	
  	// XXX right now the only incoming packet is "new request"
  	// We need to deal with arbitrary calls
  	int type = (int)inBuf.getByte();
  	switch(type) {
  	    
  	case JK_AJP13_FORWARD_REQUEST:
  	    return decodeRequest(req, inBuf);
  
  	case JK_AJP13_SHUTDOWN:
  	    return -2;
  	}
  	return 200;
      }
  
      private int decodeRequest( Request req, Ajp13Packet msg ) throws IOException
      {
  	
          boolean isSSL = false;
          byte bsc;
          int  hCount = 0;
  
          /*
           * Read the method and translate it to a String
           */
          bsc        = msg.getByte();
          req.setMethod( methodTransArray[(int)bsc - 1] );
          req.setProtocol( msg.getString());
          req.setRequestURI(  msg.getString());
  
          req.setRemoteAddr( msg.getString());
          req.setRemoteHost( msg.getString());
          req.setServerName( msg.getString());
          req.setServerPort( msg.getInt());
  
  	bsc        = msg.getByte();
          if(bsc != 0) {
              isSSL = true;
          }
  
  	// Decode headers
  	MimeHeaders headers=req.getMimeHeaders();
  	hCount     = msg.getInt();
          for(int i = 0 ; i < hCount ; i++) {
              String hName = null;
  
              int isc = msg.peekInt();
              int hId = isc & 0x000000FF;
  
              isc &= 0x0000FF00;
              if(0x0000A000 == isc) {
                  msg.getInt();
                  hName = headerTransArray[hId - 1];
              } else {
                  hName = msg.getString().toLowerCase();
              }
  
              String hValue = msg.getString();
              headers.addValue( hName ).setString( hValue );
          }
  
          for(bsc = msg.getByte() ;
              bsc != SC_A_ARE_DONE ;
              bsc = msg.getByte()) {
              switch(bsc) {
  	    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 :
  		req.setQueryString( msg.getString());
                  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 :
  		isSSL = true;
  		req.setAttribute(msg.getString(), msg.getString());
                  break;
  
  	    default:
  		return 500;
              }
          }
  
          if(isSSL) {
              req.setScheme("https");
          }
  
  	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(msg);
              if(err < 0) {
              	return 500;
  	    }
  	    
  	    blen = msg.peekInt();
  	    msg.getBytes(bodyBuff);
      	}
      
          return 200;
      }
      
      public int doRead() throws IOException 
      {
          if(pos > blen) {
              refeelReadBuffer();
          }
          return bodyBuff[pos++];
      }
      
      public int doRead(byte[] b, int off, int len) throws IOException 
      {
          // XXXXXX Stupid, but the whole thing must be rewriten ( see super()! )
          for(int i = off ; i < (len + off) ; i++) {
              int a = doRead();
              if(-1 == a) {
                  return i-off;
              }
              b[i] = (byte)a;
          }
          
          return len;
      }
      
      public void recycle() 
      {
      }
      
      public void refeelReadBuffer() throws IOException 
      {
  	inBuf.reset();
  	Ajp13Packet msg = inBuf;
  	msg.appendByte(JK_AJP13_GET_BODY_CHUNK);
  	msg.appendInt(MAX_READ_SIZE);
  	send(msg);
  	
  	int err = receive(msg);
          if(err < 0) {
  	    throw new IOException();
  	}
  	
      	blen = msg.peekInt();
      	pos = 0;
      	msg.getBytes(bodyBuff);
      }    
  
      // ==================== Output ====================
      
      // XXX if more headers that MAX_SIZE, send 2 packets!   
      public void sendHeaders(int status, MimeHeaders headers) throws IOException 
      {
  	outBuf.reset();
          Ajp13Packet msg=outBuf;
          msg.reset();
  
          msg.appendByte(JK_AJP13_SEND_HEADERS);
          msg.appendInt(status);
          msg.appendString("");
          
          msg.appendInt(headers.size());
          
          Enumeration e = headers.names();
          while(e.hasMoreElements()) {
              String headerName = (String)e.nextElement();            
              int sc = headerNameToSc(headerName);
              if(-1 != sc) {
                  msg.appendInt(sc);
              } else {
                  msg.appendString(headerName);
              }
              msg.appendString(headers.getHeader(headerName));
          }
  
          msg.end();
          send(msg);
      } 
           
      public void finish() throws IOException 
      {
  	outBuf.reset();
          Ajp13Packet msg = outBuf;
          msg.reset();
          msg.appendByte(JK_AJP13_END_RESPONSE);
          msg.appendByte((byte)1);        
          msg.end();
          send(msg);
      }
      
      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-Autheticate")) {
  		return SC_RESP_WWW_AUTHENTICATE;
  	    }
              break;          
          }
          
          return -1;
      }
  
      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();
  	    Ajp13Packet buf = outBuf;
  	    buf.reset();
  	    buf.appendByte(JK_AJP13_SEND_BODY_CHUNK);	        	
  	    buf.appendBytes(b, off + sent, to_send);	        
  	    send(buf);
  	    sent += to_send;
  	}
      }
      
  
      public int receive(Ajp13Packet msg) throws IOException {
  	byte b[]=msg.getBuff();
  	
  	int rd=in.read( b, 0, H_SIZE );
  	if( rd<=0 ) {
  	    //	    System.out.println("Rd header returned: " + rd );
  	    return rd;
  	}
  	
  	int len=msg.checkIn();
  	
  	// XXX check if enough space - it's assert()-ed !!!
  	// Can we have only one read ( with unblocking, it can read all at once - but maybe more ) ?
  	//???	len-=4; // header
  	
  	rd=in.read( b, 4, len );
  	if( rd != len ) {
  	    System.out.println( "Incomplete read, deal with it " + len + " " + rd);
  	    // ??? log
  	}
  	// 	msg.dump( "Incoming");
  	return rd;
  	//    System.out.println( "Incoming Packet len=" + len);
      }
  	
      public void send( Ajp13Packet msg ) throws IOException {
  	msg.end();
  	byte b[]=msg.getBuff();
  	int len=msg.getLen();
  	out.write( b, 0, len );
      }
  	
      public void close() throws IOException {
  	if(null != out) {        
  	    out.close();
  	}
  	if(null !=in) {
  	    in.close();
  	}
      }
  
      /** Encapsulated messages passed between Tomcat and Web servers
       */
      public static class Ajp13Packet {
  	// previous name: MsgBuff
  	byte buff[];
  	int len;
  	int pos;
  	int maxlen;
  	
  	public Ajp13Packet( int size ) {
  	    buff=new byte[size];
  	    maxlen=size;
  	}
  	
  	public Ajp13Packet( byte b[] ) {
  	    buff=b;
  	    maxlen=b.length;
  	}
  	
  	public byte[] getBuff() {
  	    return buff;
  	}
  	
  	public void setBuff(byte[] b) {
  	    buff=b;
  	    maxlen = b.length;
  	}
  	
  	public int getLen() {
  	    return len;
  	}
  	
  	public int getMaxLen() {
  	    return maxlen;
  	}
  	
  	/** Verify the buffer and read the len
  	 */
  	public int checkIn() {
  	    pos=4;
  	    int mark=BuffTool.getInt( buff,0 );
  	    len=BuffTool.getInt( buff,2 );
  	    if( mark != 0x1234 ) {
  		System.out.println("BAD packet " + mark);
  		dump( "In: " );
  		return -1;
  	    }
  	    return len;
  	}
  	
  	public void reset() {
  	    len=4;
  	    pos=4;
  	    buff[0]=(byte)'A';
  	    buff[1]=(byte)'B';
  	}
  	
  	public void end() {
  	    len=pos;
  	    setInt( 2, len-4 );
  	}
  	
  	public void setInt(int bpos, int val ) {
  	    BuffTool.addInt( buff, bpos, val );
  	}
  	
  	public void appendByte( byte val ) {
  	    buff[pos] = val;
  	    pos++;
  	}
  	
  	public void appendInt( int val ) {
  	    BuffTool.addInt( buff, pos, val );
  	    pos+=2;
  	}
  	
  	public void appendString( String val ) {
  	    pos=BuffTool.addString( buff, pos, val );
  	}
  	
  	public void appendBytes( byte b[], int off, int len ) {
  	    BuffTool.addInt( buff, pos, len );
  	    pos+=2;
  	    if( pos + len > buff.length ) {
  		System.out.println("Buffer overflow " + buff.length + " " + pos + " " + len );
  	    }
  	    System.arraycopy( b, off, buff, pos, len);
  	    buff[pos+len]=0;
  	    pos+=len;
  	    pos++;
  	}
  
  	public int getInt() {
  	    int res=BuffTool.getInt( buff, pos );
  	    pos+=2;
  	    return res;
  	}
  
  	public int peekInt() {
  	    return BuffTool.getInt( buff, pos );
  	}
  
  	public byte getByte() {
  	    byte res = buff[pos];
  	    pos++;
  	    return res;
  	}
  
  	public byte peekByte() {
  	    return buff[pos];
  	}
  
  	public String getString() throws java.io.UnsupportedEncodingException {
  	    int ll= getInt();
  	    if( (ll == 0xFFFF) || (ll==-1) ) {
  		//	    System.out.println("null string " + ll);
  		return null;
  	    }
  	    String s=BuffTool.getString( buff, pos, ll );
  	    pos +=ll;
  	    pos++;
  	    return s;
  	}
  
  	public int getBytes(byte dest[]) {
  	    int ll= getInt();
  	    if( ll > buff.length ) {
  		System.out.println("XXX Assert failed, buff too small ");
  	    }
  	
  	    if( (ll == 0xFFFF) || (ll==-1) ) {
  		System.out.println("null string " + ll);
  		return 0;
  	    }
  
  	    System.arraycopy( buff, pos,  dest, 0, ll );
  	    pos +=ll;
  	    pos++; // ??? 
  	    return ll;
  	}
  
  	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++ ) {
  		System.out.print( hex( buff[i] ) + " ");
  	    }
  	    System.out.print(" | ");
  	    for( int i=start; i< start+16; 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 + "/" + maxlen );
  
  	    for( int j=0; j<len + 16; j+=16 )
  		hexLine( j );
  	
  	    System.out.println();
  	}
      }
  
  
  }
  
  
  
  1.1                  jakarta-tomcat/src/share/org/apache/tomcat/modules/server/Ajp13Interceptor.java
  
  Index: Ajp13Interceptor.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/modules/server/Ajp13Interceptor.java,v 1.1 2000/09/19 07:02:24 costin Exp $
   * $Revision: 1.1 $
   * $Date: 2000/09/19 07:02:24 $
   *
   * ====================================================================
   *
   * 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.tomcat.modules.server;
  
  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.logging.*;
  import javax.servlet.*;
  import javax.servlet.http.*;
  
  
  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", Logger.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 
      {
  	return ajp13.doRead();
      }
      
      public int doRead(byte[] b, int off, int len) throws IOException 
      {
  	return ajp13.doRead( b,off, len );
      }
      
      public void recycle() 
      {
          super.recycle();
  	ajp13.recycle();
      }
  }
  
  class Ajp13Response extends Response 
  {
      Ajp13 ajp13;
      
      public Ajp13Response() 
      {
  	super();
      }
  
      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.getProtocol() == null) {
              return;
          }
  
  	ajp13.sendHeaders(getStatus(), getMimeHeaders());
      } 
           
      public void finish() throws IOException 
      {
          super.finish();
  	ajp13.finish();
      }
      
      public void doWrite(  byte b[], int off, int len) throws IOException 
      {
  	ajp13.doWrite(b, off, len );
      }
      
  }
  
  
  
  1.1                  jakarta-tomcat/src/share/org/apache/tomcat/modules/server/Http10.java
  
  Index: Http10.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.tomcat.modules.server;
  
  import org.apache.tomcat.core.*;
  import org.apache.tomcat.helper.*;
  import org.apache.tomcat.util.*;
  import org.apache.tomcat.logging.*;
  import java.io.*;
  import java.net.*;
  import java.util.*;
  import javax.servlet.*;
  import javax.servlet.http.*;
  
  public class Http10 {
      private Socket socket;
      private boolean moreRequests = false;
      RecycleBufferedInputStream sin;
      protected OutputStream sout;
  
      byte[] buf;
      int bufSize=2048; // default
      int off=0;
      int count=0;
      public static final String DEFAULT_CHARACTER_ENCODING = "8859_1";
  
      protected static final int DEFAULT_HEAD_BUFFER_SIZE = 1024;
      protected byte[] oBuffer = new byte[DEFAULT_HEAD_BUFFER_SIZE];
      protected int oBufferCount = 0;
  
      static final byte CRLF[]= { (byte)'\r', (byte)'\n' };
      Logger.Helper loghelper = new Logger.Helper("tc_log", this);
      
      public Http10() {
          super();
  	buf=new byte[bufSize];
      }
  
      public void setSocket(Socket socket) throws IOException {
  	if( sin==null)
  	    sin = new RecycleBufferedInputStream ( socket.getInputStream());
  	else
  	    sin.setInputStream( socket.getInputStream());
          this.socket = socket;
      	moreRequests = true;	
  	sout=socket.getOutputStream();
      }
  
      public void recycle() {
  	off=0;
  	count=0;
  	oBufferCount=0;
  	if( sin!=null )  sin.recycle();
      }
  
      // -------------------- HTTP input methods --------------------
      
      public int doRead() throws IOException {
  	return sin.read();
      }
  
      public int doRead(byte[] b, int off, int len) throws IOException {
  	return sin.read(b, off, len);
      }
  
      /**
       * Reads header fields from the specified servlet input stream until
       * a blank line is encountered.
       * @param in the servlet input stream
       * @exception IllegalArgumentException if the header format was invalid 
       * @exception IOException if an I/O error has occurred
       */
      public int readHeaders( MimeHeaders headers )  throws IOException {
  	// use pre-allocated buffer if possible
  	off = count; // where the request line ended
  	
  	while (true) {
  	    int start = off;
  
  	    while (true) {
  		int len = buf.length - off;
  
  		if (len > 0) {
  		    len = readLine(sin,buf, off, len);
  
  		    if (len == -1) {
  			return HttpServletResponse.SC_BAD_REQUEST;
  		    }
  		}
  
  		off += len;
  
  		if (len == 0 || buf[off-1] == '\n') {
  		    break;
  		}
  
  		// overflowed buffer, so temporarily expand and continue
  
  		// XXX DOS - if the length is too big - stop and throw exception
  		byte[] tmp = new byte[buf.length * 2];
  
  		System.arraycopy(buf, 0, tmp, 0, buf.length);
  		buf = tmp;
  	    }
  
  	    // strip off trailing "\r\n"
  	    if (--off > start && buf[off-1] == '\r') {
  		--off;
  	    }
  
  	    if (off == start) {
  		break;
  	    }
  	    
  	    // XXX this does not currently handle headers which
  	    // are folded to take more than one line.
  	    if( ! parseHeaderFiled(headers, buf, start, off - start) ) {
  		// error parsing header
  		return 200;
  	    }
  	}
  	return 200;
      }
  
      /**
       * Parses a header field from a subarray of bytes.
       * @param b the bytes to parse
       * @param off the start offset of the bytes
       * @param len the length of the bytes
       * @exception IllegalArgumentException if the header format was invalid
       */
      public final boolean parseHeaderFiled(MimeHeaders headers, byte[] b,
  					  int off, int len)
      {
  	int start = off;
  	byte c;
  
  	while ((c = b[off++]) != ':' && c != ' ') {
  	    if (c == '\n') {
  		loghelper.log("Parse error, empty line: " +
  			      new String( b, off, len ), Logger.ERROR);
  		return false;
  	    }
  	}
  
  	int nS=start;
  	int nE=off - start - 1;
  
  	while (c == ' ') {
  	    c = b[off++];
  	}
  
  	if (c != ':') {
  	    loghelper.log("Parse error, missing : in  " +
  			  new String( b, off, len ), Logger.ERROR);
  	    loghelper.log("Full  " + new String( b, 0, b.length ),
  			  Logger.ERROR);
  	    return false;
  	}
  
  	while ((c = b[off++]) == ' ');
  
  	headers.addValue( b, nS, nE).
  	    setBytes(b, off-1, len - (off - start - 1));
  	return true;
      }
  
      /** Parse a request line
       */
      public final int processRequestLine(MessageBytes methodMB,
  					MessageBytes uriMB,
  					MessageBytes queryMB,
  					MessageBytes protocolMB)
  	throws IOException
      {
  	count = readLine(sin,buf, 0, buf.length);
  
  	if (count < 0 ) {
  	    return HttpServletResponse.SC_BAD_REQUEST;
  	}
  	
  	off=0;
  
  	// if end of line is reached before we scan all 3 components -
  	// we're fine, off=count and remain unchanged
  	if( buf[count-1]!= '\r' && buf[count-1]!= '\n' ) {
  	    return HttpServletResponse.SC_REQUEST_URI_TOO_LONG;
  	}	    
  	
  	int startMethod=skipSpaces();
  	int endMethod=findSpace();
  
  	int startReq=skipSpaces();
  	int endReq=findSpace();
  
  	int startProto=skipSpaces();
  	int endProto=findSpace();
  
  	if( startReq < 0   ) {
  	    // we don't have 2 "words", probably only method
  	    // startReq>0 => method is fine, request has at least one char
  	    return HttpServletResponse.SC_BAD_REQUEST;
  	}
  
  	methodMB.setBytes( buf, startMethod, endMethod - startMethod );
  	// optimization - detect common strings, no allocations
  	if( buf[startMethod] == 'g' ||
  	    buf[startMethod] == 'G') {
  	    if( methodMB.equalsIgnoreCase( "get" ))
  		methodMB.setString("GET");
  	}
  	if( buf[startMethod] == 'p' ||
  	    buf[startMethod] == 'P' ) {
  	    if( methodMB.equalsIgnoreCase( "post" ))
  		methodMB.setString("POST");
  	    if( methodMB.equalsIgnoreCase( "put" ))
  		methodMB.setString("PUT");
  	}
  
  	if( endReq < 0 ) {
  	    endReq=count;
  	} else {
  	    if( startProto > 0 ) {
  		if( endProto < 0 ) endProto = count;
  		protocolMB.setBytes( buf, startProto, endProto-startProto);
  		if( protocolMB.equalsIgnoreCase( "http/1.0" ))
  		    protocolMB.setString("HTTP/1.0");
  		if( protocolMB.equalsIgnoreCase( "http/1.1" ))
  		    protocolMB.setString("HTTP/1.1");
  	    } else {
  		protocolMB.setString("");
  	    }
  	}
  
  	int qryIdx= findChar( '?', startReq, endReq );
  	if( qryIdx <0 ) {
  	    uriMB.setBytes(buf, startReq, endReq - startReq );
  	    //= new String( buf, startReq, endReq - startReq );
  	} else {
  	    uriMB.setBytes( buf, startReq, qryIdx - startReq );
  	    queryMB.setBytes( buf, qryIdx+1, endReq - qryIdx -1 );
  	}
  
  	// Perform URL decoding only if necessary
  	if ((uriMB.indexOf('%') >= 0) || (uriMB.indexOf('+') >= 0)) {
  	    try {
  		// XXX rewrite URLDecode to avoid allocation
  		String requestURI = uriMB.toString();
  		requestURI = RequestUtil.URLDecode(requestURI);
  		uriMB.setString( requestURI );
  	    } catch (Exception e) {
  		return HttpServletResponse.SC_BAD_REQUEST;
  	    }
  	}
  
  	// XXX what about query strings ?
  	
  	return 200;
      }
  
      // -------------------- Output methods --------------------
  
      /** Format and send the output headers
       */
      public void sendHeaders(MimeHeaders headers)  throws IOException 
      {
  	int count=headers.size();
  	for( int i=0; i<count; i++ ) {
  	    // response headers are set by the servlet, so probably we have
  	    // only Strings.
  	    // XXX date, cookies, etc shoud be extracted from response
  	    printHead( headers.getName( i ).toString() );
  	    printHead(": ");
  	    printHead( headers.getValue( i ).toString() );
  	    printHead("\r\n");
  	}
  	
  	printHead( "\r\n" );
  
  	sout.write( oBuffer, 0, oBufferCount );
  	sout.flush();
      }
  
      /** Needed for AJP  support - the only difference between AJP response and
  	HTTP response is the status line
      */
      public void sendStatus( int status, String message )
  	throws IOException
      {
  	printHead("HTTP/1.0 ");
  	switch( status ) {
  	case 200: printHead("200");
  	    break;
  	case 400: printHead("400");
  	    break;
  	case 404: printHead("404");
  	    break;
  	    
  	default:
  	    printHead(String.valueOf(status));
  	}
  	if(message!=null) {
  	    printHead(" ");
  	    printHead(message);
  	}
  	printHead("\r\n");
      }
  
  
      public void setHttpHeaders(Request req, MimeHeaders headers) {
  	// Hack: set Date header.
  	// This method is overriden by ajp11, ajp12 - so date will not be set
  	// for any of those ( instead the server will generate the date )
  	// This avoids redundant setting of date ( very expensive ).
  	// XXX XXX Check if IIS, NES do generate the date
  	if( false ) {
  	    headers.setValue(  "Date" ).setTime( System.currentTimeMillis());
  	}
  	
  	// 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( req.getContext() != null)
  	    headers.setValue("Servlet-Engine").setString(
  		      req.getContext().getEngineHeader());
      }
  
      
      public void doWrite( byte buffer[], int pos, int count)
  	throws IOException
      {
  	sout.write( buffer, pos, count);
      }
  
      // -------------------- Parsing Utils --------------------
      /** Advance to first non-whitespace
       */
      private  final int skipSpaces() {
  	while (off < count) {
  	    if ((buf[off] != (byte) ' ') 
  		&& (buf[off] != (byte) '\t')
  		&& (buf[off] != (byte) '\r')
  		&& (buf[off] != (byte) '\n')) {
  		return off;
  	    }
  	    off++;
  	}
  	return -1;
      }
  
      /** Advance to the first whitespace character
       */
      private  int findSpace() {
  	while (off < count) {
  	    if ((buf[off] == (byte) ' ') 
  		|| (buf[off] == (byte) '\t')
  		|| (buf[off] == (byte) '\r')
  		|| (buf[off] == (byte) '\n')) {
  		return off;
  	    }
  	    off++;
  	}
  	return -1;
      }
  
      /** Find a character, no side effects
       */
      private  int findChar( char c, int start, int end ) {
  	byte b=(byte)c;
  	int offset = start;
  	while (offset < end) {
  	    if (buf[offset] == b) {
  		return offset;
  	    }
  	    offset++;
  	}
  	return -1;
      }
  
      // cut&paste from ServletInputStream - but it's as inefficient as before
      public int readLine(InputStream in, byte[] b, int off, int len)
  	throws IOException
      {
  	if (len <= 0) {
  	    return 0;
  	}
  	int count = 0, c;
  
  	while ((c = in.read()) != -1) {
  	    b[off++] = (byte)c;
  	    count++;
  	    if (c == '\n' || count == len) {
  		break;
  	    }
  	}
  	return count > 0 ? count : -1;
      }
  
      // From BufferedServletOutputStream
      // XXX will be moved in a new in/out system, temp. code
      // Right now it's not worse than BOS
      public void printHead( String s ) {
  	if (s==null) s="null";
  
  	int len = s.length();
  	for (int i = 0; i < len; i++) {
  	    char c = s.charAt (i);
  	    
  	    //
  	    // XXX NOTE:  This is clearly incorrect for many strings,
  	    // but is the only consistent approach within the current
  	    // servlet framework.  It must suffice until servlet output
  	    // streams properly encode their output.
  	    //
  	    if ((c & 0xff00) != 0) {	// high order byte must be zero
  		// XXX will go away after we change the I/O system
  		loghelper.log("Header character is not iso8859_1, " +
  			      "not supported yet: " + c, Logger.ERROR ) ;
  	    }
  	    if( oBufferCount >= oBuffer.length ) {
  		byte bufferNew[]=new byte[ oBuffer.length * 2 ];
  		System.arraycopy( oBuffer,0, bufferNew, 0, oBuffer.length );
  		oBuffer=bufferNew;
  	    }
  	    oBuffer[oBufferCount] = (byte)c;
  	    oBufferCount++;
  	}
      }    
  
  
  }
  
  
  
  1.1                  jakarta-tomcat/src/share/org/apache/tomcat/modules/server/Http10Interceptor.java
  
  Index: Http10Interceptor.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.tomcat.modules.server;
  
  import java.io.*;
  import java.net.*;
  import java.util.*;
  import org.apache.tomcat.core.*;
  import org.apache.tomcat.helper.*;
  import org.apache.tomcat.util.*;
  import org.apache.tomcat.util.net.*;
  import org.apache.tomcat.util.net.ServerSocketFactory;
  import org.apache.tomcat.logging.*;
  import javax.servlet.*;
  import javax.servlet.http.*;
  
  
  /** Standalone http.
   *
   *  Connector properties:
   *  - secure - will load a SSL socket factory and act as https server
   *
   *  Properties passed to the net layer:
   *  - timeout
   *  - backlog
   *  - address
   *  - port
   * Thread pool properties:
   *  - minSpareThreads
   *  - maxSpareThreads
   *  - maxThreads
   *  - poolOn
   * Properties for HTTPS:
   *  - keystore - certificates - default to ~/.keystore
   *  - keypass - password
   *  - clientauth - true if the server should authenticate the client using certs
   */
  public class Http10Interceptor extends PoolTcpConnector
      implements  TcpConnectionHandler
  {
      public Http10Interceptor() {
  	super();
      }
  
      // -------------------- PoolTcpConnector --------------------
  
      protected void localInit() throws Exception {
  	ep.setConnectionHandler( this );
      }
  
      // -------------------- Attributes --------------------
      // -------------------- Handler implementation --------------------
      public void setServer( Object o ) {
  	this.cm=(ContextManager)o;
      }
      
      public Object[] init() {
  	Object thData[]=new Object[3];
  	// 	HttpRequestAdapter reqA=new HttpRequestAdapter();
  	// 	HttpResponseAdapter resA=new HttpResponseAdapter();
  	HttpRequest reqA=new HttpRequest();
  	HttpResponse resA=new HttpResponse();
  	cm.initRequest( reqA, resA );
  	thData[0]=reqA;
  	thData[1]=resA;
  	thData[2]=null;
  	return  thData;
      }
  
      public void processConnection(TcpConnection connection, Object thData[]) {
  	Socket socket=null;
  	HttpRequest reqA=null;
  	HttpResponse resA=null;
  
  	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;
  
  	    if( thData != null ) {
  		reqA=(HttpRequest)thData[0];
  		resA=(HttpResponse)thData[1];
  		if( reqA!=null ) reqA.recycle();
  		if( resA!=null ) resA.recycle();
  	    }
  
  	    if( reqA==null || resA==null ) {	
  		log("No thread data ??");
  		reqA=new HttpRequest();
  		resA=new HttpResponse();
  		cm.initRequest( reqA, resA );
  	    }
  	    
  	    socket=connection.getSocket();
  	    if (socket == null)
  		return;
  
  	    // InputStream in=socket.getInputStream();
  	    // OutputStream out=socket.getOutputStream();
  	    reqA.setSocket( socket );
  	    // resA.setOutputStream( out );
  
  	    reqA.readNextRequest(resA);
  	    if( secure ) {
  		reqA.setScheme( "https" );
  	    }
  	    
  	    cm.service( reqA, resA );
  
  	    try {
                 InputStream is = socket.getInputStream();
                 int available = is.available ();
  	       
                 // XXX on JDK 1.3 just socket.shutdownInput () which
                 // was added just to deal with such issues.
  
                 // skip any unread (bogus) bytes
                 if (available > 1) {
                     is.skip (available);
                 }
  	    }catch(NullPointerException npe) {
  		// do nothing - we are just cleaning up, this is
  		// a workaround for Netscape \n\r in POST - it is supposed
  		// to be ignored
  	    }
  	}
  	catch(java.net.SocketException e) {
  	    // SocketExceptions are normal
  	    log( "SocketException reading request, ignored", null,
  		 Logger.INFORMATION);
  	    log( "SocketException reading request:", e, Logger.DEBUG);
  	}
  	catch (java.io.IOException e) {
  	    // IOExceptions are normal 
  	    log( "IOException reading request, ignored", null,
  		 Logger.INFORMATION);
  	    log( "IOException reading request:", e, Logger.DEBUG);
  	}
  	// Future developers: if you discover any other
  	// rare-but-nonfatal exceptions, catch them here, and log as
  	// above.
  	catch (Throwable e) {
  	    // any other exception or error is odd. Here we log it
  	    // with "ERROR" level, so it will show up even on
  	    // less-than-verbose logs.
  	    log( "Error reading request, ignored", e, Logger.ERROR);
  	} 
  	finally {
  	    // recycle kernel sockets ASAP
  	    try { if (socket != null) socket.close (); }
  	    catch (IOException e) { /* ignore */ }
          }
      }
  }
  
  class HttpRequest extends Request {
      Http10 http=new Http10();
      private boolean moreRequests = false;
      Socket socket;
      
      public HttpRequest() {
          super();
      }
  
      public void recycle() {
  	super.recycle();
  	http.recycle();
      }
  
      public void setSocket(Socket socket) throws IOException {
  	http.setSocket( socket );
  	this.socket=socket;
      }
  
      public Socket getSocket() {
          return socket;
      }
  
      public int doRead() throws IOException {
  	return http.doRead();
      }
  
      public int doRead(byte[] b, int off, int len) throws IOException {
  	return http.doRead( b, off, len );
      }
      
  
      public void readNextRequest(Response response) throws IOException {
  	int status=http.processRequestLine( methodMB, uriMB,queryMB, protoMB );
  	// XXX remove this after we swich to MB
  	method=methodMB.toString();
  	requestURI=uriMB.toString();
  	queryString=queryMB.toString();
  	protocol=protoMB.toString();
  	
  	if( status > 200 ) {
  	    response.setStatus( status );
  	    return;
  	}
  
  	// for 0.9, we don't have headers!
  	if (! protoMB.equals("")) {
  	    // all HTTP versions with protocol also have headers
  	    // ( 0.9 has no HTTP/0.9 !)
  	    status=http.readHeaders( headers  );
  	    if( status >200 ) {
  		response.setStatus( status );
  		return;
  	    }
  	}
  
  	// XXX detect for real whether or not we have more requests
  	// coming
  	moreRequests = false;	
      }
  
      // -------------------- override special methods
      
      public int getServerPort() {
          return socket.getLocalPort();
      }
  
      public String getRemoteAddr() {
          return socket.getInetAddress().getHostAddress();
      }
      
      public String getRemoteHost() {
  	return socket.getInetAddress().getHostName();
      }    
  
      public String getLocalHost() {
  	InetAddress localAddress = socket.getLocalAddress();
  	localHost = localAddress.getHostName();
  	return localHost;
      }
  }
  
  
  class HttpResponse extends  Response {
      Http10 http;
      
      public HttpResponse() {
          super();
  	http=((HttpRequest)request).http;
      }
  
      public void recycle() {
  	super.recycle();
      }
  
      public void endHeaders()  throws IOException {
  	super.endHeaders();
  	http.sendStatus( status, RequestUtil.getMessage( status ));
  	http.sendHeaders( getMimeHeaders() );
      }
  
      public void doWrite( byte buffer[], int pos, int count)
  	throws IOException
      {
  	http.doWrite( buffer, pos, count);
      }
  }