You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by cr...@locus.apache.org on 2000/08/12 00:40:54 UTC

cvs commit: jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http Constants.java HttpConnector.java HttpProcessor.java HttpRequestImpl.java HttpRequestStream.java HttpResponseImpl.java HttpResponseStream.java LocalStrings.properties

craigmcc    00/08/11 15:40:53

  Added:       catalina/src/share/org/apache/catalina/connector/http
                        Constants.java HttpConnector.java
                        HttpProcessor.java HttpRequestImpl.java
                        HttpRequestStream.java HttpResponseImpl.java
                        HttpResponseStream.java LocalStrings.properties
  Log:
  Migrate to Tomcat 4.0.
  
  Revision  Changes    Path
  1.1                  jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http/Constants.java
  
  Index: Constants.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http/Constants.java,v 1.1 2000/08/11 22:40:52 craigmcc Exp $
   * $Revision: 1.1 $
   * $Date: 2000/08/11 22:40:52 $
   *
   * ====================================================================
   *
   * 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.catalina.connector.http;
  
  
  /**
   * Static constants for this package.
   */
  
  public final class Constants {
  
      public static final String Package = "org.apache.catalina.connector.http";
      public static final String ServerInfo =
  	"Catalina 4.0-dev (HTTP/1.1 Connector)";
  
  }
  
  
  
  1.1                  jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http/HttpConnector.java
  
  Index: HttpConnector.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http/HttpConnector.java,v 1.1 2000/08/11 22:40:52 craigmcc Exp $
   * $Revision: 1.1 $
   * $Date: 2000/08/11 22:40:52 $
   *
   * ====================================================================
   *
   * 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.catalina.connector.http;
  
  
  import java.io.IOException;
  import java.net.InetAddress;
  import java.net.ServerSocket;
  import java.net.Socket;
  import java.util.Stack;
  import java.util.Vector;
  import java.util.Hashtable;
  import java.util.Enumeration;
  import org.apache.catalina.Connector;
  import org.apache.catalina.Container;
  import org.apache.catalina.HttpRequest;
  import org.apache.catalina.HttpResponse;
  import org.apache.catalina.Lifecycle;
  import org.apache.catalina.LifecycleEvent;
  import org.apache.catalina.LifecycleException;
  import org.apache.catalina.LifecycleListener;
  import org.apache.catalina.Logger;
  import org.apache.catalina.Request;
  import org.apache.catalina.Response;
  import org.apache.catalina.util.LifecycleSupport;
  import org.apache.catalina.util.StringManager;
  import org.apache.catalina.net.ServerSocketFactory;
  
  
  /**
   * Implementation of an HTTP/1.1 connector.
   *
   * @author Craig R. McClanahan
   * @author Remy Maucherat
   * @version $Revision: 1.1 $ $Date: 2000/08/11 22:40:52 $
   */
  
  
  public final class HttpConnector
      implements Connector, Lifecycle, Runnable {
  
  
      // ----------------------------------------------------- Instance Variables
  
  
      /**
       * Server socket factory class name.
       */
      private String socketFactoryClassName;
  
  
      /**
       * Server socket factory parameter.
       */
      private Hashtable parameters = new Hashtable();
  
  
      /**
       * The accept count for this Connector.
       */
      private int acceptCount = 10;
  
  
      /**
       * The IP address on which to bind, if any.  If <code>null</code>, all
       * addresses on the server will be bound.
       */
      private String address = null;
  
  
      /**
       * The input buffer size we should create on input streams.
       */
      private int bufferSize = 2048;
  
  
      /**
       * The Container used for processing requests received by this Connector.
       */
      protected Container container = null;
  
  
      /**
       * The set of processors that have ever been created.
       */
      private Vector created = new Vector();
  
  
      /**
       * The current number of processors that have been created.
       */
      private int curProcessors = 0;
  
  
      /**
       * The debugging detail level for this component.
       */
      private int debug = 0;
  
  
      /**
       * Descriptive information about this Connector implementation.
       */
      private static final String info =
  	"org.apache.catalina.connector.http.HttpConnector/1.0";
  
  
      /**
       * The lifecycle event support for this component.
       */
      protected LifecycleSupport lifecycle = new LifecycleSupport(this);
  
  
      /**
       * The minimum number of processors to start at initialization time.
       */
      protected int minProcessors = 5;
  
  
      /**
       * The maximum number of processors allowed, or <0 for unlimited.
       */
      private int maxProcessors = 20;
  
  
      /**
       * The port number on which we listen for HTTP requests.
       */
      private int port = 8080;
  
  
      /**
       * The set of processors that have been created but are not currently
       * being used to process a request.
       */
      private Stack processors = new Stack();
  
  
      /**
       * The request scheme that will be set on all requests received
       * through this connector.
       */
      private String scheme = "http";
  
  
      /**
       * The secure connection flag that will be set on all requests received
       * through this connector.
       */
      private boolean secure = false;
  
  
      /**
       * The server socket through which we listen for incoming TCP connections.
       */
      private ServerSocket serverSocket = null;
  
  
      /**
       * The string manager for this package.
       */
      private StringManager sm =
  	StringManager.getManager(Constants.Package);
  
  
      /**
       * Has this component been started yet?
       */
      private boolean started = false;
  
  
      /**
       * The shutdown signal to our background thread
       */
      private boolean stopped = false;
  
  
      /**
       * The background thread.
       */
      private Thread thread = null;
  
  
      /**
       * The name to register for the background thread.
       */
      private String threadName = null;
  
  
      /**
       * The thread synchronization object.
       */
      private String threadSync = "";
  
  
      // ------------------------------------------------------------- Properties
  
  
      /**
       * Return the accept count for this Connector.
       */
      public int getAcceptCount() {
  
  	return (acceptCount);
  
      }
  
  
      /**
       * Set the accept count for this Connector.
       *
       * @param count The new accept count
       */
      public void setAcceptCount(int count) {
  
  	this.acceptCount = count;
  
      }
  
  
      /**
       * Return the bind IP address for this Connector.
       */
      public String getAddress() {
  
  	return (this.address);
  
      }
  
  
      /**
       * Set the bind IP address for this Connector.
       *
       * @param address The bind IP address
       */
      public void setAddress(String address) {
  
  	this.address = address;
  
      }
  
  
      /**
       * Is this connector available for processing requests?
       */
      public boolean isAvailable() {
  
  	return (started);
  
      }
  
  
      /**
       * Return the input buffer size for this Connector.
       */
      public int getBufferSize() {
  
  	return (this.bufferSize);
  
      }
  
  
      /**
       * Set the input buffer size for this Connector.
       *
       * @param bufferSize The new input buffer size.
       */
      public void setBufferSize(int bufferSize) {
  
  	this.bufferSize = bufferSize;
  
      }
  
  
      /**
       * Return the Container used for processing requests received by this
       * Connector.
       */
      public Container getContainer() {
  
  	return (container);
  
      }
  
  
      /**
       * Set the Container used for processing requests received by this
       * Connector.
       *
       * @param container The new Container to use
       */
      public void setContainer(Container container) {
  
  	this.container = container;
  
      }
  
  
      /**
       * Return the current number of processors that have been created.
       */
      public int getCurProcessors() {
  
  	return (curProcessors);
  
      }
  
  
      /**
       * Return the debugging detail level for this component.
       */
      public int getDebug() {
  
          return (debug);
  
      }
  
  
      /**
       * Set the debugging detail level for this component.
       *
       * @param debug The new debugging detail level
       */
      public void setDebug(int debug) {
  
          this.debug = debug;
  
      }
  
  
      /**
       * Return descriptive information about this Connector implementation.
       */
      public String getInfo() {
  
  	return (info);
  
      }
  
  
      /**
       * Return the minimum number of processors to start at initialization.
       */
      public int getMinProcessors() {
  
  	return (minProcessors);
  
      }
  
  
      /**
       * Set the minimum number of processors to start at initialization.
       *
       * @param minProcessors The new minimum processors
       */
      public void setMinProcessors(int minProcessors) {
  
  	this.minProcessors = minProcessors;
  
      }
  
  
      /**
       * Return the maximum number of processors allowed, or <0 for unlimited.
       */
      public int getMaxProcessors() {
  
  	return (maxProcessors);
  
      }
  
  
      /**
       * Set the maximum number of processors allowed, or <0 for unlimited.
       *
       * @param maxProcessors The new maximum processors
       */
      public void setMaxProcessors(int maxProcessors) {
  
  	this.maxProcessors = maxProcessors;
  
      }
  
  
      /**
       * Return the port number on which we listen for HTTP requests.
       */
      public int getPort() {
  
  	return (this.port);
  
      }
  
  
      /**
       * Set the port number on which we listen for HTTP requests.
       *
       * @param port The new port number
       */
      public void setPort(int port) {
  
  	this.port = port;
  
      }
  
  
      /**
       * Set a server factory parameter.
       * 
       * @param name Name of the parameter
       * @param value Value of the parameter
       */
      public void setParameter(String name, String value) {
          
          parameters.put(name, value);
          
      }
  
  
      /**
       * Return the scheme that will be assigned to requests received
       * through this connector.  Default value is "http".
       */
      public String getScheme() {
  
  	return (this.scheme);
  
      }
  
  
      /**
       * Set the scheme that will be assigned to requests received through
       * this connector.
       *
       * @param scheme The new scheme
       */
      public void setScheme(String scheme) {
  
  	this.scheme = scheme;
  
      }
  
  
      /**
       * Return the secure connection flag that will be assigned to requests
       * received through this connector.  Default value is "false".
       */
      public boolean getSecure() {
  
  	return (this.secure);
  
      }
  
  
      /**
       * Set the secure connection flag that will be assigned to requests
       * received through this connector.
       *
       * @param secure The new secure connection flag
       */
      public void setSecure(boolean secure) {
  
  	this.secure = secure;
  
      }
  
  
      /**
       * Return the server socket factory class name.
       */
      public String getSocketFactory() {
  
  	return (this.socketFactoryClassName);
  
      }
  
  
      /**
       * Set the server socket factory class name.
       */
      public void setSocketFactory(String className) {
          
          socketFactoryClassName = className;
          
      }
  
  
      // --------------------------------------------------------- Public Methods
  
  
      /**
       * Create (or allocate) and return a Request object suitable for
       * specifying the contents of a Request to the responsible Container.
       */
      public Request createRequest() {
  
  	HttpRequestImpl request = new HttpRequestImpl();
  	request.setConnector(this);
  	return (request);
  
      }
  
  
      /**
       * Create (or allocate) and return a Response object suitable for
       * receiving the contents of a Response from the responsible Container.
       */
      public Response createResponse() {
  
  	HttpResponseImpl response = new HttpResponseImpl();
  	response.setConnector(this);
  	return (response);
  
      }
  
  
      // -------------------------------------------------------- Package Methods
  
  
      /**
       * Recycle the specified Processor so that it can be used again.
       *
       * @param processor The processor to be recycled
       */
      void recycle(HttpProcessor processor) {
  
          processors.push(processor);
  
      }
  
  
      // -------------------------------------------------------- Private Methods
  
  
      /**
       * Create (or allocate) and return an available processor for use in
       * processing a specific HTTP request, if possible.  If the maximum
       * allowed processors have already been created and are in use, return
       * <code>null</code> instead.
       */
      private HttpProcessor createProcessor() {
  
  	synchronized (processors) {
  	    if (processors.size() > 0)
  		return ((HttpProcessor) processors.pop());
  	    if ((maxProcessors > 0) && (curProcessors < maxProcessors))
  	        return (newProcessor());
  	    else
  	        return (null);
  	}
  
      }
  
  
      /**
       * Log a message on the Logger associated with our Container (if any).
       *
       * @param message Message to be logged
       */
      private void log(String message) {
  
  	Logger logger = container.getLogger();
  	if (logger != null)
  	    logger.log(threadName + " " + message);
  	else
  	    System.out.println(threadName + " " + message);
  
      }
  
  
      /**
       * Log a message on the Logger associated with our Container (if any).
       *
       * @param message Message to be logged
       * @param throwable Associated exception
       */
      private void log(String message, Throwable throwable) {
  
  	Logger logger = container.getLogger();
  	if (logger != null)
  	    logger.log(threadName + " " + message, throwable);
  	else {
  	    System.out.println(threadName + " " + message);
  	    throwable.printStackTrace(System.out);
  	}
  
      }
  
  
      /**
       * Create and return a new processor suitable for processing HTTP
       * requests and returning the corresponding responses.
       */
      private HttpProcessor newProcessor() {
  
          HttpProcessor processor = new HttpProcessor(this, curProcessors++);
  	if (processor instanceof Lifecycle) {
  	    try {
  	        ((Lifecycle) processor).start();
  	    } catch (LifecycleException e) {
  	        log("newProcessor", e);
  	        return (null);
  	    }
  	}
  	created.addElement(processor);
  	return (processor);
  
      }
  
  
      /**
       * Open and return the server socket for this Connector.  If an IP
       * address has been specified, the socket will be opened only on that
       * address; otherwise it will be opened on all addresses.
       *
       * @exception IOException if an input/output error occurs
       */
      private ServerSocket open() throws IOException {
  
          // First, load the server socket factory
          Class socketFactoryClass = null;
          ServerSocketFactory socketFactory = null;
  
          if (socketFactoryClassName == null) {
              socketFactory = ServerSocketFactory.getDefault();
          } else {
              try {
                  socketFactoryClass = Class.forName(socketFactoryClassName);
                  socketFactory = 
                      (ServerSocketFactory) socketFactoryClass.newInstance();
                  // Set the factory attributes
                  Enumeration parametersNames = parameters.keys();
                  while (parametersNames.hasMoreElements()) {
                      String key = (String) parametersNames.nextElement();
                      Object value = parameters.get(key);
                      socketFactory.setAttribute(key, value);
                  }
              } catch (Exception e) {
                  log(sm.getString("httpConnector.failedSocketFactoryLoading"));
                  return new ServerSocket(port, acceptCount);
              }
          }
  
  	// If no address is specified, open a connection on all addresses
          if (address == null) {
  	    log(sm.getString("httpConnector.allAddresses"));
              try {
                  return socketFactory.createSocket(port, acceptCount);
              } catch(InstantiationException e) {
                  log(sm.getString("httpConnector.failedToCreateSocket"));
                  return new ServerSocket(port, acceptCount);
              }
  	}
  
  	// Open a server socket on the specified address
  	InetAddress[] addresses = InetAddress.getAllByName("localhost");
  	int i;
  	for (i = 0; i < addresses.length; i++) {
  	    if (addresses[i].getHostAddress().equals(address))
  		break;
  	}
  	if (i < addresses.length) {
  	    log(sm.getString("httpConnector.anAddress", address));
              try {
                  return socketFactory.createSocket(port, acceptCount, 
                                                    addresses[i]);
              } catch(InstantiationException e) {
                  log(sm.getString("httpConnector.failedToCreateSocket"));
                  return new ServerSocket(port, acceptCount, addresses[i]);
              }
  	} else {
  	    log(sm.getString("httpConnector.noAddress", address));
              try {
                  return socketFactory.createSocket(port, acceptCount);
              } catch (InstantiationException e) {
                  log(sm.getString("httpConnector.failedToCreateSocket"));
                  return new ServerSocket(port, acceptCount);
              }
  	}
  
      }
  
  
      // ---------------------------------------------- Background Thread Methods
  
  
      /**
       * The background thread that listens for incoming TCP/IP connections and
       * hands them off to an appropriate processor.
       */
      public void run() {
  
          // Loop until we receive a shutdown command
  	while (!stopped) {
  
  	    // Accept the next incoming connection from the server socket
  	    Socket socket = null;
  	    try {
  		socket = serverSocket.accept();
  	    } catch (IOException e) {
  		if (started && !stopped)
  		    log("accept: ", e);
  		break;
  	    }
  
  	    // Hand this socket off to an appropriate processor
  	    HttpProcessor processor = createProcessor();
  	    if (processor == null) {
  		try {
  		    log(sm.getString("httpConnector.noProcessor"));
  		    socket.close();
  		} catch (IOException e) {
  		    ;
  		}
  		continue;
  	    }
  	    processor.assign(socket);
  
  	    // The processor will recycle itself when it finishes
  
  	}
  
  	// Notify the threadStop() method that we have shut ourselves down
  	synchronized (threadSync) {
  	    threadSync.notifyAll();
  	}
  
      }
  
  
      /**
       * Start the background processing thread.
       */
      private void threadStart() {
  
  	log(sm.getString("httpConnector.starting"));
  
  	thread = new Thread(this, threadName);
  	thread.setDaemon(true);
  	thread.start();
  
      }
  
  
      /**
       * Stop the background processing thread.
       */
      private void threadStop() {
  
  	log(sm.getString("httpConnector.stopping"));
  
  	stopped = true;
  	synchronized (threadSync) {
  	    try {
  		threadSync.wait(5000);
  	    } catch (InterruptedException e) {
  		;
  	    }
  	}
  	thread = null;
  
      }
  
  
      // ------------------------------------------------------ Lifecycle Methods
  
  
      /**
       * Add a lifecycle event listener to this component.
       *
       * @param listener The listener to add
       */
      public void addLifecycleListener(LifecycleListener listener) {
  
  	lifecycle.addLifecycleListener(listener);
  
      }
  
  
      /**
       * Remove a lifecycle event listener from this component.
       *
       * @param listener The listener to add
       */
      public void removeLifecycleListener(LifecycleListener listener) {
  
  	lifecycle.removeLifecycleListener(listener);
  
      }
  
  
      /**
       * Begin processing requests via this Connector.
       *
       * @exception LifecycleException if a fatal startup error occurs
       */
      public void start() throws LifecycleException {
  
  	// Validate and update our current state
  	if (started)
  	    throw new LifecycleException
  		(sm.getString("httpConnector.alreadyStarted"));
          threadName = "HttpConnector[" + port + "]";
  	lifecycle.fireLifecycleEvent(START_EVENT, null);
  	started = true;
  
  	// Establish a server socket on the specified port
  	try {
  	    serverSocket = open();
  	} catch (IOException e) {
  	    throw new LifecycleException(threadName + ".open", e);
  	}
  
  	// Start our background thread
  	threadStart();
  
  	// Create the specified minimum number of processors
  	while (curProcessors < minProcessors) {
  	    if ((maxProcessors > 0) && (curProcessors >= maxProcessors))
  		break;
  	    HttpProcessor processor = newProcessor();
  	    recycle(processor);
  	}
  
      }
  
  
      /**
       * Terminate processing requests via this Connector.
       *
       * @exception LifecycleException if a fatal shutdown error occurs
       */
      public void stop() throws LifecycleException {
  
  	// Validate and update our current state
  	if (!started)
  	    throw new LifecycleException
  		(sm.getString("httpConnector.notStarted"));
  	lifecycle.fireLifecycleEvent(STOP_EVENT, null);
  	started = false;
  
  	// Gracefully shut down all processors we have created
  	for (int i = created.size() - 1; i >= 0; i--) {
  	    HttpProcessor processor = (HttpProcessor) created.elementAt(i);
  	    if (processor instanceof Lifecycle) {
  		try {
  		    ((Lifecycle) processor).stop();
  		} catch (LifecycleException e) {
  		    log("HttpConnector.stop", e);
  		}
  	    }
  	}
  
  	// Close the server socket we were using
  	if (serverSocket != null) {
  	    try {
  		serverSocket.close();
  	    } catch (IOException e) {
  		;
  	    }
  	    serverSocket = null;
  	}
  
  	// Stop our background thread
  	threadStop();
  
      }
  
  
  }
  
  
  
  1.1                  jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http/HttpProcessor.java
  
  Index: HttpProcessor.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http/HttpProcessor.java,v 1.1 2000/08/11 22:40:52 craigmcc Exp $
   * $Revision: 1.1 $
   * $Date: 2000/08/11 22:40:52 $
   *
   * ====================================================================
   *
   * 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.catalina.connector.http;
  
  
  import java.io.BufferedInputStream;
  import java.io.InputStream;
  import java.io.IOException;
  import java.io.OutputStream;
  import java.net.InetAddress;
  import java.net.Socket;
  import java.net.URLDecoder;
  import java.util.ArrayList;
  import java.util.Iterator;
  import java.util.Locale;
  import java.util.StringTokenizer;
  import java.util.TreeMap;
  import javax.servlet.ServletException;
  import javax.servlet.http.Cookie;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  import org.apache.catalina.Connector;
  import org.apache.catalina.Container;
  import org.apache.catalina.HttpRequest;
  import org.apache.catalina.HttpResponse;
  import org.apache.catalina.Lifecycle;
  import org.apache.catalina.LifecycleEvent;
  import org.apache.catalina.LifecycleException;
  import org.apache.catalina.LifecycleListener;
  import org.apache.catalina.Logger;
  import org.apache.catalina.util.RequestUtil;
  import org.apache.catalina.util.LifecycleSupport;
  import org.apache.catalina.util.StringManager;
  import org.apache.catalina.util.StringParser;
  
  
  /**
   * Implementation of a request processor (and its associated thread) that may
   * be used by an HttpConnector to process individual requests.  The connector
   * will allocate a processor from its pool, assign a particular socket to it,
   * and the processor will then execute the processing required to complete
   * the request.  When the processor is completed, it will recycle itself.
   *
   * @author Craig R. McClanahan
   * @author Remy Maucherat
   * @version $Revision: 1.1 $ $Date: 2000/08/11 22:40:52 $
   */
  
  final class HttpProcessor
      implements Lifecycle, Runnable {
  
  
      // ----------------------------------------------------------- Constructors
  
  
      /**
       * Construct a new HttpProcessor associated with the specified connector.
       *
       * @param connector HttpConnector that owns this processor
       * @param id Identifier of this HttpProcessor (unique per connector)
       */
      public HttpProcessor(HttpConnector connector, int id) {
  
  	super();
  	this.connector = connector;
  	this.debug = connector.getDebug();
  	this.id = id;
  	this.request = (HttpRequest) connector.createRequest();
  	this.response = (HttpResponse) connector.createResponse();
  	this.threadName =
  	  "HttpProcessor[" + connector.getPort() + "][" + id + "]";
  
      }
  
  
      // ----------------------------------------------------- Instance Variables
  
  
      /**
       * Is there a new socket available?
       */
      private boolean available = false;
  
  
      /**
       * The HttpConnector with which this processor is associated.
       */
      private HttpConnector connector = null;
  
  
      /**
       * The debugging detail level for this component.
       */
      private int debug = 0;
  
  
      /**
       * The identifier of this processor, unique per connector.
       */
      private int id = 0;
  
  
      /**
       * The lifecycle event support for this component.
       */
      private LifecycleSupport lifecycle = new LifecycleSupport(this);
  
  
      /**
       * The match string for identifying a session ID parameter.
       */
      private static final String match =
  	";" + org.apache.catalina.connector.Constants.SessionParameter + "=";
  
  
      /**
       * The string parser we will use for parsing request lines.
       */
      private StringParser parser = new StringParser();
  
  
      /**
       * The HTTP request object we will pass to our associated container.
       */
      private HttpRequest request = null;
  
  
      /**
       * The HTTP response object we will pass to our associated container.
       */
      private HttpResponse response = null;
  
  
      /**
       * The string manager for this package.
       */
      protected StringManager sm =
  	StringManager.getManager(Constants.Package);
  
  
      /**
       * The socket we are currently processing a request for.  This object
       * is used for inter-thread communication only.
       */
      private Socket socket = null;
  
  
      /**
       * Has this component been started yet?
       */
      private boolean started = false;
  
  
      /**
       * The shutdown signal to our background thread
       */
      private boolean stopped = false;
  
  
      /**
       * The background thread.
       */
      private Thread thread = null;
  
  
      /**
       * The name to register for the background thread.
       */
      private String threadName = null;
  
  
      /**
       * The thread synchronization object.
       */
      private String threadSync = "";
  
  
      /**
       * Keep alive indicator.
       */
      private boolean keepAlive = false;
  
  
      /**
       * HTTP/1.1 client.
       */
      private boolean http11 = true;
  
  
      /**
       * True if the client has asked to recieve a request acknoledgement. If so
       * the server will send a preliminary 100 Continue response just after it
       * has successfully parsed the request headers, and before starting 
       * reading the request entity body.
       */
      private boolean sendAck = false;
  
  
      /**
       * Ack string when pipelining HTTP requests.
       */
      private static final byte[] ack = 
          (new String("HTTP/1.1 100 Continue\r\n\r\n")).getBytes();
  
  
      /**
       * CRLF.
       */
      private static final byte[] CRLF = (new String("\r\n")).getBytes();
  
  
      // -------------------------------------------------------- Package Methods
  
  
      /**
       * Process an incoming TCP/IP connection on the specified socket.  Any
       * exception that occurs during processing must be logged and swallowed.
       * <b>NOTE</b>:  This method is called from our Connector's thread.  We
       * must assign it to our own thread so that multiple simultaneous
       * requests can be handled.
       *
       * @param socket TCP socket to process
       */
      synchronized void assign(Socket socket) {
  
          // Wait for the Processor to get the previous Socket
          while (available) {
  	    try {
  	        wait();
  	    } catch (InterruptedException e) {
  	    }
          }
  
  	// Store the newly available Socket and notify our thread
  	this.socket = socket;
  	available = true;
  	notifyAll();
  
  	if ((debug >= 1) && (socket != null))
  	    log(" An incoming request is being assigned");
  
      }
  
  
      // -------------------------------------------------------- Private Methods
  
  
      /**
       * Await a newly assigned Socket from our Connector, or <code>null</code>
       * if we are supposed to shut down.
       */
      private synchronized Socket await() {
  
          // Wait for the Connector to provide a new Socket
          while (!available) {
  	    try {
  	        wait();
  	    } catch (InterruptedException e) {
  	    }
          }
  
  	// Notify the Connector that we have received this Socket
  	Socket socket = this.socket;
  	available = false;
  	notifyAll();
  
  	if ((debug >= 1) && (socket != null))
  	    log("  The incoming request has been awaited");
  
  	return (socket);
  
      }
  
  
  
      /**
       * Log a message on the Logger associated with our Container (if any)
       *
       * @param message Message to be logged
       */
      private void log(String message) {
  
  	Logger logger = connector.getContainer().getLogger();
  	if (logger != null)
  	    logger.log(threadName + " " + message);
  
      }
  
  
      /**
       * Log a message on the Logger associated with our Container (if any)
       *
       * @param message Message to be logged
       * @param throwable Associated exception
       */
      private void log(String message, Throwable throwable) {
  
  	Logger logger = connector.getContainer().getLogger();
  	if (logger != null)
  	    logger.log(threadName + " " + message, throwable);
  
      }
  
  
      /**
       * Parse the value of an <code>Accept-Language</code> header, and add
       * the corresponding Locales to the current request.
       *
       * @param value The value of the <code>Accept-Language</code> header.
       */
      private void parseAcceptLanguage(String value) {
  
  	// Store the accumulated languages that have been requested in
  	// a local collection, sorted by the quality value (so we can
  	// add Locales in descending order).  The values will be ArrayLists
  	// containing the corresponding Locales to be added
  	TreeMap locales = new TreeMap();
  
  	// Preprocess the value to remove all whitespace
  	int white = value.indexOf(' ');
  	if (white < 0)
  	    white = value.indexOf('\t');
  	if (white >= 0) {
  	    StringBuffer sb = new StringBuffer();
  	    int len = value.length();
  	    for (int i = 0; i < len; i++) {
  		char ch = value.charAt(i);
  		if ((ch != ' ') && (ch != '\t'))
  		    sb.append(ch);
  	    }
  	    value = sb.toString();
  	}
  
  	// Process each comma-delimited language specification
  	parser.setString(value);	// ASSERT: parser is available to us
  	int length = parser.getLength();
  	while (true) {
  
  	    // Extract the next comma-delimited entry
  	    int start = parser.getIndex();
  	    if (start >= length)
  		break;
  	    int end = parser.findChar(',');
  	    String entry = parser.extract(start, end).trim();
  	    parser.advance();	// For the following entry
  
  	    // Extract the quality factor for this entry
  	    double quality = 1.0;
  	    int semi = entry.indexOf(";q=");
  	    if (semi >= 0) {
  		try {
  		    quality = Double.parseDouble(entry.substring(semi + 3));
  		} catch (NumberFormatException e) {
  		    quality = 0.0;
  		}
  		entry = entry.substring(0, semi);
  	    }
  
  	    // Skip entries we are not going to keep track of
  	    if (quality < 0.00005)
  		continue;	// Zero (or effectively zero) quality factors
  	    if ("*".equals(entry))
  		continue;	// FIXME - "*" entries are not handled
  
  	    // Extract the language and country for this entry
  	    String language = null;
  	    String country = null;
  	    int dash = entry.indexOf("-");
  	    if (dash < 0) {
  		language = entry;
  		country = "";
  	    } else {
  		language = entry.substring(0, dash);
  		country = entry.substring(dash + 1);
  	    }
  
  	    // Add a new Locale to the list of Locales for this quality level
  	    Locale locale = new Locale(language, country);
  	    Double key = new Double(-quality);	// Reverse the order
  	    ArrayList values = (ArrayList) locales.get(key);
  	    if (values == null) {
  		values = new ArrayList();
  		locales.put(key, values);
  	    }
  	    values.add(locale);
  
  	}
  
  	// Process the quality values in highest->lowest order (due to
  	// negating the Double value when creating the key)
  	Iterator keys = locales.keySet().iterator();
  	while (keys.hasNext()) {
  	    Double key = (Double) keys.next();
  	    ArrayList list = (ArrayList) locales.get(key);
  	    Iterator values = list.iterator();
  	    while (values.hasNext()) {
  		Locale locale = (Locale) values.next();
  		if (debug >= 1)
  		    log(" Adding locale '" + locale + "'");
  		request.addLocale(locale);
  	    }
  	}
  
      }
  
  
      /**
       * Parse and record the connection parameters related to this request.
       *
       * @param socket The socket on which we are connected
       *
       * @exception IOException if an input/output error occurs
       * @exception ServletException if a parsing error occurs
       */
      private void parseConnection(Socket socket)
          throws IOException, ServletException {
  
  	if (debug >= 2)
  	    log("  parseConnection: address=" + socket.getInetAddress() +
  		", port=" + connector.getPort());
  	((HttpRequestImpl) request).setInet(socket.getInetAddress());
  	request.setServerPort(connector.getPort());
  
      }
  
  
      /**
       * Parse the incoming HTTP request headers, and set the appropriate
       * request headers.
       *
       * @param input The input stream connected to our socket
       *
       * @exception IOException if an input/output error occurs
       * @exception ServletException if a parsing error occurs
       */
      private void parseHeaders(InputStream input)
          throws IOException, ServletException {
  
  	while (true) {
  
  	    // Read the next header line
  	    String line = read(input);
  	    if ((line == null) || (line.length() < 1))
  		break;
  
  	    // Parse the header name and value
  	    int colon = line.indexOf(":");
  	    if (colon < 0)
  		throw new ServletException
  		    (sm.getString("httpProcessor.parseHeaders.colon"));
  	    String name = line.substring(0, colon).trim();
  	    String match = name.toLowerCase();
  	    String value = line.substring(colon + 1).trim();
  	    if (debug >= 1)
  	        log(" Header " + name + " = " + value);
  
  	    // Set the corresponding request headers
  	    if (match.equals("authorization")) {
  		request.setAuthorization(value);
  	    } else if (match.equals("accept-language")) {
  		parseAcceptLanguage(value);
  	    } else if (match.equals("cookie")) {
  		Cookie cookies[] = RequestUtil.parseCookieHeader(value);
  		for (int i = 0; i < cookies.length; i++) {
  		    if (cookies[i].getName().equals
  			(org.apache.catalina.connector.Constants.SessionCookie)) {
  			// Override anything requested in the URL
  			request.setRequestedSessionId(cookies[i].getValue());
  			request.setRequestedSessionCookie(true);
  			request.setRequestedSessionURL(false);
  			if (debug >= 1)
  			  log(" Requested cookie session id is " +
  			      ((HttpServletRequest) request.getRequest())
                                .getRequestedSessionId());
  		    } else
  			request.addCookie(cookies[i]);
  		}
  	    } else if (match.equals("content-length")) {
  		int n = -1;
  		try {
  		    n = Integer.parseInt(value);
  		} catch (Exception e) {
  		    throw new ServletException
  			(sm.getString
                           ("httpProcessor.parseHeaders.contentLength"));
  		}
  		request.setContentLength(n);
  	    } else if (match.equals("content-type")) {
  		request.setContentType(value);
  	    } else if (match.equals("host")) {
  		int n = value.indexOf(":");
  		if (n < 0)
  		    request.setServerName(value);
  		else {
  		    request.setServerName(value.substring(0, n).trim());
  		    int port = 80;
  		    try {
  			port = Integer.parseInt(value.substring(n+1).trim());
  		    } catch (Exception e) {
  			throw new ServletException
  			    (sm.getString
                               ("httpProcessor.parseHeaders.portNumber"));
  		    }
  		    request.setServerPort(port);
  		}
  	    } else if (match.equals("connection")) {
                  if ("close".equalsIgnoreCase(value)) {
                      keepAlive = false;
                  }
                  /*
                    if ("keep-alive".equalsIgnoreCase(value)) {
                    keepAlive = true;
                    }
                  */
              } else if (match.equals("expect")) {
                  if ("100-continue".equalsIgnoreCase(value))
                      sendAck = true;
                  else
                      throw new ServletException
                          (sm.getString
                           ("httpProcessor.parseHeaders.unknownExpectation"));
  	    }
  	    if (!match.equals("authorization"))
  		request.addHeader(name, value);
  	}
  
      }
  
  
      /**
       * Parse the incoming HTTP request and set the corresponding HTTP request
       * properties.
       *
       * @param input The input stream attached to our socket
       * @param output The output stream of the socket
       *
       * @exception IOException if an input/output error occurs
       * @exception ServletException if a parsing error occurs
       */
      private void parseRequest(InputStream input, OutputStream output)
          throws IOException, ServletException {
  
  	// Parse the incoming request line
  	String line = read(input);
  	if (line == null)
  	    throw new ServletException
  		(sm.getString("httpProcessor.parseRequest.read"));
  	parser.setString(line);
  
  	int methodStart = parser.skipWhite();
  	int methodEnd = parser.findWhite();
  	int uriStart = parser.skipWhite();
  	int uriEnd = parser.findWhite();
  	int protocolStart = parser.skipWhite();
  	int protocolEnd = parser.findWhite();
  
  	String method = parser.extract(methodStart, methodEnd);
  	String uri = parser.extract(uriStart, uriEnd);
  	String protocol = parser.extract(protocolStart, protocolEnd);
  	if (protocol.length() == 0)
  	    protocol = "HTTP/0.9";
  
          // Now check if the connection should be kept alive after parsing the
          // request.
          if ( protocol.equals("HTTP/1.1") ) {
              http11 = true;
              sendAck = false;
          } else {
              http11 = false;
              sendAck = false;
              // For HTTP/1.0, connection are not persistent by default,
              // unless specified with a Connection: Keep-Alive header.
              keepAlive = false;
          }
          
  	// Validate the incoming request line
  	if (method.length() < 1) {
  	    throw new ServletException
  		(sm.getString("httpProcessor.parseRequest.method"));
  	} else if (uri.length() < 1) {
  	    throw new ServletException
  		(sm.getString("httpProcessor.parseRequest.uri"));
  	}
  
  	// Parse any query parameters out of the request URI
  	int question = uri.indexOf("?");
  	if (question >= 0) {
  	    request.setQueryString(uri.substring(question + 1));
  	    if (debug >= 1)
  	        log(" Query string is " +
  		    ((HttpServletRequest) request.getRequest())
                      .getQueryString());
  	    uri = uri.substring(0, question);
  	} else
  	    request.setQueryString(null);
  
  	// Parse any requested session ID out of the request URI
  	int semicolon = uri.indexOf(match);
  	if (semicolon >= 0) {
  	    String rest = uri.substring(semicolon + match.length());
  	    int semicolon2 = rest.indexOf(";");
  	    if (semicolon2 >= 0) {
  		request.setRequestedSessionId(rest.substring(0, semicolon2));
  		rest = rest.substring(semicolon2);
  	    } else {
  		request.setRequestedSessionId(rest);
  		rest = "";
  	    }
  	    request.setRequestedSessionURL(true);
  	    uri = uri.substring(0, semicolon) + rest;
  	    if (debug >= 1)
  	        log(" Requested URL session id is " +
  		    ((HttpServletRequest) request.getRequest())
                      .getRequestedSessionId());
  	} else {
  	    request.setRequestedSessionId(null);
  	    request.setRequestedSessionURL(false);
  	}
  
  	// Perform decoding on the request URI if necessary
  	if ((uri.indexOf('%') >= 0) || (uri.indexOf('+') >= 0)) {
  	    try {
  		uri = URLDecoder.decode(uri);
  	    } catch (Exception e) {
  		throw new ServletException
  		    (sm.getString("httpProcessor.parseRequest.decode"));
  	    }
  	}
  
  	// Set the corresponding request properties
  	((HttpRequest) request).setMethod(method);
  	request.setProtocol(protocol);
  	((HttpRequest) request).setRequestURI(uri);
  	request.setSecure(connector.getSecure());
  	request.setScheme(connector.getScheme());
  
  	if (debug >= 1)
  	    log(" Request is '" + method + "' for '" + uri +
  		"' with protocol '" + protocol + "'");
  
      }
  
  
      /**
       * Send a confirmation that a request has been processed when pipelining.
       * HTTP/1.1 100 Continue is sent back to the client.
       *
       * @param output Socket output stream
       */
      private void ackRequest(OutputStream output)
          throws IOException {
          if (sendAck)
              output.write(ack);
      }
  
  
      /**
       * Process an incoming HTTP request on the Socket that has been assigned
       * to this Processor.  Any exceptions that occur during processing must be
       * swallowed and dealt with.
       *
       * @param socket The socket on which we are connected to the client
       */
      private void process(Socket socket) {
  
  	boolean ok = true;
  	InputStream input = null;
  	OutputStream output = null;
  
  	// Construct and initialize the objects we will need
  	try {
  	    input = new BufferedInputStream(socket.getInputStream(),
  	    				    connector.getBufferSize());
  	} catch (Exception e) {
  	    log("process.create", e);
  	    ok = false;
  	}
  
          keepAlive = true;
  
          while (ok && keepAlive) {
              
              //System.out.println("Parsing request");
              
              try {
                  request.setStream(input);
                  request.setResponse(response);
                  output = socket.getOutputStream();
                  response.setStream(output);
                  response.setRequest(request);
                  ((HttpServletResponse) response.getResponse()).setHeader
                      ("Server", Constants.ServerInfo);
              } catch (Exception e) {
                  log("process.create", e);
                  ok = false;
              }
  
              // Parse the incoming request
              try {
                  if (ok) {
                      parseConnection(socket);
                      parseRequest(input, output);
                      if (!request.getRequest().getProtocol()
                          .startsWith("HTTP/0"))
                          parseHeaders(input);
                      if (http11) {
                          // Sending a request acknowledge back to the client if
                          // requested.
                          ackRequest(output);
                          // If the protocol is HTTP/1.1, chunking is allowed.
                          ((HttpResponseImpl) response).setAllowChunking(true);
                      }
                  }
              } catch (Exception e) {
                  try {
                      log("process.parse", e);
                      ((HttpServletResponse) response.getResponse()).sendError
                          (HttpServletResponse.SC_BAD_REQUEST);
                  } catch (Exception f) {
                      ;
                  }
                  ok = false;
              }
  
              // Ask our Container to process this request
              try {
                  ((HttpServletResponse) response).addDateHeader
                      ("Date", System.currentTimeMillis());
                  if (ok) {
                      connector.getContainer().invoke(request, response);
                  }
              } catch (ServletException e) {
                  log("process.invoke", e);
                  try {
                      ((HttpServletResponse) response.getResponse()).sendError
                          (HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                  } catch (Exception f) {
                      ;
                  }
                  ok = false;
              } catch (Throwable e) {
                  log("process.invoke", e);
                  try {
                      ((HttpServletResponse) response.getResponse()).sendError
                          (HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                  } catch (Exception f) {
                      ;
                  }
                  ok = false;
              }
              
              // Finish up the handling of the request
              try {
                  if (ok) {
                      ((HttpResponseImpl) response).finishResponse();
                      ((HttpRequestImpl) request).finishRequest();
                  }
                  if (output != null)
                      output.flush();
              } catch (IOException e) {
                  ok = false;
              }
              
              // We have to check if the connection closure has been requested
              // by the application or the response stream (in case of HTTP/1.0
              // and keep-alive).
              if ( "close".equals(((HttpResponseImpl) response)
                                  .getHeader("Connection")) ) {
                  keepAlive = false;
              }
              
              // Recycling the request and the response objects
              request.recycle();
              response.recycle();
              
          }
          
  	try {
              socket.close();
  	} catch (IOException e) {
  	    ;
  	}
  	socket = null;
  
      }
  
  
      /**
       * Read a line from the specified input stream, and strip off the
       * trailing carriage return and newline (if any).  Return the remaining
       * characters that were read as a String.
       *
       * @param input The input stream connected to our socket
       *
       * @return The line that was read, or <code>null</code> if end-of-file
       *  was encountered
       *
       * @exception IOException if an input/output error occurs
       */
      private String read(InputStream input) throws IOException {
  
  	StringBuffer sb = new StringBuffer();
  	while (true) {
  	    int ch = input.read();
  	    if (ch < 0) {
  		if (sb.length() == 0) {
  		    return (null);
  		} else {
  		    break;
  		}
  	    } else if (ch == '\r') {
  		continue;
  	    } else if (ch == '\n') {
  		break;
  	    }
  	    sb.append((char) ch);
  	}
  	if (debug >= 2)
  	    log("  Read: " + sb.toString());
  	return (sb.toString());
  
      }
  
  
      // ---------------------------------------------- Background Thread Methods
  
  
      /**
       * The background thread that listens for incoming TCP/IP connections and
       * hands them off to an appropriate processor.
       */
      public void run() {
  
          // Process requests until we receive a shutdown signal
  	while (!stopped) {
  
  	    // Wait for the next socket to be assigned
  	    Socket socket = await();
  	    if (socket == null)
  		continue;
  
  	    // Process the request from this socket
  	    process(socket);
  
  	    // Finish up this request
  	    connector.recycle(this);
  
  	}
  
  	// Tell threadStop() we have shut ourselves down successfully
  	synchronized (threadSync) {
  	    threadSync.notifyAll();
  	}
  
      }
  
  
      /**
       * Start the background processing thread.
       */
      private void threadStart() {
  
  	log(sm.getString("httpProcessor.starting"));
  
  	thread = new Thread(this, threadName);
  	thread.setDaemon(true);
  	thread.start();
  
  	if (debug >= 1)
  	    log(" Background thread has been started");
  
      }
  
  
      /**
       * Stop the background processing thread.
       */
      private void threadStop() {
  
  	log(sm.getString("httpProcessor.stopping"));
  
  	stopped = true;
          assign(null);
  	synchronized (threadSync) {
  	    try {
  		threadSync.wait(5000);
  	    } catch (InterruptedException e) {
  		;
  	    }
  	}
  	thread = null;
  
      }
  
  
      // ------------------------------------------------------ Lifecycle Methods
  
  
      /**
       * Add a lifecycle event listener to this component.
       *
       * @param listener The listener to add
       */
      public void addLifecycleListener(LifecycleListener listener) {
  
  	lifecycle.addLifecycleListener(listener);
  
      }
  
  
      /**
       * Remove a lifecycle event listener from this component.
       *
       * @param listener The listener to add
       */
      public void removeLifecycleListener(LifecycleListener listener) {
  
  	lifecycle.removeLifecycleListener(listener);
  
      }
  
  
      /**
       * Start the background thread we will use for request processing.
       *
       * @exception LifecycleException if a fatal startup error occurs
       */
      public void start() throws LifecycleException {
  
  	if (started)
  	    throw new LifecycleException
  		(sm.getString("httpProcessor.alreadyStarted"));
  	lifecycle.fireLifecycleEvent(START_EVENT, null);
  	started = true;
  
  	threadStart();
  
      }
  
  
      /**
       * Stop the background thread we will use for request processing.
       *
       * @exception LifecycleException if a fatal shutdown error occurs
       */
      public void stop() throws LifecycleException {
  
  	if (!started)
  	    throw new LifecycleException
  		(sm.getString("httpProcessor.notStarted"));
  	lifecycle.fireLifecycleEvent(STOP_EVENT, null);
  	started = false;
  
  	threadStop();
  
      }
  
  
  }
  
  
  
  1.1                  jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http/HttpRequestImpl.java
  
  Index: HttpRequestImpl.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http/HttpRequestImpl.java,v 1.1 2000/08/11 22:40:52 craigmcc Exp $
   * $Revision: 1.1 $
   * $Date: 2000/08/11 22:40:52 $
   *
   * ====================================================================
   *
   * 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.catalina.connector.http;
  
  
  import java.io.IOException;
  import java.net.InetAddress;
  import javax.servlet.ServletInputStream;
  import org.apache.catalina.connector.HttpRequestBase;
  
  
  /**
   * Implementation of <b>HttpRequest</b> specific to the HTTP connector.
   *
   * @author Craig R. McClanahan
   * @version $Revision: 1.1 $ $Date: 2000/08/11 22:40:52 $
   */
  
  final class HttpRequestImpl
      extends HttpRequestBase {
  
  
      // ----------------------------------------------------- Instance Variables
  
  
      /**
       * The InetAddress of the remote client of ths request.
       */
      protected InetAddress inet = null;
  
  
      /**
       * Descriptive information about this Request implementation.
       */
      protected static final String info =
  	"org.apache.catalina.connector.http.HttpRequestImpl/1.0";
  
  
      // ------------------------------------------------------------- Properties
  
  
      /**
       * [Package Private] Return the InetAddress of the remote client of
       * this request.
       */
      InetAddress getInet() {
  
  	return (inet);
  
      }
  
  
      /**
       * [Package Private] Set the InetAddress of the remote client of
       * this request.
       *
       * @param inet The new InetAddress
       */
      void setInet(InetAddress inet) {
  
  	this.inet = inet;
  
      }
  
  
      /**
       * Return descriptive information about this Request implementation and
       * the corresponding version number, in the format
       * <code>&lt;description&gt;/&lt;version&gt;</code>.
       */
      public String getInfo() {
  
  	return (info);
  
      }
  
  
      // --------------------------------------------------------- Public Methods
  
  
      /**
       * Release all object references, and initialize instance variables, in
       * preparation for reuse of this object.
       */
      public void recycle() {
  
  	super.recycle();
  	inet = null;
  
      }
  
  
      /**
       * Create and return a ServletInputStream to read the content
       * associated with this Request.  The default implementation creates an
       * instance of RequestStream associated with this request, but this can
       * be overridden if necessary.
       *
       * @exception IOException if an input/output error occurs
       */
      public ServletInputStream createInputStream() throws IOException {
  
  	return (new HttpRequestStream(this));
  
      }
  
  
      // ------------------------------------------------- ServletRequest Methods
  
  
      /**
       * Return the Internet Protocol (IP) address of the client that sent
       * this request.
       */
      public String getRemoteAddr() {
  
  	return (inet.getHostAddress());
  
      }
  
  
      /**
       * Return the fully qualified name of the client that sent this request,
       * or the IP address of the client if the name cannot be determined.
       */
      public String getRemoteHost() {
  
  	return (inet.getHostName());
  
      }
  
  
      // --------------------------------------------- HttpServletRequest Methods
  
  
  }
  
  
  
  1.1                  jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http/HttpRequestStream.java
  
  Index: HttpRequestStream.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http/HttpRequestStream.java,v 1.1 2000/08/11 22:40:52 craigmcc Exp $
   * $Revision: 1.1 $
   * $Date: 2000/08/11 22:40:52 $
   *
   * ====================================================================
   * 
   * 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.catalina.connector.http;
  
  import java.io.IOException;
  import org.apache.catalina.Request;
  import org.apache.catalina.connector.RequestStream;
  
  /**
   * 
   *
   * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
   */
  public class HttpRequestStream extends RequestStream {
      
      
      // ----------------------------------------------------------- Constructors
  
  
      /**
       * Construct a servlet input stream associated with the specified Request.
       *
       * @param request The associated request
       */
      public HttpRequestStream(HttpRequestImpl request) {
  
  	super(request);
          String transferEncoding = request.getHeader("Transfer-Encoding");
          
          chunk = ((transferEncoding != null) 
                   && (transferEncoding.indexOf("chunked") != -1));
          
      }
      
      
      // ----------------------------------------------------- Instance Variables
      
      
      /**
       * Use chunking ?
       */
      protected boolean chunk = false;
      
      
      /**
       * True if the final chunk was found.
       */
      protected boolean endChunk = false;
      
      
      /**
       * Chunk buffer.
       */
      protected byte[] chunkBuffer = null;
      
      
      /**
       * Chunk length.
       */
      protected int chunkLength = 0;
      
      
      /**
       * Chunk buffer position.
       */
      protected int chunkPos = 0;
      
      
      // --------------------------------------------------------- Public Methods
      
      
      /**
       * Close this input stream.  No physical level I-O is performed, but
       * any further attempt to read from this stream will throw an IOException.
       * If a content length has been set but not all of the bytes have yet been
       * consumed, the remaining bytes will be swallowed.
       */
      public void close()
          throws IOException {
          
  	if (closed)
  	    throw new IOException(sm.getString("requestStream.close.closed"));
          
          if (chunk) {
              
              while (!endChunk) {
                  int b = read();
                  if (b < 0)
                      break;
              }
              
          } else {
              
              if (length > 0) {
                  while (count < length) {
                      int b = read();
                      if (b < 0)
                          break;
                  }
              }
              
          }
          
  	closed = true;
          
      }
      
      
      /**
       * Read and return a single byte from this input stream, or -1 if end of
       * file has been encountered.
       *
       * @exception IOException if an input/output error occurs
       */
      public int read()
          throws IOException {
          
          // Has this stream been closed?
          if (closed)
              throw new IOException(sm.getString("requestStream.read.closed"));
          
          if (chunk) {
              
              if (endChunk)
                  return (-1);
              
              if ((chunkBuffer == null)
                  || (chunkPos == chunkLength)) {
                  
                  chunkPos = 0;
                  
                  try {
                      chunkLength = Integer.parseInt(readLine(), 16);
                  } catch (NumberFormatException e) {
                      // Critical error, unable to parse the chunk length
                      chunkLength = 0;
                      chunk = false;
                      close();
                      return -1;
                  }
                  
                  if (chunkLength == 0) {
                      
                      // Skipping trailing headers, if any
                      String trailingLine = readLine();
                      while (!trailingLine.equals(""))
                          trailingLine = readLine();
                      endChunk = true;
                      return (-1);
                      // TODO : Should the stream be automatically closed ?
                      
                  } else {
                      
                      if ((chunkBuffer == null)
                          || (chunkLength > chunkBuffer.length))
                          chunkBuffer = new byte[chunkLength];
                      
                      // Now read the whole chunk into the buffer
                      
                      int nbRead = 0;
                      int currentRead = 0;
                      
                      while (nbRead < chunkLength) {
                          currentRead = 
                              stream.read(chunkBuffer, nbRead, 
                                          chunkLength - nbRead);
                          if (currentRead == -1)
                              throw new IOException
                                  (sm.getString("requestStream.read.error"));
                          nbRead += currentRead;
                      }
                      
                      // Skipping the CRLF
                      stream.read();
                      stream.read();
                      
                  }
                  
              }
              
              return (chunkBuffer[chunkPos++]);
              
          } else {
              
              // Have we read the specified content length already?
              if ((length >= 0) && (count >= length))
                  return (-1);	// End of file indicator
              
              // Read and count the next byte, then return it
              int b = stream.read();
              if (b >= 0)
                  count++;
              return (b);
              
          }
          
      }
  
  
      // -------------------------------------------------------- Private Methods
      
  
      /**
       * Reads the input stream, one line at a time. Reads bytes into an array, 
       * until it reads a certain number of bytes or reaches a newline character,
       * which it reads into the array as well.
       * 
       * @param input Input stream on which the bytes are read
       * @return The line that was read, or <code>null</code> if end-of-file
       *  was encountered
       * @exception IOException	if an input or output exception has occurred
       */
      private String readLine() 
          throws IOException {
          
  	StringBuffer sb = new StringBuffer();
  	while (true) {
  	    int ch = stream.read();
  	    if (ch < 0) {
  		if (sb.length() == 0) {
  		    return (null);
  		} else {
  		    break;
  		}
  	    } else if (ch == '\r') {
  		continue;
  	    } else if (ch == '\n') {
  		break;
  	    }
  	    sb.append((char) ch);
  	}
  	return (sb.toString());
          
      }
      
      
  }
  
  
  
  1.1                  jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http/HttpResponseImpl.java
  
  Index: HttpResponseImpl.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http/HttpResponseImpl.java,v 1.1 2000/08/11 22:40:52 craigmcc Exp $
   * $Revision: 1.1 $
   * $Date: 2000/08/11 22:40:52 $
   *
   * ====================================================================
   *
   * 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.catalina.connector.http;
  
  
  import java.io.IOException;
  import java.io.PrintWriter;
  import java.io.OutputStream;
  import javax.servlet.ServletOutputStream;
  import org.apache.catalina.connector.HttpResponseBase;
  
  
  /**
   * Implementation of <b>HttpResponse</b> specific to the HTTP connector.
   *
   * @author Craig R. McClanahan
   * @version $Revision: 1.1 $ $Date: 2000/08/11 22:40:52 $
   */
  
  final class HttpResponseImpl
      extends HttpResponseBase {
  
  
      // ----------------------------------------------------- Instance Variables
  
  
      /**
       * Descriptive information about this Response implementation.
       */
      protected static final String info =
  	"org.apache.catalina.connector.http.HttpResponseImpl/1.0";
  
  
      /**
       * True if chunking is allowed.
       */
      protected boolean allowChunking;
  
  
      // ------------------------------------------------------------- Properties
  
  
      /**
       * Return descriptive information about this Response implementation and
       * the corresponding version number, in the format
       * <code>&lt;description&gt;/&lt;version&gt;</code>.
       */
      public String getInfo() {
  
  	return (info);
  
      }
  
  
      /**
       * Set the chunking flag.
       */
      void setAllowChunking(boolean allowChunking) {
          this.allowChunking = allowChunking;
      }
  
  
      /**
       * True if chunking is allowed.
       */
      public boolean isChunkingAllowed() {
          return allowChunking;
      }
  
  
      // --------------------------------------------------------- Public Methods
  
  
      /**
       * Release all object references, and initialize instance variables, in
       * preparation for reuse of this object.
       */
      public void recycle() {
  
  	super.recycle();
          allowChunking = false;
  
      }
  
  
      /**
       * Return a PrintWriter that can be used to render error messages,
       * regardless of whether a stream or writer has already been acquired.
       */
      public PrintWriter getReporter() {
  
          try {
              return (new PrintWriter(getOutputStream()));
          } catch (IOException e) {
              return (new PrintWriter(output));
          }
  
      }
  
  
      /**
       * Send an error response with the specified status and message.
       *
       * @param status HTTP status code to send
       * @param message Corresponding message to send
       *
       * @exception IllegalStateException if this response has
       *  already been committed
       * @exception IOException if an input/output error occurs
       */
      public void sendError(int status, String message) throws IOException {
  
          setAllowChunking(false);
          addHeader("Connection", "close");
          super.sendError(status, message);
  
      }
  
  
      /**
       * Clear any content written to the buffer.  In addition, all cookies
       * and headers are cleared, and the status is reset.
       *
       * @exception IllegalStateException if this response has already
       *  been committed
       */
      public void reset() {
  
          // Saving important HTTP/1.1 specific headers
          String connectionValue = 
              (String) getHeader("Connection");
          String transferEncodingValue = 
              (String) getHeader("Transfer-Encoding");
  	super.reset();
          if (connectionValue != null)
              addHeader("Connection", connectionValue);
          if (transferEncodingValue != null)
              addHeader("Transfer-Encoding", transferEncodingValue);
  
      }
  
  
      /**
       * Create and return a ServletOutputStream to write the content
       * associated with this Response.
       *
       * @exception IOException if an input/output error occurs
       */
      public ServletOutputStream createOutputStream() throws IOException {
  
  	return (new HttpResponseStream(this));
  
      }
  
  
  }
  
  
  
  1.1                  jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http/HttpResponseStream.java
  
  Index: HttpResponseStream.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http/HttpResponseStream.java,v 1.1 2000/08/11 22:40:53 craigmcc Exp $
   * $Revision: 1.1 $
   * $Date: 2000/08/11 22:40:53 $
   *
   * ====================================================================
   * 
   * 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.catalina.connector.http;
  
  import java.io.IOException;
  import org.apache.catalina.Response;
  import org.apache.catalina.connector.ResponseStream;
  
  /**
   * Response stream for the HTTP/1.1 connector. This stream will automatically
   * chunk the answer if using HTTP/1.1 and no Content-Length has been properly
   * set.
   *
   * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
   */
  public final class HttpResponseStream extends ResponseStream {
  
  
      // ----------------------------------------------------------- Constructors
  
  
      private static final int MAX_CHUNK_SIZE = 4096;
  
  
      private static final String CRLF = "\r\n";
  
  
      // ----------------------------------------------------------- Constructors
  
  
      /**
       * Construct a servlet output stream associated with the specified Request.
       *
       * @param response The associated response
       */
      public HttpResponseStream(HttpResponseImpl response) {
  
  	super(response);
          this.useChunking = (response.isChunkingAllowed()
                              && response.getContentLength() == -1
                              && response.getStatus() != 206
                              && response.getStatus() != 304);
          if (this.useChunking)
              response.addHeader("Transfer-Encoding", "chunked");
  
      }
  
  
      // ----------------------------------------------------- Instance Variables
  
  
      /**
       * True if chunking is allowed.
       */
      private boolean useChunking;
  
  
      /**
       * True if printing a chunk.
       */
      private boolean writingChunk;
  
  
      // -------------------------------------------- ServletOutputStream Methods
  
  
      /**
       * Write the specified byte to our output stream.
       *
       * @param b The byte to be written
       *
       * @exception IOException if an input/output error occurs
       */
      public void write(int b)
          throws IOException {
          if (useChunking && !writingChunk) {
              writingChunk = true;
              print("1\r\n");
              super.write(b);
              println();
              writingChunk = false;
          } else {
              super.write(b);
          }
      }
  
  
      /**
       * Write the specified byte array.
       */
      public void write(byte[] b, int off, int len)
          throws IOException {
          if (useChunking && !writingChunk) {
              writingChunk = true;
              println(Integer.toHexString(len));
              super.write(b, off, len);
              println();
              writingChunk = false;
          } else {
              super.write(b, off, len);
          }
      }
  
  
      /**
       * Close this output stream, causing any buffered data to be flushed and
       * any further output data to throw an IOException.
       */
      public void close() throws IOException {
  
          if (useChunking) {
              // Write the final chunk.
              writingChunk = true;
              print("0\r\n\r\n");
              writingChunk = false;
          }
          super.close();
  
      }
  
  
  }
  
  
  
  1.1                  jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http/LocalStrings.properties
  
  Index: LocalStrings.properties
  ===================================================================
  httpConnector.alreadyStarted=HTTP connector has already been started
  httpConnector.allAddresses=Opening server socket on all host IP addresses
  httpConnector.failedSocketFactoryLoading=Failed to load socket factory
  httpConnector.failedToCreateSocket=Socket factory failed to create socket
  httpConnector.anAddress=Opening server socket on host IP address {0}
  httpConnector.noAddress=No host IP address matching {0}, opening on all addresses
  httpConnector.noProcessor=No processor available, rejecting this connection
  httpConnector.notStarted=HTTP connector has not yet been started
  httpConnector.starting=Starting background thread
  httpConnector.stopping=Stopping background thread
  httpProcessor.alreadyStarted=HTTP processor has already been started
  httpProcessor.notStarted=HTTP processor has not yet been started
  httpProcessor.parseHeaders.contentLength=Invalid 'Content-Length' header
  httpProcessor.parseHeaders.colon=Invalid HTTP header format
  httpProcessor.parseHeaders.portNumber=Invalid TCP/IP port number in 'Host' header
  httpProcessor.parseRequest.decode=Cannot decode HTTP request URI
  httpProcessor.parseRequest.method=Missing HTTP request method
  httpProcessor.parseRequest.read=Missing HTTP request line
  httpProcessor.parseRequest.uri=Missing HTTP request URI
  httpProcessor.parseHeaders.unknownExpectation=Unknown Expect header field
  httpProcessor.start=HTTP processor has already been started
  httpProcessor.starting=Starting background thread
  httpProcessor.stopping=Stopping background thread
  requestStream.close.closed=Request stream has already been closed
  requestStream.read.closed=Unable to read from a closed stream
  requestStream.read.error=Unexpected end of stream