tomcat-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From r...@apache.org
Subject cvs commit: jakarta-tomcat-connectors/jk/java/org/apache/coyote/ajp AjpAprProcessor.java Constants.java LocalStrings.properties AjpAprProtocol.java AjpMessage.java
Date Thu, 09 Jun 2005 16:14:51 GMT
remm        2005/06/09 09:14:51

  Added:       jk/java/org/apache/coyote/ajp AjpAprProcessor.java
                        Constants.java LocalStrings.properties
                        AjpAprProtocol.java AjpMessage.java
  Log:
  - Add my WIP AJP implementation using APR. Performance sucks right now,
    and I think it has lots of bugs ;)
  - It won't be compiled in or used for now.
  
  Revision  Changes    Path
  1.1                  jakarta-tomcat-connectors/jk/java/org/apache/coyote/ajp/AjpAprProcessor.java
  
  Index: AjpAprProcessor.java
  ===================================================================
  /*
   *  Copyright 1999-2004 The Apache Software Foundation
   *
   *  Licensed under the Apache License, Version 2.0 (the "License");
   *  you may not use this file except in compliance with the License.
   *  You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   *  Unless required by applicable law or agreed to in writing, software
   *  distributed under the License is distributed on an "AS IS" BASIS,
   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   *  See the License for the specific language governing permissions and
   *  limitations under the License.
   */
  
  package org.apache.coyote.ajp;
  
  import java.io.IOException;
  import java.io.InterruptedIOException;
  import java.net.InetAddress;
  
  import org.apache.coyote.ActionCode;
  import org.apache.coyote.ActionHook;
  import org.apache.coyote.Adapter;
  import org.apache.coyote.InputBuffer;
  import org.apache.coyote.OutputBuffer;
  import org.apache.coyote.Request;
  import org.apache.coyote.RequestInfo;
  import org.apache.coyote.Response;
  import org.apache.jk.common.AjpConstants;
  import org.apache.tomcat.jni.Address;
  import org.apache.tomcat.jni.Sockaddr;
  import org.apache.tomcat.jni.Socket;
  import org.apache.tomcat.jni.Status;
  import org.apache.tomcat.util.buf.ByteChunk;
  import org.apache.tomcat.util.buf.HexUtils;
  import org.apache.tomcat.util.buf.MessageBytes;
  import org.apache.tomcat.util.http.HttpMessages;
  import org.apache.tomcat.util.http.MimeHeaders;
  import org.apache.tomcat.util.net.AprEndpoint;
  import org.apache.tomcat.util.net.SSLSupport;
  import org.apache.tomcat.util.res.StringManager;
  import org.apache.tomcat.util.threads.ThreadWithAttributes;
  
  
  /**
   * Processes HTTP requests.
   *
   * @author Remy Maucherat
   */
  public class AjpAprProcessor implements ActionHook {
  
  
      /**
       * Logger.
       */
      protected static org.apache.commons.logging.Log log
          = org.apache.commons.logging.LogFactory.getLog(AjpAprProcessor.class);
  
  
      /**
       * The string manager for this package.
       */
      protected static StringManager sm =
          StringManager.getManager(Constants.Package);
  
  
      // ----------------------------------------------------------- Constructors
  
  
      public AjpAprProcessor(int headerBufferSize, AprEndpoint endpoint) {
  
          this.endpoint = endpoint;
          
          request = new Request();
          request.setInputBuffer(new SocketInputBuffer());
  
          response = new Response();
          response.setHook(this);
          response.setOutputBuffer(new SocketOutputBuffer());
          request.setResponse(response);
  
          readTimeout = endpoint.getFirstReadTimeout() * 1000;
  
          // Cause loading of HexUtils
          int foo = HexUtils.DEC[0];
  
          readBodyMessage.appendByte(Constants.JK_AJP13_GET_BODY_CHUNK);
          readBodyMessage.appendInt(Constants.MAX_READ_SIZE);
          
      }
  
  
      // ----------------------------------------------------- Instance Variables
  
  
      /**
       * Associated adapter.
       */
      protected Adapter adapter = null;
  
  
      /**
       * Request object.
       */
      protected Request request = null;
  
  
      /**
       * Response object.
       */
      protected Response response = null;
  
  
      /**
       * Header message. Note that this header is merely the one used during the
       * processing of the first message of a "request", so it might not be a request
       * header. It will stay unchanged during the processing of the whole request. 
       */
      protected AjpMessage headerMessage = new AjpMessage(); 
  
  
      /**
       * Message used for output. 
       */
      protected AjpMessage outputMessage = new AjpMessage(); 
  
  
      /**
       * Char version of the message header.
       */
      protected char[] headerChar = new char[8*1024]; // FIXME: Size should be configurable
  
  
      /**
       * Body message.
       */
      protected AjpMessage bodyMessage = new AjpMessage();
  
      
      /**
       * Body message.
       */
      protected MessageBytes bodyBytes = MessageBytes.newInstance();
  
      
      /**
       * All purpose response message.
       */
      protected AjpMessage responseMessage = new AjpMessage();
      
      
      /**
       * Read body message.
       */
      protected AjpMessage readBodyMessage = new AjpMessage();
  
      
      /**
       * State flag.
       */
      protected boolean started = false;
  
  
      /**
       * Error flag.
       */
      protected boolean error = false;
  
  
      /**
       * Is there an expectation ?
       */
      protected boolean expectation = false;
  
  
      /**
       * Use Tomcat authentication ?
       */
      protected boolean tomcatAuthentication = true;
  
  
      /**
       * Socket associated with the current connection.
       */
      protected long socket;
  
  
      /**
       * Remote Address associated with the current connection.
       */
      protected String remoteAddr = null;
  
  
      /**
       * Remote Host associated with the current connection.
       */
      protected String remoteHost = null;
  
  
      /**
       * Local Host associated with the current connection.
       */
      protected String localName = null;
  
  
      /**
       * Local port to which the socket is connected
       */
      protected int localPort = -1;
  
  
      /**
       * Remote port to which the socket is connected
       */
      protected int remotePort = -1;
  
  
      /**
       * The local Host address.
       */
      protected String localAddr = null;
  
  
      /**
       * Max post size.
       */
      protected int maxPostSize = 2 * 1024 * 1024;
  
  
      /**
       * Host name (used to avoid useless B2C conversion on the host name).
       */
      protected char[] hostNameC = new char[0];
  
  
      /**
       * Associated endpoint.
       */
      protected AprEndpoint endpoint;
  
  
      /**
       * Allow a customized the server header for the tin-foil hat folks.
       */
      protected String server = null;
  
  
      /**
       * The socket timeout used when reading the first block of the request
       * header.
       */
      protected long readTimeout;
      
      
      /**
       * Temp message bytes used for processing.
       */
      protected MessageBytes tmpMB = MessageBytes.newInstance();
      
      
      /**
       * Byte chunk for certs.
       */
      protected MessageBytes certificates = MessageBytes.newInstance();
      
      
      /**
       * End of stream flag.
       */
      protected boolean endOfStream = false;
      
      
      /**
       * Body empty flag.
       */
      protected boolean empty = true;
      
      
      /**
       * First read.
       */
      protected boolean first = true;
      
      
      // ------------------------------------------------------------- Properties
  
  
      // --------------------------------------------------------- Public Methods
  
  
      /**
       * Set the maximum size of a POST which will be buffered in SSL mode.
       */
      public void setMaxPostSize(int mps) {
          maxPostSize = mps;
      }
  
  
      /**
       * Return the maximum size of a POST which will be buffered in SSL mode.
       */
      public int getMaxPostSize() {
          return maxPostSize;
      }
  
  
      /**
       * Set the server header name.
       */
      public void setServer( String server ) {
          if (server==null || server.equals("")) {
              this.server = null;
          } else {
              this.server = server;
          }
      }
  
      
      /**
       * Get the server header name.
       */
      public String getServer() {
          return server;
      }
  
  
      /** Get the request associated with this processor.
       *
       * @return The request
       */
      public Request getRequest() {
          return request;
      }
  
      /**
       * Process pipelined HTTP requests using the specified input and output
       * streams.
       *
       * @throws IOException error during an I/O operation
       */
      public boolean process(long socket, long pool)
          throws IOException {
          ThreadWithAttributes thrA=
                  (ThreadWithAttributes)Thread.currentThread();
          RequestInfo rp = request.getRequestProcessor();
          thrA.setCurrentStage(endpoint, "parsing http request");
          rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
  
          // Set the remote address
          remoteAddr = null;
          remoteHost = null;
          localAddr = null;
          remotePort = -1;
          localPort = -1;
  
          // Setting up the socket
          this.socket = socket;
  
          // Error flag
          error = false;
  
          long soTimeout = endpoint.getSoTimeout();
  
          boolean openSocket = true;
  
          while (started && !error) {
  
              // Parsing the request header
              try {
                  // Get first message of the request
                  if (!readMessage(headerMessage, true)) {
                      // This means that no data is available right now
                      // (long keepalive), so that the processor should be recycled
                      // and the method should return true
                      rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
                      openSocket = true;
                      // Add the socket to the poller
                      endpoint.getPoller().add(socket, pool);
                      break;
                  }
                  // FIXME: Check message type, process right away and break if 
                  // not regular request processing
                  int type = headerMessage.getByte();
                  if (type == Constants.JK_AJP13_CPING_REQUEST) {
                      headerMessage.reset();
                      headerMessage.appendByte(Constants.JK_AJP13_CPONG_REPLY);
                      writeMessage(headerMessage);
                      continue;
                  }
  
                  request.setStartTime(System.currentTimeMillis());
              } catch (IOException e) {
                  error = true;
                  break;
              } catch (Throwable t) {
                  log.debug("Error parsing HTTP request", t);
                  // 400 - Bad Request
                  response.setStatus(400);
                  error = true;
              }
  
              // Setting up filters, and parse some request headers
              thrA.setCurrentStage(endpoint, "prepareRequest");
              rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
              try {
                  prepareRequest();
                  thrA.setParam(endpoint, request.requestURI());
              } catch (Throwable t) {
                  log.debug("Error preparing request", t);
                  // 400 - Internal Server Error
                  response.setStatus(400);
                  error = true;
              }
  
              // Process the request in the adapter
              if (!error) {
                  try {
                      thrA.setCurrentStage(endpoint, "service");
                      rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
                      adapter.service(request, response);
                  } catch (InterruptedIOException e) {
                      error = true;
                  } catch (Throwable t) {
                      t.printStackTrace();
                      log.error("Error processing request", t);
                      // 500 - Internal Server Error
                      response.setStatus(500);
                      error = true;
                  }
              }
  
              // Finish the handling of the request
              try {
                  thrA.setCurrentStage(endpoint, "endRequest");
                  rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);
                  endRequest();
                  // FXIME: Do one last flush
              } catch (IOException e) {
                  error = true;
              } catch (Throwable t) {
                  log.error("Error finishing request", t);
                  // 500 - Internal Server Error
                  response.setStatus(500);
                  error = true;
              }
  
              // If there was an error, make sure the request is counted as
              // and error, and update the statistics counter
              if (error) {
                  response.setStatus(500);
              }
              request.updateCounters();
  
              thrA.setCurrentStage(endpoint, "ended");
              rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
              recycle();
              
          }
  
          rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
          recycle();
          
          return openSocket;
          
      }
  
  
      // ----------------------------------------------------- ActionHook Methods
  
  
      /**
       * Send an action to the connector.
       *
       * @param actionCode Type of the action
       * @param param Action parameter
       */
      public void action(ActionCode actionCode, Object param) {
  
          if (actionCode == ActionCode.ACTION_COMMIT) {
  
              if (response.isCommitted())
                  return;
  
              // Validate and write response headers
              try {
                  prepareResponse();
              } catch (IOException e) {
                  // Set error flag
                  error = true;
              }
  
          } else if (actionCode == ActionCode.ACTION_ACK) {
  
              // Acknowlege request
  
              // Send a 100 status back if it makes sense (response not committed
              // yet, and client specified an expectation for 100-continue)
  
              if ((response.isCommitted()) || !expectation)
                  return;
  
              // FIXME: No way to reply to an expectation in AJP ?
  
          } else if (actionCode == ActionCode.ACTION_CLIENT_FLUSH) {
  
              if (response.isCommitted())
                  return;
  
              // Validate and write response headers
              try {
                  prepareResponse();
              } catch (IOException e) {
                  // Set error flag
                  error = true;
              }
  
          } else if (actionCode == ActionCode.ACTION_CLOSE) {
              // Close
  
              // End the processing of the current request, and stop any further
              // transactions with the client
  
              try {
                  endRequest();
              } catch (IOException e) {
                  // Set error flag
                  error = true;
              }
  
          } else if (actionCode == ActionCode.ACTION_RESET) {
  
              // Reset response
  
              // Note: This must be called before the response is committed
  
          } else if (actionCode == ActionCode.ACTION_CUSTOM) {
  
              // Do nothing
  
          } else if (actionCode == ActionCode.ACTION_START) {
  
              started = true;
  
          } else if (actionCode == ActionCode.ACTION_STOP) {
  
              started = false;
  
          } else if (actionCode == ActionCode.ACTION_REQ_SSL_ATTRIBUTE ) {
  
              // FIXME: SSL implementation
              
          } else if (actionCode == ActionCode.ACTION_REQ_HOST_ADDR_ATTRIBUTE) {
  
              // Get remote host address
              if (remoteAddr == null) {
                  try {
                      long sa = Address.get(Socket.APR_REMOTE, socket);
                      remoteAddr = Address.getip(sa);
                  } catch (Exception e) {
                      log.warn("Exception getting socket information " ,e);
                  }
              }
              request.remoteAddr().setString(remoteAddr);
  
          } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_NAME_ATTRIBUTE) {
  
              // Get local host name
              if (localName == null) {
                  try {
                      long sa = Address.get(Socket.APR_LOCAL, socket);
                      localName = Address.getnameinfo(sa, 0);
                  } catch (Exception e) {
                      log.warn("Exception getting socket information " ,e);
                  }
              }
              request.localName().setString(localName);
  
          } else if (actionCode == ActionCode.ACTION_REQ_HOST_ATTRIBUTE) {
  
              // Get remote host name
              if (remoteHost == null) {
                  try {
                      long sa = Address.get(Socket.APR_REMOTE, socket);
                      remoteHost = Address.getnameinfo(sa, 0);
                  } catch (Exception e) {
                      log.warn("Exception getting socket information " ,e);
                  }
              }
              request.remoteHost().setString(remoteHost);
  
          } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_ADDR_ATTRIBUTE) {
  
              // Get local host address
              if (localAddr == null) {
                  try {
                      long sa = Address.get(Socket.APR_LOCAL, socket);
                      Sockaddr addr = new Sockaddr();
                      if (Address.fill(addr, sa)) {
                          localAddr = addr.hostname;
                          localPort = addr.port;
                      }
                  } catch (Exception e) {
                      log.warn("Exception getting socket information " ,e);
                  }
              }
  
              request.localAddr().setString(localAddr);
  
          } else if (actionCode == ActionCode.ACTION_REQ_REMOTEPORT_ATTRIBUTE) {
  
              // Get remote port
              if (remotePort == -1) {
                  try {
                      long sa = Address.get(Socket.APR_REMOTE, socket);
                      Sockaddr addr = Address.getInfo(sa);
                      remotePort = addr.port;
                  } catch (Exception e) {
                      log.warn("Exception getting socket information " ,e);
                  }
              }
              request.setRemotePort(remotePort);
  
          } else if (actionCode == ActionCode.ACTION_REQ_LOCALPORT_ATTRIBUTE) {
  
              // Get local port
              if (localPort == -1) {
                  try {
                      long sa = Address.get(Socket.APR_LOCAL, socket);
                      Sockaddr addr = new Sockaddr();
                      if (Address.fill(addr, sa)) {
                          localAddr = addr.hostname;
                          localPort = addr.port;
                      }
                  } catch (Exception e) {
                      log.warn("Exception getting socket information " ,e);
                  }
              }
              request.setLocalPort(localPort);
  
          } else if (actionCode == ActionCode.ACTION_REQ_SSL_CERTIFICATE) {
              // FIXME: SSL implementation
          }
  
      }
  
  
      // ------------------------------------------------------ Connector Methods
  
  
      /**
       * Set the associated adapter.
       *
       * @param adapter the new adapter
       */
      public void setAdapter(Adapter adapter) {
          this.adapter = adapter;
      }
  
  
      /**
       * Get the associated adapter.
       *
       * @return the associated adapter
       */
      public Adapter getAdapter() {
          return adapter;
      }
  
  
      // ------------------------------------------------------ Protected Methods
  
      
      /**
       * After reading the request headers, we have to setup the request filters.
       */
      protected void prepareRequest() {
  
          // Translate the HTTP method code to a String.
          byte methodCode = headerMessage.getByte();
          if (methodCode != Constants.SC_M_JK_STORED) {
              String methodName = Constants.methodTransArray[(int)methodCode - 1];
              request.method().setString(methodName);
          }
  
          headerMessage.getBytes(request.protocol()); 
          headerMessage.getBytes(request.requestURI());
  
          headerMessage.getBytes(request.remoteAddr());
          headerMessage.getBytes(request.remoteHost());
          headerMessage.getBytes(request.localName());
          request.setLocalPort(headerMessage.getInt());
  
          boolean isSSL = headerMessage.getByte() != 0;
          if( isSSL ) {
              // XXX req.setSecure( true );
              request.scheme().setString("https");
          }
  
          // Decode headers
          MimeHeaders headers = request.getMimeHeaders();
  
          int hCount = headerMessage.getInt();
          for(int i = 0 ; i < hCount ; i++) {
              String hName = null;
              
              // Header names are encoded as either an integer code starting
              // with 0xA0, or as a normal string (in which case the first
              // two bytes are the length).
              int isc = headerMessage.peekInt();
              int hId = isc & 0xFF;
              
              MessageBytes vMB = null;
              isc &= 0xFF00;
              if(0xA000 == isc) {
                  headerMessage.getInt(); // To advance the read position
                  hName = Constants.headerTransArray[hId - 1];
                  vMB = headers.addValue( hName );
              } else {
                  // reset hId -- if the header currently being read
                  // happens to be 7 or 8 bytes long, the code below
                  // will think it's the content-type header or the
                  // content-length header - SC_REQ_CONTENT_TYPE=7,
                  // SC_REQ_CONTENT_LENGTH=8 - leading to unexpected
                  // behaviour.  see bug 5861 for more information.
                  hId = -1;
                  headerMessage.getBytes(tmpMB);
                  ByteChunk bc = tmpMB.getByteChunk();
                  vMB = headers.addValue(bc.getBuffer(),
                          bc.getStart(), bc.getLength());
              }
              
              headerMessage.getBytes(vMB);
              
              if (hId == Constants.SC_REQ_CONTENT_LENGTH ||
                      (hId == -1 && tmpMB.equalsIgnoreCase("Content-Length"))) {
                  // just read the content-length header, so set it
                  request.setContentLength( vMB.getInt() );
              } else if (hId == Constants.SC_REQ_CONTENT_TYPE ||
                      (hId == -1 && tmpMB.equalsIgnoreCase("Content-Type"))) {
                  // just read the content-type header, so set it
                  ByteChunk bchunk = vMB.getByteChunk();
                  request.contentType().setBytes(bchunk.getBytes(),
                          bchunk.getOffset(),
                          bchunk.getLength());
              }
          }
  
          // Decode extra attributes
          boolean moreAttr = true;
  
          while (moreAttr) {
              byte attributeCode = headerMessage.getByte();
              if (attributeCode == Constants.SC_A_ARE_DONE)
                  break;
  
              /* Special case ( XXX in future API make it separate type !)
               */
              if (attributeCode == Constants.SC_A_SSL_KEY_SIZE) {
                  // Bug 1326: it's an Integer.
                  request.setAttribute(SSLSupport.KEY_SIZE_KEY,
                                   new Integer(headerMessage.getInt()));
                 //Integer.toString(msg.getInt()));
              }
  
              if (attributeCode == Constants.SC_A_REQ_ATTRIBUTE ) {
                  // 2 strings ???...
                  headerMessage.getBytes(tmpMB);
                  String n = tmpMB.toString();
                  headerMessage.getBytes(tmpMB);
                  String v = tmpMB.toString();
                  request.setAttribute(n, v);
                  if (log.isTraceEnabled())
                      log.trace("jk Attribute set " + n + "=" + v);
              }
  
              // 1 string attributes
              switch (attributeCode) {
              case Constants.SC_A_CONTEXT :
                  headerMessage.getBytes(tmpMB);
                  // nothing
                  break;
                  
              case Constants.SC_A_SERVLET_PATH :
                  headerMessage.getBytes(tmpMB);
                  // nothing 
                  break;
                  
              case Constants.SC_A_REMOTE_USER :
                  if (tomcatAuthentication) {
                      // ignore server
                      headerMessage.getBytes(tmpMB);
                  } else {
                      headerMessage.getBytes(request.getRemoteUser());
                  }
                  break;
                  
              case Constants.SC_A_AUTH_TYPE :
                  if (tomcatAuthentication) {
                      // ignore server
                      headerMessage.getBytes(tmpMB);
                  } else {
                      headerMessage.getBytes(request.getAuthType());
                  }
                  break;
                  
              case Constants.SC_A_QUERY_STRING :
                  headerMessage.getBytes(request.queryString());
                  break;
                  
              case Constants.SC_A_JVM_ROUTE :
                  headerMessage.getBytes(request.instanceId());
                  break;
                  
              case Constants.SC_A_SSL_CERT :
                  request.scheme().setString("https");
                  // SSL certificate extraction is costy, moved to JkCoyoteHandler
                  headerMessage.getBytes(certificates);
                  break;
                  
              case Constants.SC_A_SSL_CIPHER   :
                  request.scheme().setString( "https" );
                  headerMessage.getBytes(tmpMB);
                  request.setAttribute(SSLSupport.CIPHER_SUITE_KEY,
                                   tmpMB.toString());
                  break;
                  
              case Constants.SC_A_SSL_SESSION  :
                  request.scheme().setString( "https" );
                  headerMessage.getBytes(tmpMB);
                  request.setAttribute(SSLSupport.SESSION_ID_KEY, 
                                    tmpMB.toString());
                  break;
                  
              case Constants.SC_A_SECRET  :
                  headerMessage.getBytes(tmpMB);
                  String secret = tmpMB.toString();
                  if(log.isInfoEnabled())
                      log.info("Secret: " + secret);
                  // FIXME: endpoint note - what's that ?
                  // endpoint.setNote(secretNote, secret);
                  break;
                  
              case Constants.SC_A_STORED_METHOD:
                  headerMessage.getBytes(request.method()); 
                  break;
                  
              default:
                  break; // ignore, we don't know about it - backward compat
              }
              
          }
  
          // Check for a full URI (including protocol://host:port/)
          ByteChunk uriBC = request.requestURI().getByteChunk();
          if (uriBC.startsWithIgnoreCase("http", 0)) {
  
              int pos = uriBC.indexOf("://", 0, 3, 4);
              int uriBCStart = uriBC.getStart();
              int slashPos = -1;
              if (pos != -1) {
                  byte[] uriB = uriBC.getBytes();
                  slashPos = uriBC.indexOf('/', pos + 3);
                  if (slashPos == -1) {
                      slashPos = uriBC.getLength();
                      // Set URI as "/"
                      request.requestURI().setBytes
                          (uriB, uriBCStart + pos + 1, 1);
                  } else {
                      request.requestURI().setBytes
                          (uriB, uriBCStart + slashPos,
                           uriBC.getLength() - slashPos);
                  }
                  MessageBytes hostMB = headers.setValue("host");
                  hostMB.setBytes(uriB, uriBCStart + pos + 3,
                                  slashPos - pos - 3);
              }
  
          }
  
          MessageBytes valueMB = request.getMimeHeaders().getValue("host");
          parseHost(valueMB);
  
      }
  
  
      /**
       * Parse host.
       */
      public void parseHost(MessageBytes valueMB) {
  
          if (valueMB == null || valueMB.isNull()) {
              // HTTP/1.0
              // Default is what the socket tells us. Overriden if a host is
              // found/parsed
              request.setServerPort(endpoint.getPort()/*socket.getLocalPort()*/);
              InetAddress localAddress = endpoint.getAddress()/*socket.getLocalAddress()*/;
              // Setting the socket-related fields. The adapter doesn't know
              // about socket.
              request.setLocalHost(localAddress.getHostName());
              request.serverName().setString(localAddress.getHostName());
              return;
          }
  
          ByteChunk valueBC = valueMB.getByteChunk();
          byte[] valueB = valueBC.getBytes();
          int valueL = valueBC.getLength();
          int valueS = valueBC.getStart();
          int colonPos = -1;
          if (hostNameC.length < valueL) {
              hostNameC = new char[valueL];
          }
  
          boolean ipv6 = (valueB[valueS] == '[');
          boolean bracketClosed = false;
          for (int i = 0; i < valueL; i++) {
              char b = (char) valueB[i + valueS];
              hostNameC[i] = b;
              if (b == ']') {
                  bracketClosed = true;
              } else if (b == ':') {
                  if (!ipv6 || bracketClosed) {
                      colonPos = i;
                      break;
                  }
              }
          }
  
          if (colonPos < 0) {
              if (request.scheme().equalsIgnoreCase("https")) {
                  // 443 - Default HTTPS port
                  request.setServerPort(443);
              } else {
                  // 80 - Default HTTTP port
                  request.setServerPort(80);
              }
              request.serverName().setChars(hostNameC, 0, valueL);
          } else {
  
              request.serverName().setChars(hostNameC, 0, colonPos);
  
              int port = 0;
              int mult = 1;
              for (int i = valueL - 1; i > colonPos; i--) {
                  int charValue = HexUtils.DEC[(int) valueB[i + valueS]];
                  if (charValue == -1) {
                      // Invalid character
                      error = true;
                      // 400 - Bad request
                      response.setStatus(400);
                      break;
                  }
                  port = port + (charValue * mult);
                  mult = 10 * mult;
              }
              request.setServerPort(port);
  
          }
  
      }
  
  
      /**
       * When committing the response, we have to validate the set of headers, as
       * well as setup the response filters.
       */
      protected void prepareResponse()
          throws IOException {
  
          response.setCommitted(true);
          
          outputMessage.reset();
          outputMessage.appendByte(Constants.JK_AJP13_SEND_HEADERS);
          outputMessage.appendInt(response.getStatus());
          
          String message = response.getMessage();
          if (message == null){
              message= HttpMessages.getMessage(response.getStatus());
          } else {
              message = message.replace('\n', ' ').replace('\r', ' ');
          }
          tmpMB.setString(message);
          outputMessage.appendBytes(tmpMB);
  
          // XXX add headers
          
          MimeHeaders headers = response.getMimeHeaders();
          String contentType = response.getContentType();
          if (contentType != null) {
              headers.setValue("Content-Type").setString(contentType);
          }
          String contentLanguage = response.getContentLanguage();
          if (contentLanguage != null) {
              headers.setValue("Content-Language").setString(contentLanguage);
          }
          int contentLength = response.getContentLength();
          if (contentLength >= 0) {
              headers.setValue("Content-Length").setInt(contentLength);
          }
          int numHeaders = headers.size();
          outputMessage.appendInt(numHeaders);
          for (int i = 0; i < numHeaders; i++) {
              MessageBytes hN = headers.getName(i);
              outputMessage.appendBytes(hN);
              MessageBytes hV=headers.getValue(i);
              outputMessage.appendBytes(hV);
          }
          writeMessage(outputMessage);
  
      }
  
      
      /**
       * Finish AJP response.
       */
      protected void endRequest()
          throws IOException {
  
          if (!response.isCommitted()) {
              // Validate and write response headers
              try {
                  prepareResponse();
              } catch (IOException e) {
                  // Set error flag
                  error = true;
              }
          }
  
          outputMessage.reset();
          outputMessage.appendByte(Constants.JK_AJP13_END_RESPONSE);
          outputMessage.appendByte(1);
          writeMessage(outputMessage);
      }
      
      
      /** Receive a chunk of data. Called to implement the
       *  'special' packet in ajp13 and to receive the data
       *  after we send a GET_BODY packet
       */
      public boolean receive() throws IOException {
          first = false;
          bodyMessage.reset();
          boolean err = readMessage(bodyMessage, false);
          if( log.isDebugEnabled() )
              log.info( "Receiving: getting request body chunk " + err + " " + bodyMessage.getLen() );
          
          if(err) {
              throw new IOException();
          }
  
          // No data received.
          if( bodyMessage.getLen() == 0 ) { // just the header
              // Don't mark 'end of stream' for the first chunk.
              // end_of_stream = true;
              return false;
          }
          int blen = bodyMessage.peekInt();
  
          if( blen == 0 ) {
              return false;
          }
  
          if( log.isTraceEnabled() ) {
              bodyMessage.dump("Body buffer");
          }
          
          bodyMessage.getBytes(bodyBytes);
          if( log.isTraceEnabled() )
              log.trace( "Data:\n" + bodyBytes);
          empty = false;
          return true;
      }
      
      /**
       * Get more request body data from the web server and store it in the 
       * internal buffer.
       *
       * @return true if there is more data, false if not.    
       */
      private boolean refillReadBuffer() throws IOException 
      {
          // If the server returns an empty packet, assume that that end of
          // the stream has been reached (yuck -- fix protocol??).
          // FIXME: FORM support here ?
          //if(replay) {
          //    endOfStream = true; // we've read everything there is
          //}
          if (endOfStream) {
              if( log.isDebugEnabled() ) 
                  log.debug("refillReadBuffer: end of stream " );
              return false;
          }
  
          // Why not use outBuf??
          writeMessage(readBodyMessage);
  
          // In JNI mode, response will be in bodyMsg. In TCP mode, response need to be
          // read
  
          boolean moreData=receive();
          if( !moreData ) {
              endOfStream = true;
          }
          return moreData;
      }
      
  
      /**
       * Read an AJP message.
       * 
       * @param first is true if the message is the first in the request, which
       *        will cause a short duration blocking read
       * @return true if the message has been read, false if the short read 
       *         didn't return anything
       * @throws IOException any other failure, including incomplete reads
       */
      protected boolean readMessage(AjpMessage message, boolean first)
          throws IOException {
          
          byte[] buf = message.getBuffer();
          int headerLength = message.getHeaderLength();
  
          // Read the message header
          // FIXME: do crazy tricks to avoid doing two reads !!!!
          if (first) {
              int nRead = Socket.recvt
                  (socket, buf, 0, headerLength, readTimeout);
              // Note: Unlike before, I assume it is not acceptable to do more than
              // one read for the first four bytes
              if (nRead == headerLength) {
                  message.processHeader();
              } else {
                  if (Status.APR_STATUS_IS_ETIMEDOUT(-nRead)) {
                      return false;
                  } else {
                      throw new IOException(sm.getString("iib.failedread"));
                  }
              }
          } else {
              int nRead = Socket.recv(socket, buf, 0, headerLength);
              if (nRead == headerLength) {
                  message.processHeader();
              } else {
                  throw new IOException(sm.getString("iib.failedread"));
              }
          }
          
          // Read the full message body; incomplete reads is a protocol error
          int messageLength = message.getLen();
          int pos = headerLength;
          while (pos < headerLength + messageLength) {
              int nRead = Socket.recv(socket, buf, pos, headerLength + messageLength - pos);
              if (nRead > 0) {
                  pos += nRead;
              } else {
                  throw new IOException(sm.getString("iib.failedread"));
              }
          }
          return true;
      }
      
      
      /**
       * Send the specified AJP message.
       *   
       * @param message to send
       * @throws IOException IO error when writing the message
       */
      protected void writeMessage(AjpMessage message) 
          throws IOException {
          // FIXME: Write to a temporary direct buffer
          message.end();
          if (Socket.send(socket, message.getBuffer(), 0, message.getLen()) < 0)
              throw new IOException(sm.getString("iib.failedwrite"));
      }
  
  
      /**
       * Recycle the processor.
       */
      public void recycle() {
  
          // Recycle Request object
          request.recycle();
          response.recycle();
          headerMessage.reset();
  
      }
  
  
      // ------------------------------------- InputStreamInputBuffer Inner Class
  
  
      /**
       * This class is an input buffer which will read its data from an input
       * stream.
       */
      protected class SocketInputBuffer 
          implements InputBuffer {
  
  
          /**
           * Read bytes into the specified chunk.
           */
          public int doRead(ByteChunk chunk, Request req ) 
              throws IOException {
  
              if (endOfStream) {
                  return -1;
              }
              if (first) {
                  // Handle special first-body-chunk
                  if (!receive()) {
                      return 0;
                  }
              } else if (empty) {
                  if (!refillReadBuffer()) {
                      return -1;
                  }
              }
              ByteChunk bc = bodyBytes.getByteChunk();
              chunk.setBytes( bc.getBuffer(), bc.getStart(), bc.getLength() );
              empty = true;
              return chunk.getLength();
  
          }
  
  
      }
  
  
      // ----------------------------------- OutputStreamOutputBuffer Inner Class
  
  
      /**
       * This class is an output buffer which will write data to an output
       * stream.
       */
      protected class SocketOutputBuffer 
          implements OutputBuffer {
  
  
          /**
           * Write chunk.
           */
          public int doWrite(ByteChunk chunk, Response res) 
              throws IOException {
  
              if (!response.isCommitted()) {
                  // Send the connector a request for commit. The connector should
                  // then validate the headers, send them (using sendHeader) and 
                  // set the filters accordingly.
                  response.action(ActionCode.ACTION_COMMIT, null);
              }
  
              int len = chunk.getLength();
              byte buf[] = bodyMessage.getBuffer();
              // 4 - hardcoded, byte[] marshalling overhead 
              int chunkSize=buf.length - bodyMessage.getHeaderLength() - 4;
              int off=0;
              while( len > 0 ) {
                  int thisTime=len;
                  if( thisTime > chunkSize ) {
                      thisTime=chunkSize;
                  }
                  len -= thisTime;
                  
                  // FIXME: Don't use a temp buffer
                  bodyMessage.reset();
                  bodyMessage.appendByte( AjpConstants.JK_AJP13_SEND_BODY_CHUNK);
                  if (log.isTraceEnabled()) 
                      log.trace("doWrite " + off + " " + thisTime + " " + len);
                  bodyMessage.appendBytes(chunk.getBytes(), chunk.getOffset() + off, thisTime);
                  off += thisTime;
                  writeMessage(bodyMessage);
              }
              
              return chunk.getLength();
  
          }
  
  
      }
  
  
  }
  
  
  
  1.1                  jakarta-tomcat-connectors/jk/java/org/apache/coyote/ajp/Constants.java
  
  Index: Constants.java
  ===================================================================
  /*
   *  Copyright 1999-2004 The Apache Software Foundation
   *
   *  Licensed under the Apache License, Version 2.0 (the "License");
   *  you may not use this file except in compliance with the License.
   *  You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   *  Unless required by applicable law or agreed to in writing, software
   *  distributed under the License is distributed on an "AS IS" BASIS,
   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   *  See the License for the specific language governing permissions and
   *  limitations under the License.
   */
  
  package org.apache.coyote.ajp;
  
  import org.apache.tomcat.util.buf.ByteChunk;
  
  
  /**
   * Constants.
   *
   * @author Remy Maucherat
   */
  public final class Constants {
  
  
      // -------------------------------------------------------------- Constants
  
  
      /**
       * Package name.
       */
      public static final String Package = "org.apache.coyote.ajp";
  
      public static final int DEFAULT_CONNECTION_LINGER = -1;
      public static final int DEFAULT_CONNECTION_TIMEOUT = 60000;
      public static final int DEFAULT_CONNECTION_UPLOAD_TIMEOUT = 300000;
      public static final int DEFAULT_SERVER_SOCKET_TIMEOUT = 0;
      public static final boolean DEFAULT_TCP_NO_DELAY = true;
      
      // Prefix codes for message types from server to container
      public static final byte JK_AJP13_FORWARD_REQUEST   = 2;
      public static final byte JK_AJP13_SHUTDOWN          = 7;
      public static final byte JK_AJP13_PING_REQUEST      = 8;
      public static final byte JK_AJP13_CPING_REQUEST     = 10;
      
      // Prefix codes for message types from container to server
      public static final byte JK_AJP13_SEND_BODY_CHUNK   = 3;
      public static final byte JK_AJP13_SEND_HEADERS      = 4;
      public static final byte JK_AJP13_END_RESPONSE      = 5;
      public static final byte JK_AJP13_GET_BODY_CHUNK    = 6;
      public static final byte JK_AJP13_CPONG_REPLY       = 9;
      
      // Integer codes for common response header strings
      public static final int SC_RESP_CONTENT_TYPE        = 0xA001;
      public static final int SC_RESP_CONTENT_LANGUAGE    = 0xA002;
      public static final int SC_RESP_CONTENT_LENGTH      = 0xA003;
      public static final int SC_RESP_DATE                = 0xA004;
      public static final int SC_RESP_LAST_MODIFIED       = 0xA005;
      public static final int SC_RESP_LOCATION            = 0xA006;
      public static final int SC_RESP_SET_COOKIE          = 0xA007;
      public static final int SC_RESP_SET_COOKIE2         = 0xA008;
      public static final int SC_RESP_SERVLET_ENGINE      = 0xA009;
      public static final int SC_RESP_STATUS              = 0xA00A;
      public static final int SC_RESP_WWW_AUTHENTICATE    = 0xA00B;
      
      // Integer codes for common (optional) request attribute names
      public static final byte SC_A_CONTEXT       = 1;  // XXX Unused
      public static final byte SC_A_SERVLET_PATH  = 2;  // XXX Unused
      public static final byte SC_A_REMOTE_USER   = 3;
      public static final byte SC_A_AUTH_TYPE     = 4;
      public static final byte SC_A_QUERY_STRING  = 5;
      public static final byte SC_A_JVM_ROUTE     = 6;
      public static final byte SC_A_SSL_CERT      = 7;
      public static final byte SC_A_SSL_CIPHER    = 8;
      public static final byte SC_A_SSL_SESSION   = 9;
      public static final byte SC_A_SSL_KEYSIZE   = 11;
      public static final byte SC_A_SECRET        = 12;
      public static final byte SC_A_STORED_METHOD = 13;
      
      // Used for attributes which are not in the list above
      public static final byte SC_A_REQ_ATTRIBUTE = 10; 
      
      // Terminates list of attributes
      public static final byte SC_A_ARE_DONE      = (byte)0xFF;
      
      // Ajp13 specific -  needs refactoring for the new model
      /**
       * Maximum Total byte size for a AJP packet
       */
      public static final int MAX_PACKET_SIZE = 8192;
      /**
       * Size of basic packet header
       */
      public static final int H_SIZE = 4;  
      /**
       * Maximum size of data that can be sent in one packet
       */
      public static final int  MAX_READ_SIZE = MAX_PACKET_SIZE - H_SIZE - 2;
  
      // Translates integer codes to names of HTTP methods
      public static final String []methodTransArray = {
              "OPTIONS",
              "GET",
              "HEAD",
              "POST",
              "PUT",
              "DELETE",
              "TRACE",
              "PROPFIND",
              "PROPPATCH",
              "MKCOL",
              "COPY",
              "MOVE",
              "LOCK",
              "UNLOCK",
              "ACL",
              "REPORT",
              "VERSION-CONTROL",
              "CHECKIN",
              "CHECKOUT",
              "UNCHECKOUT",
              "SEARCH",
              "MKWORKSPACE",
              "UPDATE",
              "LABEL",
              "MERGE",
              "BASELINE-CONTROL",
              "MKACTIVITY"
      };
      public static final int SC_M_JK_STORED = (byte) 0xFF;
      
      // id's for common request headers
      public static final int SC_REQ_ACCEPT          = 1;
      public static final int SC_REQ_ACCEPT_CHARSET  = 2;
      public static final int SC_REQ_ACCEPT_ENCODING = 3;
      public static final int SC_REQ_ACCEPT_LANGUAGE = 4;
      public static final int SC_REQ_AUTHORIZATION   = 5;
      public static final int SC_REQ_CONNECTION      = 6;
      public static final int SC_REQ_CONTENT_TYPE    = 7;
      public static final int SC_REQ_CONTENT_LENGTH  = 8;
      public static final int SC_REQ_COOKIE          = 9;
      public static final int SC_REQ_COOKIE2         = 10;
      public static final int SC_REQ_HOST            = 11;
      public static final int SC_REQ_PRAGMA          = 12;
      public static final int SC_REQ_REFERER         = 13;
      public static final int SC_REQ_USER_AGENT      = 14;
      // AJP14 new header
      public static final byte SC_A_SSL_KEY_SIZE  = 11; // XXX ??? 
      
      // Translates integer codes to request header names    
      public static final String []headerTransArray = {
              "accept",
              "accept-charset",
              "accept-encoding",
              "accept-language",
              "authorization",
              "connection",
              "content-type",
              "content-length",
              "cookie",
              "cookie2",
              "host",
              "pragma",
              "referer",
              "user-agent"
      };
      
      
      /**
       * CRLF.
       */
      public static final String CRLF = "\r\n";
  
      
      /**
       * Server string.
       */
      public static final byte[] SERVER_BYTES = 
          ByteChunk.convertToBytes("Server: Apache-Coyote/1.1" + CRLF);
  
      
      /**
       * CR.
       */
      public static final byte CR = (byte) '\r';
  
  
      /**
       * LF.
       */
      public static final byte LF = (byte) '\n';
  
  
      /**
       * SP.
       */
      public static final byte SP = (byte) ' ';
  
  
      /**
       * HT.
       */
      public static final byte HT = (byte) '\t';
  
  
      /**
       * COLON.
       */
      public static final byte COLON = (byte) ':';
  
  
      /**
       * 'A'.
       */
      public static final byte A = (byte) 'A';
  
  
      /**
       * 'a'.
       */
      public static final byte a = (byte) 'a';
  
  
      /**
       * 'Z'.
       */
      public static final byte Z = (byte) 'Z';
  
  
      /**
       * '?'.
       */
      public static final byte QUESTION = (byte) '?';
  
  
      /**
       * Lower case offset.
       */
      public static final byte LC_OFFSET = A - a;
  
  
      /**
       * Default HTTP header buffer size.
       */
      public static final int DEFAULT_HTTP_HEADER_BUFFER_SIZE = 48 * 1024;
  
  
      /* Various constant "strings" */
      public static final byte[] CRLF_BYTES = ByteChunk.convertToBytes(CRLF);
      public static final byte[] COLON_BYTES = ByteChunk.convertToBytes(": ");
      public static final String CONNECTION = "Connection";
      public static final String CLOSE = "close";
      public static final byte[] CLOSE_BYTES = 
          ByteChunk.convertToBytes(CLOSE);
      public static final String KEEPALIVE = "keep-alive";
      public static final byte[] KEEPALIVE_BYTES = 
          ByteChunk.convertToBytes(KEEPALIVE);
      public static final String CHUNKED = "chunked";
      public static final byte[] ACK_BYTES = 
          ByteChunk.convertToBytes("HTTP/1.1 100 Continue" + CRLF + CRLF);
      public static final String TRANSFERENCODING = "Transfer-Encoding";
      public static final byte[] _200_BYTES = 
          ByteChunk.convertToBytes("200");
      public static final byte[] _400_BYTES = 
          ByteChunk.convertToBytes("400");
      public static final byte[] _404_BYTES = 
          ByteChunk.convertToBytes("404");
      
  
      /**
       * Identity filters (input and output).
       */
      public static final int IDENTITY_FILTER = 0;
  
  
      /**
       * Chunked filters (input and output).
       */
      public static final int CHUNKED_FILTER = 1;
  
  
      /**
       * Void filters (input and output).
       */
      public static final int VOID_FILTER = 2;
  
  
      /**
       * GZIP filter (output).
       */
      public static final int GZIP_FILTER = 3;
  
  
      /**
       * Buffered filter (input)
       */
      public static final int BUFFERED_FILTER = 3;
  
  
      /**
       * HTTP/1.0.
       */
      public static final String HTTP_10 = "HTTP/1.0";
  
  
      /**
       * HTTP/1.1.
       */
      public static final String HTTP_11 = "HTTP/1.1";
      public static final byte[] HTTP_11_BYTES = 
          ByteChunk.convertToBytes(HTTP_11);
  
  
      /**
       * GET.
       */
      public static final String GET = "GET";
  
  
      /**
       * HEAD.
       */
      public static final String HEAD = "HEAD";
  
  
      /**
       * POST.
       */
      public static final String POST = "POST";
  
  
  }
  
  
  
  1.1                  jakarta-tomcat-connectors/jk/java/org/apache/coyote/ajp/LocalStrings.properties
  
  Index: LocalStrings.properties
  ===================================================================
  # $Id: LocalStrings.properties,v 1.1 2005/06/09 16:14:51 remm Exp $
  
  # language 
  
  # package org.apache.coyote.ajp
  
  #
  # AjpAprProtocol
  #
  
  ajpprotocol.endpoint.initerror=Error initializing endpoint
  ajpprotocol.endpoint.starterror=Error starting endpoint
  ajpprotocol.init=Initializing Coyote AJP/1.3 on {0}
  ajpprotocol.proto.error=Error reading request, ignored
  ajpprotocol.getattribute=Attribute {0}
  ajpprotocol.setattribute=Attribute {0}: {1}
  ajpprotocol.start=Starting Coyote AJP/1.3 on {0}
  ajpprotocol.stop=Stopping Coyote AJP/1.3 on {0}
  ajpprotocol.pause=Pausing Coyote AJP/1.3 on {0}
  ajpprotocol.endpoint.pauseerror=Error pausing endpoint
  ajpprotocol.resume=Resuming Coyote AJP/1.3 on {0}
  ajpprotocol.endpoint.resumeerror=Error resuming endpoint
  
  
  
  
  1.1                  jakarta-tomcat-connectors/jk/java/org/apache/coyote/ajp/AjpAprProtocol.java
  
  Index: AjpAprProtocol.java
  ===================================================================
  /*
   *  Copyright 1999-2004 The Apache Software Foundation
   *
   *  Licensed under the Apache License, Version 2.0 (the "License");
   *  you may not use this file except in compliance with the License.
   *  You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   *  Unless required by applicable law or agreed to in writing, software
   *  distributed under the License is distributed on an "AS IS" BASIS,
   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   *  See the License for the specific language governing permissions and
   *  limitations under the License.
   */
  
  package org.apache.coyote.ajp;
  
  import java.net.InetAddress;
  import java.net.URLEncoder;
  import java.util.Hashtable;
  import java.util.Iterator;
  
  import javax.management.MBeanRegistration;
  import javax.management.MBeanServer;
  import javax.management.ObjectName;
  
  import org.apache.commons.modeler.Registry;
  import org.apache.coyote.ActionCode;
  import org.apache.coyote.ActionHook;
  import org.apache.coyote.Adapter;
  import org.apache.coyote.ProtocolHandler;
  import org.apache.coyote.RequestGroupInfo;
  import org.apache.coyote.RequestInfo;
  import org.apache.tomcat.util.net.AprEndpoint;
  import org.apache.tomcat.util.net.ServerSocketFactory;
  import org.apache.tomcat.util.net.AprEndpoint.Handler;
  import org.apache.tomcat.util.res.StringManager;
  
  
  /**
   * Abstract the protocol implementation, including threading, etc.
   * Processor is single threaded and specific to stream-based protocols,
   * will not fit Jk protocols like JNI.
   *
   * @author Remy Maucherat
   * @author Costin Manolache
   */
  public class AjpAprProtocol implements ProtocolHandler, MBeanRegistration
  {
      public AjpAprProtocol() {
          cHandler = new AjpConnectionHandler( this );
          setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
          setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
          //setServerSoTimeout(Constants.DEFAULT_SERVER_SOCKET_TIMEOUT);
          setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
      }
  
      /**
       * The string manager for this package.
       */
      protected static StringManager sm =
          StringManager.getManager(Constants.Package);
  
      /** Pass config info
       */
      public void setAttribute( String name, Object value ) {
          if( log.isTraceEnabled())
              log.trace(sm.getString("ajpprotocol.setattribute", name, value));
  
          attributes.put(name, value);
      }
  
      public Object getAttribute( String key ) {
          if( log.isTraceEnabled())
              log.trace(sm.getString("ajpprotocol.getattribute", key));
          return attributes.get(key);
      }
  
      public Iterator getAttributeNames() {
          return attributes.keySet().iterator();
      }
  
      /**
       * Set a property.
       */
      public void setProperty(String name, String value) {
          setAttribute(name, value);
      }
  
      /**
       * Get a property
       */
      public String getProperty(String name) {
          return (String)getAttribute(name);
      }
  
      /** The adapter, used to call the connector
       */
      public void setAdapter(Adapter adapter) {
          this.adapter=adapter;
      }
  
      public Adapter getAdapter() {
          return adapter;
      }
  
  
      /** Start the protocol
       */
      public void init() throws Exception {
          ep.setName(getName());
          ep.setHandler(cHandler);
          ep.setUseSendfile(false);
  
          // XXX get domain from registration
          try {
              ep.init();
          } catch (Exception ex) {
              log.error(sm.getString("ajpprotocol.endpoint.initerror"), ex);
              throw ex;
          }
          if(log.isInfoEnabled())
              log.info(sm.getString("ajpprotocol.init", getName()));
  
      }
  
      ObjectName tpOname;
      ObjectName rgOname;
  
      public void start() throws Exception {
          if( this.domain != null ) {
              try {
                  // XXX We should be able to configure it separately
                  // XXX It should be possible to use a single TP
                  tpOname=new ObjectName
                      (domain + ":" + "type=ThreadPool,name=" + getName());
                  Registry.getRegistry(null, null)
                  .registerComponent(ep, tpOname, null );
              } catch (Exception e) {
                  log.error("Can't register threadpool" );
              }
              rgOname=new ObjectName
                  (domain + ":type=GlobalRequestProcessor,name=" + getName());
              Registry.getRegistry(null, null).registerComponent
                  ( cHandler.global, rgOname, null );
          }
  
          try {
              ep.start();
          } catch (Exception ex) {
              log.error(sm.getString("ajpprotocol.endpoint.starterror"), ex);
              throw ex;
          }
          if(log.isInfoEnabled())
              log.info(sm.getString("ajpprotocol.start", getName()));
      }
  
      public void pause() throws Exception {
          try {
              ep.pause();
          } catch (Exception ex) {
              log.error(sm.getString("ajpprotocol.endpoint.pauseerror"), ex);
              throw ex;
          }
          if(log.isInfoEnabled())
              log.info(sm.getString("ajpprotocol.pause", getName()));
      }
  
      public void resume() throws Exception {
          try {
              ep.resume();
          } catch (Exception ex) {
              log.error(sm.getString("ajpprotocol.endpoint.resumeerror"), ex);
              throw ex;
          }
          if(log.isInfoEnabled())
              log.info(sm.getString("ajpprotocol.resume", getName()));
      }
  
      public void destroy() throws Exception {
          if(log.isInfoEnabled())
              log.info(sm.getString("ajpprotocol.stop", getName()));
          ep.destroy();
          if( tpOname!=null )
              Registry.getRegistry(null, null).unregisterComponent(tpOname);
          if( rgOname != null )
              Registry.getRegistry(null, null).unregisterComponent(rgOname);
      }
  
      // -------------------- Properties--------------------
      protected AprEndpoint ep=new AprEndpoint();
      protected boolean secure;
  
      protected Hashtable attributes = new Hashtable();
  
      private int timeout = 300000;   // 5 minutes as in Apache HTTPD server
      private int maxPostSize = 2 * 1024 * 1024;
      private int maxHttpHeaderSize = 4 * 1024;
      private String reportedname;
      private int socketCloseDelay=-1;
      private boolean disableUploadTimeout = true;
      private int socketBuffer = 9000;
      private Adapter adapter;
      private AjpConnectionHandler cHandler;
  
      /**
       * Compression value.
       */
      private String compression = "off";
      private String noCompressionUserAgents = null;
      private String restrictedUserAgents = null;
      private String compressableMimeTypes = "text/html,text/xml,text/plain";
      private int compressionMinSize    = 2048;
  
      private String server;
  
      // -------------------- Pool setup --------------------
  
      public int getMaxThreads() {
          return ep.getMaxThreads();
      }
  
      public void setMaxThreads( int maxThreads ) {
          ep.setMaxThreads(maxThreads);
          setAttribute("maxThreads", "" + maxThreads);
      }
  
      public void setThreadPriority(int threadPriority) {
        ep.setThreadPriority(threadPriority);
        setAttribute("threadPriority", "" + threadPriority);
      }
  
      public int getThreadPriority() {
        return ep.getThreadPriority();
      }
  
      // -------------------- Tcp setup --------------------
  
      public int getBacklog() {
          return ep.getBacklog();
      }
  
      public void setBacklog( int i ) {
          ep.setBacklog(i);
          setAttribute("backlog", "" + i);
      }
  
      public int getPort() {
          return ep.getPort();
      }
  
      public void setPort( int port ) {
          ep.setPort(port);
          setAttribute("port", "" + port);
      }
  
      public boolean getUseSendfile() {
          return ep.getUseSendfile();
      }
  
      public void setUseSendfile(boolean useSendfile) {
          ep.setUseSendfile(useSendfile);
      }
  
      public InetAddress getAddress() {
          return ep.getAddress();
      }
  
      public void setAddress(InetAddress ia) {
          ep.setAddress( ia );
          setAttribute("address", "" + ia);
      }
  
      public String getName() {
          String encodedAddr = "";
          if (getAddress() != null) {
              encodedAddr = "" + getAddress();
              if (encodedAddr.startsWith("/"))
                  encodedAddr = encodedAddr.substring(1);
              encodedAddr = URLEncoder.encode(encodedAddr) + "-";
          }
          return ("ajp-" + encodedAddr + ep.getPort());
      }
  
      public boolean getTcpNoDelay() {
          return ep.getTcpNoDelay();
      }
  
      public void setTcpNoDelay( boolean b ) {
          ep.setTcpNoDelay( b );
          setAttribute("tcpNoDelay", "" + b);
      }
  
      public boolean getDisableUploadTimeout() {
          return disableUploadTimeout;
      }
  
      public void setDisableUploadTimeout(boolean isDisabled) {
          disableUploadTimeout = isDisabled;
      }
  
      public int getSocketBuffer() {
          return socketBuffer;
      }
  
      public void setSocketBuffer(int valueI) {
          socketBuffer = valueI;
      }
  
      public String getCompression() {
          return compression;
      }
  
      public void setCompression(String valueS) {
          compression = valueS;
          setAttribute("compression", valueS);
      }
  
      public int getMaxPostSize() {
          return maxPostSize;
      }
  
      public void setMaxPostSize(int valueI) {
          maxPostSize = valueI;
          setAttribute("maxPostSize", "" + valueI);
      }
  
      public int getMaxHttpHeaderSize() {
          return maxHttpHeaderSize;
      }
  
      public void setMaxHttpHeaderSize(int valueI) {
          maxHttpHeaderSize = valueI;
          setAttribute("maxHttpHeaderSize", "" + valueI);
      }
  
      public String getRestrictedUserAgents() {
          return restrictedUserAgents;
      }
  
      public void setRestrictedUserAgents(String valueS) {
          restrictedUserAgents = valueS;
          setAttribute("restrictedUserAgents", valueS);
      }
  
      public String getNoCompressionUserAgents() {
          return noCompressionUserAgents;
      }
  
      public void setNoCompressionUserAgents(String valueS) {
          noCompressionUserAgents = valueS;
          setAttribute("noCompressionUserAgents", valueS);
      }
  
      public String getCompressableMimeType() {
          return compressableMimeTypes;
      }
  
      public void setCompressableMimeType(String valueS) {
          compressableMimeTypes = valueS;
          setAttribute("compressableMimeTypes", valueS);
      }
  
      public int getCompressionMinSize() {
          return compressionMinSize;
      }
  
      public void setCompressionMinSize(int valueI) {
          compressionMinSize = valueI;
          setAttribute("compressionMinSize", "" + valueI);
      }
  
      public int getSoLinger() {
          return ep.getSoLinger();
      }
  
      public void setSoLinger( int i ) {
          ep.setSoLinger( i );
          setAttribute("soLinger", "" + i);
      }
  
      public int getSoTimeout() {
          return ep.getSoTimeout();
      }
  
      public void setSoTimeout( int i ) {
          ep.setSoTimeout(i);
          setAttribute("soTimeout", "" + i);
      }
  
      /*
      public int getServerSoTimeout() {
          return ep.getServerSoTimeout();
      }
  
      public void setServerSoTimeout( int i ) {
          ep.setServerSoTimeout(i);
          setAttribute("serverSoTimeout", "" + i);
      }
      */
  
      public String getKeystore() {
          return getProperty("keystore");
      }
  
      public void setKeystore( String k ) {
          setAttribute("keystore", k);
      }
  
      public String getKeypass() {
          return getProperty("keypass");
      }
  
      public void setKeypass( String k ) {
          attributes.put("keypass", k);
          //setAttribute("keypass", k);
      }
  
      public String getKeytype() {
          return getProperty("keystoreType");
      }
  
      public void setKeytype( String k ) {
          setAttribute("keystoreType", k);
      }
  
      public String getClientauth() {
          return getProperty("clientauth");
      }
  
      public void setClientauth( String k ) {
          setAttribute("clientauth", k);
      }
  
      public String getProtocol() {
          return getProperty("protocol");
      }
  
      public void setProtocol( String k ) {
          setSecure(true);
          setAttribute("protocol", k);
      }
  
      public String getProtocols() {
          return getProperty("protocols");
      }
  
      public void setProtocols(String k) {
          setAttribute("protocols", k);
      }
  
      public String getAlgorithm() {
          return getProperty("algorithm");
      }
  
      public void setAlgorithm( String k ) {
          setAttribute("algorithm", k);
      }
  
      public boolean getSecure() {
          return secure;
      }
  
      public void setSecure( boolean b ) {
          secure=b;
          setAttribute("secure", "" + b);
      }
  
      public String getCiphers() {
          return getProperty("ciphers");
      }
  
      public void setCiphers(String ciphers) {
          setAttribute("ciphers", ciphers);
      }
  
      public String getKeyAlias() {
          return getProperty("keyAlias");
      }
  
      public void setKeyAlias(String keyAlias) {
          setAttribute("keyAlias", keyAlias);
      }
  
      public int getSocketCloseDelay() {
          return socketCloseDelay;
      }
  
      public void setSocketCloseDelay( int d ) {
          socketCloseDelay=d;
          setAttribute("socketCloseDelay", "" + d);
      }
  
      public void setServer( String server ) {
          this.server = server;
      }
  
      public String getServer() {
          return server;
      }
  
  
      private static ServerSocketFactory string2SocketFactory( String val)
          throws ClassNotFoundException, IllegalAccessException,
                 InstantiationException {
          Class chC=Class.forName( val );
          return (ServerSocketFactory)chC.newInstance();
      }
  
      public int getTimeout() {
          return timeout;
      }
  
      public void setTimeout( int timeouts ) {
          timeout = timeouts;
          setAttribute("timeout", "" + timeouts);
      }
  
      public String getReportedname() {
          return reportedname;
      }
  
      public void setReportedname( String reportedName) {
          reportedname = reportedName;
      }
  
      // --------------------  Connection handler --------------------
      public static final int THREAD_DATA_PROCESSOR=1;
      public static final int THREAD_DATA_OBJECT_NAME=2;
  
  
      static class AjpConnectionHandler implements Handler {
          AjpAprProtocol proto;
          static int count=0;
          RequestGroupInfo global=new RequestGroupInfo();
          ThreadLocal localProcessor = new ThreadLocal();
  
          AjpConnectionHandler( AjpAprProtocol proto ) {
              this.proto=proto;
          }
  
          public Object[] init() {
              Object thData[]=new Object[3];
  
              AjpAprProcessor  processor =
                  new AjpAprProcessor(proto.maxHttpHeaderSize, proto.ep);
              processor.setAdapter( proto.adapter );
              processor.setMaxPostSize( proto.maxPostSize );
              processor.setServer( proto.server );
  
              thData[AjpAprProtocol.THREAD_DATA_PROCESSOR]=processor;
  
              if( proto.getDomain() != null ) {
                  try {
                      RequestInfo rp=processor.getRequest().getRequestProcessor();
                      rp.setGlobalProcessor(global);
                      ObjectName rpName=new ObjectName
                          (proto.getDomain() + ":type=RequestProcessor,worker="
                           + proto.getName() +",name=AjpRequest" + count++ );
                      Registry.getRegistry(null, null).registerComponent( rp, rpName, null);
                      thData[AjpAprProtocol.THREAD_DATA_OBJECT_NAME]=rpName;
                  } catch( Exception ex ) {
                      log.warn("Error registering request");
                  }
              }
  
              return  thData;
          }
  
          public boolean process(long socket, long pool) {
              AjpAprProcessor processor=null;
              try {
                  // FIXME: It is also possible to use the TWA data, so keep init() [] for
                  // now to test which is more efficient
                  processor = (AjpAprProcessor) localProcessor.get();
                  if (processor == null) {
                      processor = (AjpAprProcessor) (init()[AjpAprProtocol.THREAD_DATA_PROCESSOR]);
                      localProcessor.set(processor);
                  }
  
                  if (processor instanceof ActionHook) {
                      ((ActionHook) processor).action(ActionCode.ACTION_START, null);
                  }
                  //socket = connection.getSocket();
  
                  //InputStream in = socket.getInputStream();
                  //OutputStream out = socket.getOutputStream();
  
                  // FIXME: SSL implementation
                  /*
                  if( proto.secure ) {
                      SSLSupport sslSupport=null;
                      if(proto.sslImplementation != null)
                          sslSupport = proto.sslImplementation.getSSLSupport(socket);
                      processor.setSSLSupport(sslSupport);
                  } else {
                      processor.setSSLSupport( null );
                  }
                  processor.setSocket( socket );
                  */
  
                  return processor.process(socket, pool);
  
              } catch(java.net.SocketException e) {
                  // SocketExceptions are normal
                  AjpAprProtocol.log.debug
                      (sm.getString
                       ("ajpprotocol.proto.socketexception.debug"), e);
              } catch (java.io.IOException e) {
                  // IOExceptions are normal
                  AjpAprProtocol.log.debug
                      (sm.getString
                       ("ajpprotocol.proto.ioexception.debug"), e);
              }
              // Future developers: if you discover any other
              // rare-but-nonfatal exceptions, catch them here, and log as
              // above.
              catch (Throwable e) {
                  // any other exception or error is odd. Here we log it
                  // with "ERROR" level, so it will show up even on
                  // less-than-verbose logs.
                  AjpAprProtocol.log.error
                      (sm.getString("ajpprotocol.proto.error"), e);
              } finally {
                  //       if(proto.adapter != null) proto.adapter.recycle();
                  //                processor.recycle();
  
                  if (processor instanceof ActionHook) {
                      ((ActionHook) processor).action(ActionCode.ACTION_STOP, null);
                  }
              }
              return false;
          }
      }
  
      protected static org.apache.commons.logging.Log log
          = org.apache.commons.logging.LogFactory.getLog(AjpAprProtocol.class);
  
      // -------------------- Various implementation classes --------------------
  
      protected String domain;
      protected ObjectName oname;
      protected MBeanServer mserver;
  
      public ObjectName getObjectName() {
          return oname;
      }
  
      public String getDomain() {
          return domain;
      }
  
      public ObjectName preRegister(MBeanServer server,
                                    ObjectName name) throws Exception {
          oname=name;
          mserver=server;
          domain=name.getDomain();
          return name;
      }
  
      public void postRegister(Boolean registrationDone) {
      }
  
      public void preDeregister() throws Exception {
      }
  
      public void postDeregister() {
      }
  }
  
  
  
  1.1                  jakarta-tomcat-connectors/jk/java/org/apache/coyote/ajp/AjpMessage.java
  
  Index: AjpMessage.java
  ===================================================================
  /*
   *  Copyright 1999-2004 The Apache Software Foundation
   *
   *  Licensed under the Apache License, Version 2.0 (the "License");
   *  you may not use this file except in compliance with the License.
   *  You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   *  Unless required by applicable law or agreed to in writing, software
   *  distributed under the License is distributed on an "AS IS" BASIS,
   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   *  See the License for the specific language governing permissions and
   *  limitations under the License.
   */
  
  package org.apache.coyote.ajp;
  
  import java.io.IOException;
  
  import org.apache.tomcat.util.buf.ByteChunk;
  import org.apache.tomcat.util.buf.CharChunk;
  import org.apache.tomcat.util.buf.MessageBytes;
  
  /**
   * A single packet for communication between the web server and the
   * container.  Designed to be reused many times with no creation of
   * garbage.  Understands the format of data types for these packets.
   * Can be used (somewhat confusingly) for both incoming and outgoing
   * packets.  
   *
   * See Ajp14/Ajp13Packet.java.
   *
   * @author Henri Gomez [hgomez@apache.org]
   * @author Dan Milstein [danmil@shore.net]
   * @author Keith Wannamaker [Keith@Wannamaker.org]
   * @author Kevin Seguin
   * @author Costin Manolache
   */
  public class AjpMessage {
      private static org.apache.commons.logging.Log log=
          org.apache.commons.logging.LogFactory.getLog( AjpMessage.class );
  
      // that's the original buffer size in ajp13 - otherwise we'll get interoperability problems.
      private byte buf[]=new byte[8*1024]; 
      // The current read or write position in the buffer
      private int pos;
      /**
       * This actually means different things depending on whether the
       * packet is read or write.  For read, it's the length of the
       * payload (excluding the header).  For write, it's the length of
       * the packet as a whole (counting the header).  Oh, well.
       */
      private int len; 
  
  
      
      
      /**
       * Prepare this packet for accumulating a message from the container to
       * the web server.  Set the write position to just after the header
       * (but leave the length unwritten, because it is as yet unknown).
       */
      public void reset() {
          len = 4;
          pos = 4;
      }
  	
      /**
       * For a packet to be sent to the web server, finish the process of
       * accumulating data and write the length of the data payload into
       * the header.  
       */
      public void end() {
          len=pos;
          int dLen=len-4;
  
          buf[0] = (byte)0x41;
          buf[1] = (byte)0x42;
          buf[2]=  (byte)((dLen>>>8 ) & 0xFF );
          buf[3] = (byte)(dLen & 0xFF);
      }
  
      public byte[] getBuffer() {
          return buf;
      }
  
      public int getLen() {
          return len;
      }
      
      // ============ Data Writing Methods ===================
  
      /**
       * Add an int.
       *
       * @param val The integer to write.
       */
      public void appendInt( int val ) {
          buf[pos++]   = (byte) ((val >>>  8) & 0xFF);
          buf[pos++] = (byte) (val & 0xFF);
      }
  
      public void appendByte( int val ) {
          buf[pos++] = (byte)val;
      }
  	
      public void appendLongInt( int val ) {
          buf[pos++]   = (byte) ((val >>>  24) & 0xFF);
          buf[pos++] = (byte) ((val >>>  16) & 0xFF);
          buf[pos++] = (byte) ((val >>>   8) & 0xFF);
          buf[pos++] = (byte) (val & 0xFF);
      }
  
      /**
       * Write a String out at the current write position.  Strings are
       * encoded with the length in two bytes first, then the string, and
       * then a terminating \0 (which is <B>not</B> included in the
       * encoded length).  The terminator is for the convenience of the C
       * code, where it saves a round of copying.  A null string is
       * encoded as a string with length 0.  
       */
      public void appendBytes(MessageBytes mb) throws IOException {
          if(mb==null || mb.isNull() ) {
              appendInt( 0);
              appendByte(0);
              return;
          }
  
          if (mb.getType() == MessageBytes.T_BYTES) {
              ByteChunk bc = mb.getByteChunk();
              appendByteChunk(bc);
          } else if (mb.getType() == MessageBytes.T_CHARS) {
              CharChunk cc = mb.getCharChunk();
              appendCharChunk(cc);
          } else {
              appendString(mb.toString());
          }
      }
  
      public void appendByteChunk(ByteChunk bc) throws IOException {
          if(bc==null) {
              log.error("appendByteChunk() null");
              appendInt( 0);
              appendByte(0);
              return;
          }
  
          byte[] bytes = bc.getBytes();
          int start=bc.getStart();
          appendInt( bc.getLength() );
          cpBytes(bytes, start, bc.getLength());
          appendByte(0);
      }
  
      public void appendCharChunk(CharChunk cc) throws IOException {
          if(cc==null) {
              log.error("appendByteChunk() null");
              appendInt( 0);
              appendByte(0);
              return;
          }
  
          int start = cc.getStart();
          int end = cc.getEnd();
          appendInt( cc.getLength() );
          char[] cbuf = cc.getBuffer();
          for (int i = start; i < end; i++) {
              char c = cbuf[i];
              // Note:  This is clearly incorrect for many strings,
              // but is the only consistent approach within the current
              // servlet framework.  It must suffice until servlet output
              // streams properly encode their output.
              if ((c <= 31) && (c != 9)) {
                  c = ' ';
              } else if (c == 127) {
                  c = ' ';
              }
              appendByte(c);
          }
          appendByte(0);
      }
  
      public void appendString(String str) throws IOException {
          if(str==null) {
              log.error("appendByteChunk() null");
              appendInt( 0);
              appendByte(0);
              return;
          }
  
          // From the Tomcat 3.3 HTTP/1.0 connector
          int len = str.length();
          appendInt(len);
          for (int i = 0; i < len; i++) {
              char c = str.charAt (i);
              // Note:  This is clearly incorrect for many strings,
              // but is the only consistent approach within the current
              // servlet framework.  It must suffice until servlet output
              // streams properly encode their output.
              if ((c <= 31) && (c != 9)) {
                  c = ' ';
              } else if (c == 127) {
                  c = ' ';
              }
              appendByte(c);
          }
          appendByte(0);
      }
  
      /** 
       * Copy a chunk of bytes into the packet, starting at the current
       * write position.  The chunk of bytes is encoded with the length
       * in two bytes first, then the data itself, and finally a
       * terminating \0 (which is <B>not</B> included in the encoded
       * length).
       *
       * @param b The array from which to copy bytes.
       * @param off The offset into the array at which to start copying
       * @param numBytes The number of bytes to copy.  
       */
      public void appendBytes( byte b[], int off, int numBytes ) {
          appendInt( numBytes );
          cpBytes( b, off, numBytes );
          appendByte(0);
      }
      
      private void cpBytes( byte b[], int off, int numBytes ) {
          if( pos + numBytes >= buf.length ) {
              log.error("Buffer overflow: buffer.len=" + buf.length + " pos=" +
                        pos + " data=" + numBytes );
              dump("Overflow/coBytes");
              log.error( "Overflow ", new Throwable());
              return;
          }
          System.arraycopy( b, off, buf, pos, numBytes);
          pos += numBytes;
          // buf[pos + numBytes] = 0; // Terminating \0
      }
      
  
      
      // ============ Data Reading Methods ===================
  
      /**
       * Read an integer from packet, and advance the read position past
       * it.  Integers are encoded as two unsigned bytes with the
       * high-order byte first, and, as far as I can tell, in
       * little-endian order within each byte.  
       */
      public int getInt() {
          int b1 = buf[pos++] & 0xFF;  // No swap, Java order
          int b2 = buf[pos++] & 0xFF;
  
          return  (b1<<8) + b2;
      }
  
      public int peekInt() {
          int b1 = buf[pos] & 0xFF;  // No swap, Java order
          int b2 = buf[pos+1] & 0xFF;
  
          return  (b1<<8) + b2;
      }
  
      public byte getByte() {
          byte res = buf[pos++];
          return res;
      }
  
      public byte peekByte() {
          byte res = buf[pos];
          return res;
      }
  
      public void getBytes(MessageBytes mb) {
          int length = getInt();
          if( (length == 0xFFFF) || (length == -1) ) {
              mb.recycle();
              return;
          }
          mb.setBytes( buf, pos, length );
          pos += length;
          pos++; // Skip the terminating \0
      }
      
      /**
       * Copy a chunk of bytes from the packet into an array and advance
       * the read position past the chunk.  See appendBytes() for details
       * on the encoding.
       *
       * @return The number of bytes copied.
       */
      public int getBytes(byte dest[]) {
          int length = getInt();
          if( length > buf.length ) {
              // XXX Should be if(pos + length > buff.legth)?
              log.error("getBytes() buffer overflow " + length + " " + buf.length );
          }
  	
          if( (length == 0xFFFF) || (length == -1) ) {
              log.info("Null string " + length);
              return 0;
          }
  
          System.arraycopy( buf, pos,  dest, 0, length );
          pos += length;
          pos++; // Skip terminating \0  XXX I believe this is wrong but harmless
          return length;
      }
  
      /**
       * Read a 32 bits integer from packet, and advance the read position past
       * it.  Integers are encoded as four unsigned bytes with the
       * high-order byte first, and, as far as I can tell, in
       * little-endian order within each byte.
       */
      public int getLongInt() {
          int b1 = buf[pos++] & 0xFF;  // No swap, Java order
          b1 <<= 8;
          b1 |= (buf[pos++] & 0xFF);
          b1 <<= 8;
          b1 |= (buf[pos++] & 0xFF);
          b1 <<=8;
          b1 |= (buf[pos++] & 0xFF);
          return  b1;
      }
  
      public int getHeaderLength() {
          return 4;
      }
  
      public int processHeader() {
          pos = 0;
          int mark = getInt();
          len      = getInt();
  	    
          if( mark != 0x1234 && mark != 0x4142 ) {
              // XXX Logging
              log.error("BAD packet signature " + mark);
              dump( "In: " );
              return -1;
          }
  
          if( log.isDebugEnabled() ) 
              log.debug( "Received " + len + " " + buf[0] );
          return len;
      }
      
      public void dump(String msg) {
          if( log.isDebugEnabled() ) 
              log.debug( msg + ": " + buf + " " + pos +"/" + (len + 4));
          int max=pos;
          if( len + 4 > pos )
              max=len+4;
          if( max >1000 ) max=1000;
          if( log.isDebugEnabled() ) 
              for( int j=0; j < max; j+=16 ) 
                  log.debug( hexLine( buf, j, len ));
  	
      }
  
      /* -------------------- Utilities -------------------- */
      // XXX Move to util package
  
      public static String hexLine( byte buf[], int start, int len ) {
          StringBuffer sb=new StringBuffer();
          for( int i=start; i< start+16 ; i++ ) {
              if( i < len + 4)
                  sb.append( hex( buf[i] ) + " ");
              else 
                  sb.append( "   " );
          }
          sb.append(" | ");
          for( int i=start; i < start+16 && i < len + 4; i++ ) {
              if( ! Character.isISOControl( (char)buf[i] ))
                  sb.append( new Character((char)buf[i]) );
              else
                  sb.append( "." );
          }
          return sb.toString();
      }
  
      private  static String hex( int x ) {
          //	    if( x < 0) x=256 + x;
          String h=Integer.toHexString( x );
          if( h.length() == 1 ) h = "0" + h;
          return h.substring( h.length() - 2 );
      }
  
  }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org


Mime
View raw message