Author: costin
Date: Tue Mar 25 08:17:03 2008
New Revision: 640852

URL: http://svn.apache.org/viewvc?rev=640852&view=rev
Log:
A bunch of files to make it easier to load-test, profile, debug coyote.
It can also be used to run simple servers with the maximum speed available - no
tomcat or servlet engine overhead. 

To be useful, a couple of extensions to MessageBytes are needed - to make it 
easier to 
use.

Some of this is from an old experiment, removed than added back.


Added:
    
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/ClientAbortException.java
    
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/CoyoteServer.java
    
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/EchoAdapter.java
    
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/MapperAdapter.java
    
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/MessageReader.java
    
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/MessageWriter.java
    
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/SimpleFileAdapter.java
    
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/StaticAdapter.java
    
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/StaticMain.java

Added: 
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/ClientAbortException.java
URL: 
http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/ClientAbortException.java?rev=640852&view=auto
==============================================================================
--- 
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/ClientAbortException.java
 (added)
+++ 
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/ClientAbortException.java
 Tue Mar 25 08:17:03 2008
@@ -0,0 +1,144 @@
+/*
+ * 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.adapters;
+
+import java.io.IOException;
+
+/**
+ * Wrap an IOException identifying it as being caused by an abort
+ * of a request by a remote client.
+ *
+ * @author Glenn L. Nielsen
+ * @version $Revision: 304063 $ $Date: 2005-08-18 06:25:18 -0700 (Thu, 18 Aug 
2005) $
+ */
+
+public final class ClientAbortException extends IOException {
+
+
+    //------------------------------------------------------------ Constructors
+
+
+    /**
+     * Construct a new ClientAbortException with no other information.
+     */
+    public ClientAbortException() {
+
+        this(null, null);
+
+    }
+
+
+    /**
+     * Construct a new ClientAbortException for the specified message.
+     *
+     * @param message Message describing this exception
+     */
+    public ClientAbortException(String message) {
+
+        this(message, null);
+
+    }
+
+
+    /**
+     * Construct a new ClientAbortException for the specified throwable.
+     *
+     * @param throwable Throwable that caused this exception
+     */
+    public ClientAbortException(Throwable throwable) {
+
+        this(null, throwable);
+
+    }
+
+
+    /**
+     * Construct a new ClientAbortException for the specified message
+     * and throwable.
+     *
+     * @param message Message describing this exception
+     * @param throwable Throwable that caused this exception
+     */
+    public ClientAbortException(String message, Throwable throwable) {
+
+        super();
+        this.message = message;
+        this.throwable = throwable;
+
+    }
+
+
+    //------------------------------------------------------ Instance Variables
+
+
+    /**
+     * The error message passed to our constructor (if any)
+     */
+    protected String message = null;
+
+
+    /**
+     * The underlying exception or error passed to our constructor (if any)
+     */
+    protected Throwable throwable = null;
+
+
+    //---------------------------------------------------------- Public Methods
+
+
+    /**
+     * Returns the message associated with this exception, if any.
+     */
+    public String getMessage() {
+
+        return (message);
+
+    }
+
+
+    /**
+     * Returns the cause that caused this exception, if any.
+     */
+    public Throwable getCause() {
+        
+        return (throwable);
+        
+    }
+
+    
+    /**
+     * Return a formatted string that describes this exception.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("ClientAbortException:  ");
+        if (message != null) {
+            sb.append(message);
+            if (throwable != null) {
+                sb.append(":  ");
+            }
+        }
+        if (throwable != null) {
+            sb.append(throwable.toString());
+        }
+        return (sb.toString());
+
+    }
+
+
+}

Added: 
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/CoyoteServer.java
URL: 
http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/CoyoteServer.java?rev=640852&view=auto
==============================================================================
--- 
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/CoyoteServer.java
 (added)
+++ 
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/CoyoteServer.java
 Tue Mar 25 08:17:03 2008
@@ -0,0 +1,136 @@
+package org.apache.coyote.adapters;
+
+import java.lang.management.ManagementFactory;
+
+import org.apache.coyote.Adapter;
+import org.apache.coyote.ProtocolHandler;
+import org.apache.coyote.http11.Http11NioProtocol;
+import org.apache.juli.JdkLoggerConfig;
+import org.apache.tomcat.util.modeler.Registry;
+
+
+/** 
+ * Simple example of embeding coyote servlet.
+ * 
+ */
+public class CoyoteServer  {
+  int port = 8800;
+  String args[];
+
+  protected ProtocolHandler proto;
+
+  Registry registry;
+  
+  protected Adapter adapter;
+  int maxThreads = 20;
+  
+  public CoyoteServer() {        
+  }
+  
+  public CoyoteServer(int i) {
+    port = i;
+  }
+
+  public CoyoteServer(int i, Adapter adapter) {
+    port = i;
+    addAdapter("/", adapter);
+  }
+
+  public void setArgs(String[] args) {
+    this.args = args;
+  }
+  
+  /** 
+   * Add an adapter. If more than the 'default' adapter is
+   * added, a MapperAdapter will be inserted.
+   * 
+   * @param path Use "/" for the default.
+   * @param adapter
+   */
+  public void addAdapter(String path, Adapter added) {
+    if (adapter == null && "/".equals(path)) {
+      adapter = added;
+    } else {
+      if (!(adapter instanceof MapperAdapter)) {
+        Adapter oldDefault = adapter;
+        adapter = new MapperAdapter();
+        ((MapperAdapter) adapter).setDefaultAdapter(oldDefault);
+      }
+      ((MapperAdapter) adapter).getMapper().addWrapper(path, added);
+    }
+  }
+  
+  /**
+   */
+  public void run() {
+    init();
+    start();
+  }
+
+  public void init() {
+    new JdkLoggerConfig();
+    initJMX();
+  }
+
+  protected void initAdapters() {
+    // adapter = ...
+    // Adapter secondaryadapter = ...
+    //registry.registerComponent(secondaryadapter, ":name=adapter", null);
+  }
+
+  public void stop() throws Exception {
+    proto.destroy();
+  }
+  
+  /**
+   *  Simple CLI support - arg is a path:className pair.
+   */
+  public void setAdapter(String arg)  {
+    String[] pathClass = arg.split(":", 2);
+    try {
+      Class c = Class.forName(pathClass[1]);
+      Adapter a = (Adapter) c.newInstance();
+      addAdapter(pathClass[0],a);
+    } catch (Throwable e) {
+      e.printStackTrace();
+    }
+  }
+  
+  public void setPort() {
+    this.port = port;
+  }
+
+  /** 
+   */
+  public static ProtocolHandler getDefaultConnector(int port) {
+    Http11NioProtocol proto = new Http11NioProtocol();
+    proto.setCompression("on");
+    proto.setCompressionMinSize(32);
+    proto.setPort(port);
+    proto.getEndpoint().setDaemon(false);
+    return proto;
+  }
+  
+  public void start() {
+    try {
+      proto = getDefaultConnector(port);
+      initAdapters();
+      registry.registerComponent(adapter, ":name=adapter" + (port), null);
+      
+      proto.setAdapter(adapter);
+      
+      registry.registerComponent(proto, ":name=ep-" + port, null);
+      proto.start();      
+      proto.init();
+    } catch (Throwable e) {
+      e.printStackTrace();
+    }
+  }
+  
+  public void initJMX() {
+    ManagementFactory.getPlatformMBeanServer();
+    registry = Registry.getRegistry(null, null);
+    
+  } 
+ 
+}
\ No newline at end of file

Added: 
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/EchoAdapter.java
URL: 
http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/EchoAdapter.java?rev=640852&view=auto
==============================================================================
--- 
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/EchoAdapter.java
 (added)
+++ 
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/EchoAdapter.java
 Tue Mar 25 08:17:03 2008
@@ -0,0 +1,42 @@
+package org.apache.coyote.adapters;
+
+import java.util.logging.Logger;
+
+import org.apache.coyote.Adapter;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.http.HttpProcessor;
+import org.apache.tomcat.util.net.SocketStatus;
+
+/**
+ * Response is plain/text, copy of the received request
+ */
+public class EchoAdapter implements Adapter {
+    Logger log = Logger.getLogger("coyote.static");
+    
+    String contentType = "text/plain";
+
+
+    public EchoAdapter() {
+    }
+
+    public void service(Request req, final Response res) throws Exception {
+      ByteChunk reqBuf = new ByteChunk(1024);
+      HttpProcessor.serializeRequest(req, reqBuf);
+      
+      res.setStatus(200);
+      res.setContentLength(reqBuf.getLength());
+      res.setContentType(contentType);
+      
+      res.sendHeaders();
+      
+      res.doWrite(reqBuf);
+    }
+
+    public boolean event(Request req, Response res, SocketStatus status)
+        throws Exception {
+      return false;
+    }
+    
+}
\ No newline at end of file

Added: 
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/MapperAdapter.java
URL: 
http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/MapperAdapter.java?rev=640852&view=auto
==============================================================================
--- 
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/MapperAdapter.java
 (added)
+++ 
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/MapperAdapter.java
 Tue Mar 25 08:17:03 2008
@@ -0,0 +1,214 @@
+package org.apache.coyote.adapters;
+
+import java.io.IOException;
+
+import org.apache.coyote.Adapter;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.mapper.Mapper;
+import org.apache.tomcat.util.http.mapper.MappingData;
+import org.apache.tomcat.util.net.SocketStatus;
+
+/**
+ * 
+ */
+public class MapperAdapter implements Adapter {
+
+    private Mapper mapper=new Mapper();
+    
+    public MapperAdapter() {
+        mapper = new Mapper();
+        mapper.setDefaultHostName("localhost");
+        mapper.setContext("", new String[] {"index.html"},
+            null);
+    }
+
+    public MapperAdapter(Mapper mapper2) {
+        mapper = mapper2;
+    }
+    
+    public static void decodeRequest(Request reqB) throws IOException {
+      MessageBytes decodedURI = reqB.decodedURI();
+      decodedURI.duplicate(reqB.requestURI());
+
+      if (decodedURI.getType() == MessageBytes.T_BYTES) {
+          // %xx decoding of the URL
+          reqB.getURLDecoder().convert(decodedURI, false);
+          // Normalization
+          if (!normalize(decodedURI)) {
+              throw new IOException("Error normalizing");
+          }
+          // Character decoding
+          //convertURI(decodedURI, request);
+      } else {
+          // The URL is chars or String, and has been sent using an in-memory
+          // protocol handler, we have to assume the URL has been properly
+          // decoded already
+          decodedURI.toChars();
+      }
+    }
+
+    /**
+     * Normalize URI.
+     * <p>
+     * This method normalizes "\", "//", "/./" and "/../". This method will
+     * return false when trying to go above the root, or if the URI contains
+     * a null byte.
+     * 
+     * @param uriMB URI to be normalized
+     */
+    public static boolean normalize(MessageBytes uriMB) {
+
+        ByteChunk uriBC = uriMB.getByteChunk();
+        byte[] b = uriBC.getBytes();
+        int start = uriBC.getStart();
+        int end = uriBC.getEnd();
+
+        // URL * is acceptable
+        if ((end - start == 1) && b[start] == (byte) '*')
+          return true;
+
+        int pos = 0;
+        int index = 0;
+
+        // Replace '\' with '/'
+        // Check for null byte
+        for (pos = start; pos < end; pos++) {
+            if (b[pos] == (byte) '\\')
+                b[pos] = (byte) '/';
+            if (b[pos] == (byte) 0)
+                return false;
+        }
+
+        // The URL must start with '/'
+        if (b[start] != (byte) '/') {
+            return false;
+        }
+
+        // Replace "//" with "/"
+        for (pos = start; pos < (end - 1); pos++) {
+            if (b[pos] == (byte) '/') {
+                while ((pos + 1 < end) && (b[pos + 1] == (byte) '/')) {
+                    copyBytes(b, pos, pos + 1, end - pos - 1);
+                    end--;
+                }
+            }
+        }
+
+        // If the URI ends with "/." or "/..", then we append an extra "/"
+        // Note: It is possible to extend the URI by 1 without any side effect
+        // as the next character is a non-significant WS.
+        if (((end - start) >= 2) && (b[end - 1] == (byte) '.')) {
+            if ((b[end - 2] == (byte) '/') 
+                || ((b[end - 2] == (byte) '.') 
+                    && (b[end - 3] == (byte) '/'))) {
+                b[end] = (byte) '/';
+                end++;
+            }
+        }
+
+        uriBC.setEnd(end);
+
+        index = 0;
+
+        // Resolve occurrences of "/./" in the normalized path
+        while (true) {
+            index = uriBC.indexOf("/./", 0, 3, index);
+            if (index < 0)
+                break;
+            copyBytes(b, start + index, start + index + 2, 
+                      end - start - index - 2);
+            end = end - 2;
+            uriBC.setEnd(end);
+        }
+
+        index = 0;
+
+        // Resolve occurrences of "/../" in the normalized path
+        while (true) {
+            index = uriBC.indexOf("/../", 0, 4, index);
+            if (index < 0)
+                break;
+            // Prevent from going outside our context
+            if (index == 0)
+                return false;
+            int index2 = -1;
+            for (pos = start + index - 1; (pos >= 0) && (index2 < 0); pos --) {
+                if (b[pos] == (byte) '/') {
+                    index2 = pos;
+                }
+            }
+            copyBytes(b, start + index2, start + index + 3,
+                      end - start - index - 3);
+            end = end + index2 - index - 3;
+            uriBC.setEnd(end);
+            index = index2;
+        }
+
+        //uriBC.setBytes(b, start, end);
+        uriBC.setEnd(end);
+        return true;
+
+    }
+    
+    /**
+     * Copy an array of bytes to a different position. Used during 
+     * normalization.
+     */
+    public static void copyBytes(byte[] b, int dest, int src, int len) {
+        for (int pos = 0; pos < len; pos++) {
+            b[pos + dest] = b[pos + src];
+        }
+    }
+
+    
+    public void service(Request req, final Response res)
+            throws Exception {
+        try {
+          // compute decodedURI - not done by connector
+          decodeRequest(req);
+          MappingData mapRes = new MappingData();
+            
+          mapper.map(req.requestURI(), mapRes);
+            
+            Adapter h=(Adapter)mapRes.wrapper;
+            if (h != null) {
+                h.service( req, res );
+            } else {
+              res.setStatus(404);
+            }
+             
+        } catch( Throwable t ) {
+            t.printStackTrace();
+        } 
+
+        // Final processing
+        MessageWriter.getWriter(req, res, 0).flush();
+        res.finish();
+
+        req.recycle();
+        res.recycle();
+
+    }
+
+    public Mapper getMapper() {
+      return mapper;
+    }
+
+    public void setDefaultAdapter(Adapter adapter) {
+        mapper.addWrapper("/", adapter);
+    }
+    
+    public boolean event(Request req, Response res, boolean error) throws 
Exception {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    public boolean event(Request req, Response res, SocketStatus status)
+        throws Exception {
+      return false;
+    }
+
+}
\ No newline at end of file

Added: 
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/MessageReader.java
URL: 
http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/MessageReader.java?rev=640852&view=auto
==============================================================================
--- 
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/MessageReader.java
 (added)
+++ 
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/MessageReader.java
 Tue Mar 25 08:17:03 2008
@@ -0,0 +1,503 @@
+/*
+ * 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.adapters;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.HashMap;
+
+import org.apache.coyote.Request;
+import org.apache.tomcat.util.buf.B2CConverter;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.CharChunk;
+
+/**
+ * Refactored from catalina.connector.InputBuffer. Renamed to avoid conflict
+ * with coyote class.
+ * 
+ * TODO: move to coyote package.
+ */
+
+/**
+ * The buffer used by Tomcat request. This is a derivative of the Tomcat 3.3
+ * OutputBuffer, adapted to handle input instead of output. This allows 
+ * complete recycling of the facade objects (the ServletInputStream and the
+ * BufferedReader).
+ *
+ * @author Remy Maucherat
+ */
+public class MessageReader extends Reader
+    implements ByteChunk.ByteInputChannel, CharChunk.CharInputChannel,
+               CharChunk.CharOutputChannel {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    public static final String DEFAULT_ENCODING = 
+        org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING;
+    public static final int DEFAULT_BUFFER_SIZE = 8*1024;
+
+    // The buffer can be used for byte[] and char[] reading
+    // ( this is needed to support ServletInputStream and BufferedReader )
+    public final int INITIAL_STATE = 0;
+    public final int CHAR_STATE = 1;
+    public final int BYTE_STATE = 2;
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The byte buffer.
+     */
+    private ByteChunk bb;
+
+
+    /**
+     * The chunk buffer.
+     */
+    private CharChunk cb;
+
+
+    /**
+     * State of the output buffer.
+     */
+    private int state = 0;
+
+
+    /**
+     * Number of bytes read.
+     */
+    private int bytesRead = 0;
+
+
+    /**
+     * Number of chars read.
+     */
+    private int charsRead = 0;
+
+
+    /**
+     * Flag which indicates if the input buffer is closed.
+     */
+    private boolean closed = false;
+
+
+    /**
+     * Byte chunk used to input bytes.
+     */
+    private ByteChunk inputChunk = new ByteChunk();
+
+
+    /**
+     * Encoding to use.
+     */
+    private String enc;
+
+
+    /**
+     * Encoder is set.
+     */
+    private boolean gotEnc = false;
+
+
+    /**
+     * List of encoders.
+     */
+    protected HashMap encoders = new HashMap();
+
+
+    /**
+     * Current byte to char converter.
+     */
+    protected B2CConverter conv;
+
+
+    /**
+     * Associated Coyote request.
+     */
+    private Request coyoteRequest;
+
+
+    /**
+     * Buffer position.
+     */
+    private int markPos = -1;
+
+
+    /**
+     * Buffer size.
+     */
+    private int size = -1;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Default constructor. Allocate the buffer with the default buffer size.
+     */
+    public MessageReader() {
+
+        this(DEFAULT_BUFFER_SIZE);
+
+    }
+
+
+    /**
+     * Alternate constructor which allows specifying the initial buffer size.
+     * 
+     * @param size Buffer size to use
+     */
+    public MessageReader(int size) {
+
+        this.size = size;
+        bb = new ByteChunk(size);
+        bb.setLimit(size);
+        bb.setByteInputChannel(this);
+        cb = new CharChunk(size);
+        cb.setLimit(size);
+        cb.setOptimizedWrite(false);
+        cb.setCharInputChannel(this);
+        cb.setCharOutputChannel(this);
+
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Associated Coyote request.
+     * 
+     * @param coyoteRequest Associated Coyote request
+     */
+    public void setRequest(Request coyoteRequest) {
+       this.coyoteRequest = coyoteRequest;
+    }
+
+
+    /**
+     * Get associated Coyote request.
+     * 
+     * @return the associated Coyote request
+     */
+    public Request getRequest() {
+        return this.coyoteRequest;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Recycle the output buffer.
+     */
+    public void recycle() {
+        
+        state = INITIAL_STATE;
+        bytesRead = 0;
+        charsRead = 0;
+        
+        // If usage of mark made the buffer too big, reallocate it
+        if (cb.getChars().length > size) {
+            cb = new CharChunk(size);
+            cb.setLimit(size);
+            cb.setCharInputChannel(this);
+            cb.setCharOutputChannel(this);
+        } else {
+            cb.recycle();
+        }
+        markPos = -1;
+        bb.recycle(); 
+        closed = false;
+        
+        if (conv != null) {
+            conv.recycle();
+        }
+        
+        gotEnc = false;
+        enc = null;
+        
+    }
+
+
+    /**
+     * Close the input buffer.
+     * 
+     * @throws IOException An underlying IOException occurred
+     */
+    public void close()
+        throws IOException {
+        closed = true;
+    }
+
+
+    public int available()
+        throws IOException {
+        if (state == BYTE_STATE) {
+            return bb.getLength();
+        } else if (state == CHAR_STATE) {
+            return cb.getLength();
+        } else {
+            return 0;
+        }
+    }
+
+
+    // ------------------------------------------------- Bytes Handling Methods
+
+
+    /** 
+     * Reads new bytes in the byte chunk.
+     * 
+     * @param cbuf Byte buffer to be written to the response
+     * @param off Offset
+     * @param len Length
+     * 
+     * @throws IOException An underlying IOException occurred
+     */
+    public int realReadBytes(byte cbuf[], int off, int len)
+       throws IOException {
+
+        if (closed)
+            return -1;
+        if (coyoteRequest == null)
+            return -1;
+
+        state = BYTE_STATE;
+
+        int result = coyoteRequest.doRead(bb);
+
+        return result;
+
+    }
+
+
+    public int readByte()
+        throws IOException {
+        return bb.substract();
+    }
+
+
+    public int read(byte[] b, int off, int len)
+        throws IOException {
+        return bb.substract(b, off, len);
+    }
+
+
+    // ------------------------------------------------- Chars Handling Methods
+
+
+    /**
+     * Since the converter will use append, it is possible to get chars to
+     * be removed from the buffer for "writing". Since the chars have already
+     * been read before, they are ignored. If a mark was set, then the
+     * mark is lost.
+     */
+    public void realWriteChars(char c[], int off, int len) 
+        throws IOException {
+        markPos = -1;
+    }
+
+
+    public void setEncoding(String s) {
+        enc = s;
+    }
+
+
+    public int realReadChars(char cbuf[], int off, int len)
+        throws IOException {
+
+        if (!gotEnc)
+            setConverter();
+
+        if (bb.getLength() <= 0) {
+            int nRead = realReadBytes(bb.getBytes(), 0, bb.getBytes().length);
+            if (nRead < 0) {
+                return -1;
+            }
+        }
+
+        if (markPos == -1) {
+            cb.setOffset(0);
+            cb.setEnd(0);
+        }
+
+        conv.convert(bb, cb);
+        bb.setOffset(bb.getEnd());
+        state = CHAR_STATE;
+
+        return cb.getLength();
+
+    }
+
+
+    public int read()
+        throws IOException {
+        return cb.substract();
+    }
+
+
+    public int read(char[] cbuf)
+        throws IOException {
+        return read(cbuf, 0, cbuf.length);
+    }
+
+
+    public int read(char[] cbuf, int off, int len)
+        throws IOException {
+        return cb.substract(cbuf, off, len);
+    }
+
+
+    public long skip(long n)
+        throws IOException {
+
+        if (n < 0) {
+            throw new IllegalArgumentException();
+        }
+
+        long nRead = 0;
+        while (nRead < n) {
+            if (cb.getLength() >= n) {
+                cb.setOffset(cb.getStart() + (int) n);
+                nRead = n;
+            } else {
+                nRead += cb.getLength();
+                cb.setOffset(cb.getEnd());
+                int toRead = 0;
+                if (cb.getChars().length < (n - nRead)) {
+                    toRead = cb.getChars().length;
+                } else {
+                    toRead = (int) (n - nRead);
+                }
+                int nb = realReadChars(cb.getChars(), 0, toRead);
+                if (nb < 0)
+                    break;
+            }
+        }
+
+        return nRead;
+
+    }
+
+
+    public boolean ready()
+        throws IOException {
+        return (cb.getLength() > 0);
+    }
+
+
+    public boolean markSupported() {
+        return true;
+    }
+
+
+    public void mark(int readAheadLimit)
+        throws IOException {
+        if (cb.getLength() <= 0) {
+            cb.setOffset(0);
+            cb.setEnd(0);
+        } else {
+            if ((cb.getBuffer().length > (2 * size)) 
+                && (cb.getLength()) < (cb.getStart())) {
+                System.arraycopy(cb.getBuffer(), cb.getStart(), 
+                                 cb.getBuffer(), 0, cb.getLength());
+                cb.setEnd(cb.getLength());
+                cb.setOffset(0);
+            }
+        }
+        int offset = readAheadLimit;
+        if (offset < size) {
+            offset = size;
+        }
+        cb.setLimit(cb.getStart() + offset);
+        markPos = cb.getStart();
+    }
+
+
+    public void reset()
+        throws IOException {
+        if (state == CHAR_STATE) {
+            if (markPos < 0) {
+                cb.recycle();
+                markPos = -1;
+                throw new IOException();
+            } else {
+                cb.setOffset(markPos);
+            }
+        } else {
+            bb.recycle();
+        }
+    }
+
+
+    public void checkConverter() 
+        throws IOException {
+
+        if (!gotEnc)
+            setConverter();
+
+    }
+
+
+    protected void setConverter()
+        throws IOException {
+
+        if (coyoteRequest != null)
+            enc = coyoteRequest.getCharacterEncoding();
+
+        gotEnc = true;
+        if (enc == null)
+            enc = DEFAULT_ENCODING;
+        conv = (B2CConverter) encoders.get(enc);
+        if (conv == null) {
+            if (packageDefinitionEnabled && System.getSecurityManager() !=  
null) {
+                //SecurityUtil.isPackageProtectionEnabled()){
+                try{
+                    conv = (B2CConverter)AccessController.doPrivileged(
+                            new PrivilegedExceptionAction(){
+
+                                public Object run() throws IOException{
+                                    return new B2CConverter(enc);
+                                }
+
+                            }
+                    );              
+                }catch(PrivilegedActionException ex){
+                    Exception e = ex.getException();
+                    if (e instanceof IOException)
+                        throw (IOException)e; 
+                }
+            } else {
+                conv = new B2CConverter(enc);
+            }
+            encoders.put(enc, conv);
+        }
+
+    }
+
+    private static boolean packageDefinitionEnabled =  
+        (System.getProperty("package.definition") == null && 
+          System.getProperty("package.access")  == null) ? false : true;
+
+}

Added: 
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/MessageWriter.java
URL: 
http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/MessageWriter.java?rev=640852&view=auto
==============================================================================
--- 
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/MessageWriter.java
 (added)
+++ 
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/MessageWriter.java
 Tue Mar 25 08:17:03 2008
@@ -0,0 +1,680 @@
+/*
+ * 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.adapters;
+
+
+import java.io.IOException;
+import java.io.Writer;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.HashMap;
+
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.C2BConverter;
+import org.apache.tomcat.util.buf.CharChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+
+/*
+ * Refactoring: original code in catalina.connector.
+ * - renamed to OutputWriter to avoid confusion with coyote OutputBuffer
+ * - 
+ * TODO: move it to coyote, add Response.getWriter 
+ * 
+ */
+
+/**
+ * The buffer used by Tomcat response. This is a derivative of the Tomcat 3.3
+ * OutputBuffer, with the removal of some of the state handling (which in 
+ * Coyote is mostly the Processor's responsability).
+ *
+ * @author Costin Manolache
+ * @author Remy Maucherat
+ */
+public class MessageWriter extends Writer
+    implements ByteChunk.ByteOutputChannel, CharChunk.CharOutputChannel {
+
+    // used in getWriter, until a method is added to res.
+    private static final int WRITER_NOTE = 3;
+
+    // -------------------------------------------------------------- Constants
+
+
+    public static final String DEFAULT_ENCODING = 
+        org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING;
+    public static final int DEFAULT_BUFFER_SIZE = 8*1024;
+
+
+    // The buffer can be used for byte[] and char[] writing
+    // ( this is needed to support ServletOutputStream and for
+    // efficient implementations of templating systems )
+    public final int INITIAL_STATE = 0;
+    public final int CHAR_STATE = 1;
+    public final int BYTE_STATE = 2;
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The byte buffer.
+     */
+    private ByteChunk bb;
+
+
+    /**
+     * The chunk buffer.
+     */
+    private CharChunk cb;
+
+
+    /**
+     * State of the output buffer.
+     */
+    private int state = 0;
+
+
+    /**
+     * Number of bytes written.
+     */
+    private int bytesWritten = 0;
+
+
+    /**
+     * Number of chars written.
+     */
+    private int charsWritten = 0;
+
+
+    /**
+     * Flag which indicates if the output buffer is closed.
+     */
+    private boolean closed = false;
+
+
+    /**
+     * Do a flush on the next operation.
+     */
+    private boolean doFlush = false;
+
+
+    /**
+     * Byte chunk used to output bytes. This is just used to wrap the byte[]
+     * to match the coyote OutputBuffer interface
+     */
+    private ByteChunk outputChunk = new ByteChunk();
+
+
+    /**
+     * Encoding to use. 
+     * TODO: isn't it redundant ? enc, gotEnc, conv plus the enc in the bb
+     */
+    private String enc;
+
+
+    /**
+     * Encoder is set.
+     */
+    private boolean gotEnc = false;
+
+
+    /**
+     * List of encoders. The writer is reused - the encoder mapping 
+     * avoids creating expensive objects. In future it'll contain nio.Charsets
+     */
+    protected HashMap encoders = new HashMap();
+
+
+    /**
+     * Current char to byte converter. TODO: replace with Charset
+     */
+    protected C2BConverter conv;
+
+
+    /**
+     * Associated Coyote response.
+     */
+    private Response coyoteResponse;
+
+
+    /**
+     * Suspended flag. All output bytes will be swallowed if this is true.
+     */
+    private boolean suspended = false;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Default constructor. Allocate the buffer with the default buffer size.
+     */
+    public MessageWriter() {
+
+        this(DEFAULT_BUFFER_SIZE);
+
+    }
+
+
+    /**
+     * Alternate constructor which allows specifying the initial buffer size.
+     * 
+     * @param size Buffer size to use
+     */
+    public MessageWriter(int size) {
+
+        bb = new ByteChunk(size);
+        bb.setLimit(size);
+        bb.setByteOutputChannel(this);
+        cb = new CharChunk(size);
+        cb.setCharOutputChannel(this);
+        cb.setLimit(size);
+
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Associated Coyote response.
+     * 
+     * @param coyoteResponse Associated Coyote response
+     */
+    public void setResponse(Response coyoteResponse) {
+       this.coyoteResponse = coyoteResponse;
+    }
+
+
+    /**
+     * Get associated Coyote response.
+     * 
+     * @return the associated Coyote response
+     */
+    public Response getResponse() {
+        return this.coyoteResponse;
+    }
+
+
+    /**
+     * Is the response output suspended ?
+     * 
+     * @return suspended flag value
+     */
+    public boolean isSuspended() {
+        return this.suspended;
+    }
+
+
+    /**
+     * Set the suspended flag.
+     * 
+     * @param suspended New suspended flag value
+     */
+    public void setSuspended(boolean suspended) {
+        this.suspended = suspended;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Recycle the output buffer.
+     */
+    public void recycle() {
+        
+        state = INITIAL_STATE;
+        bytesWritten = 0;
+        charsWritten = 0;
+        
+        cb.recycle();
+        bb.recycle(); 
+        closed = false;
+        suspended = false;
+        
+        if (conv!= null) {
+            conv.recycle();
+        }
+        
+        gotEnc = false;
+        enc = null;
+        
+    }
+
+
+    /**
+     * Close the output buffer. This tries to calculate the response size if 
+     * the response has not been committed yet.
+     * 
+     * @throws IOException An underlying IOException occurred
+     */
+    public void close()
+        throws IOException {
+
+        if (closed)
+            return;
+        if (suspended)
+            return;
+
+        if ((!coyoteResponse.isCommitted()) 
+            && (coyoteResponse.getContentLengthLong() == -1)) {
+            // Flushing the char buffer
+            if (state == CHAR_STATE) {
+                cb.flushBuffer();
+                state = BYTE_STATE;
+            }
+            // If this didn't cause a commit of the response, the final content
+            // length can be calculated
+            if (!coyoteResponse.isCommitted()) {
+                coyoteResponse.setContentLength(bb.getLength());
+            }
+        }
+
+        doFlush(false);
+        closed = true;
+
+        coyoteResponse.finish();
+
+    }
+
+
+    /**
+     * Flush bytes or chars contained in the buffer.
+     * 
+     * @throws IOException An underlying IOException occurred
+     */
+    public void flush()
+        throws IOException {
+        doFlush(true);
+    }
+
+
+    /**
+     * Flush bytes or chars contained in the buffer.
+     * 
+     * @throws IOException An underlying IOException occurred
+     */
+    protected void doFlush(boolean realFlush)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        doFlush = true;
+        if (state == CHAR_STATE) {
+            cb.flushBuffer();
+            bb.flushBuffer();
+            state = BYTE_STATE;
+        } else if (state == BYTE_STATE) {
+            bb.flushBuffer();
+        } else if (state == INITIAL_STATE) {
+            // If the buffers are empty, commit the response header
+            coyoteResponse.sendHeaders();
+        }
+        doFlush = false;
+
+        if (realFlush) {
+            coyoteResponse.action(ActionCode.ACTION_CLIENT_FLUSH, 
+                                  coyoteResponse);
+            // If some exception occurred earlier, or if some IOE occurred
+            // here, notify the servlet with an IOE
+            if (coyoteResponse.isExceptionPresent()) {
+                throw new ClientAbortException
+                    (coyoteResponse.getErrorException());
+            }
+        }
+
+    }
+
+
+    // ------------------------------------------------- Bytes Handling Methods
+
+
+    /** 
+     * Sends the buffer data to the client output, checking the
+     * state of Response and calling the right interceptors.
+     * 
+     * @param buf Byte buffer to be written to the response
+     * @param off Offset
+     * @param cnt Length
+     * 
+     * @throws IOException An underlying IOException occurred
+     */
+    public void realWriteBytes(byte buf[], int off, int cnt)
+       throws IOException {
+
+        if (closed)
+            return;
+        if (coyoteResponse == null)
+            return;
+
+        // If we really have something to write
+        if (cnt > 0) {
+            // real write to the adapter
+            outputChunk.setBytes(buf, off, cnt);
+            try {
+                coyoteResponse.doWrite(outputChunk);
+            } catch (IOException e) {
+                // An IOException on a write is almost always due to
+                // the remote client aborting the request.  Wrap this
+                // so that it can be handled better by the error dispatcher.
+                throw new ClientAbortException(e);
+            }
+        }
+
+    }
+
+
+    public void write(byte b[], int off, int len) throws IOException {
+
+        if (suspended)
+            return;
+
+        if (state == CHAR_STATE)
+            cb.flushBuffer();
+        state = BYTE_STATE;
+        writeBytes(b, off, len);
+
+    }
+
+
+    private void writeBytes(byte b[], int off, int len) 
+        throws IOException {
+
+        if (closed)
+            return;
+
+        bb.append(b, off, len);
+        bytesWritten += len;
+
+        // if called from within flush(), then immediately flush
+        // remaining bytes
+        if (doFlush) {
+            bb.flushBuffer();
+        }
+
+    }
+
+
+    public void writeByte(int b)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        if (state == CHAR_STATE)
+            cb.flushBuffer();
+        state = BYTE_STATE;
+
+        bb.append( (byte)b );
+        bytesWritten++;
+
+    }
+
+
+    // ------------------------------------------------- Chars Handling Methods
+
+
+    public void write(int c)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        state = CHAR_STATE;
+
+        cb.append((char) c);
+        charsWritten++;
+
+    }
+
+
+    public void write(char c[])
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        write(c, 0, c.length);
+
+    }
+
+
+    public void write(char c[], int off, int len)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        state = CHAR_STATE;
+
+        cb.append(c, off, len);
+        charsWritten += len;
+
+    }
+
+
+    public void write(StringBuffer sb)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        state = CHAR_STATE;
+
+        int len = sb.length();
+        charsWritten += len;
+        cb.append(sb);
+
+    }
+
+
+    /**
+     * Append a string to the buffer
+     */
+    public void write(String s, int off, int len)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        state=CHAR_STATE;
+
+        charsWritten += len;
+        if (s==null)
+            s="null";
+        cb.append( s, off, len );
+
+    }
+
+
+    public void write(String s)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        state = CHAR_STATE;
+        if (s==null)
+            s="null";
+        write(s, 0, s.length());
+
+    } 
+
+
+    public void flushChars()
+        throws IOException {
+
+        cb.flushBuffer();
+        state = BYTE_STATE;
+
+    }
+
+
+    public boolean flushCharsNeeded() {
+        return state == CHAR_STATE;
+    }
+
+
+    public void setEncoding(String s) {
+        enc = s;
+    }
+
+
+    public void realWriteChars(char c[], int off, int len) 
+        throws IOException {
+
+        if (!gotEnc)
+            setConverter();
+
+        conv.convert(c, off, len);
+        conv.flushBuffer();    // ???
+
+    }
+
+
+    public void checkConverter() 
+        throws IOException {
+
+        if (!gotEnc)
+            setConverter();
+
+    }
+
+
+    protected void setConverter() 
+        throws IOException {
+
+        if (coyoteResponse != null)
+            enc = coyoteResponse.getCharacterEncoding();
+
+        gotEnc = true;
+        if (enc == null)
+            enc = DEFAULT_ENCODING;
+        conv = (C2BConverter) encoders.get(enc);
+        if (conv == null) {
+            
+            if (System.getSecurityManager() != null){
+                try{
+                    conv = (C2BConverter)AccessController.doPrivileged(
+                            new PrivilegedExceptionAction(){
+
+                                public Object run() throws IOException{
+                                    return new C2BConverter(bb, enc);
+                                }
+
+                            }
+                    );              
+                }catch(PrivilegedActionException ex){
+                    Exception e = ex.getException();
+                    if (e instanceof IOException)
+                        throw (IOException)e; 
+                }
+            } else {
+                conv = new C2BConverter(bb, enc);
+            }
+            
+            encoders.put(enc, conv);
+
+        }
+    }
+
+    
+    // --------------------  BufferedOutputStream compatibility
+
+
+    /**
+     * Real write - this buffer will be sent to the client
+     */
+    public void flushBytes()
+        throws IOException {
+
+        bb.flushBuffer();
+
+    }
+
+
+    public int getBytesWritten() {
+        return bytesWritten;
+    }
+
+
+    public int getCharsWritten() {
+        return charsWritten;
+    }
+
+
+    public int getContentWritten() {
+        return bytesWritten + charsWritten;
+    }
+
+
+    /** 
+     * True if this buffer hasn't been used ( since recycle() ) -
+     * i.e. no chars or bytes have been added to the buffer.  
+     */
+    public boolean isNew() {
+        return (bytesWritten == 0) && (charsWritten == 0);
+    }
+
+
+    public void setBufferSize(int size) {
+        if (size > bb.getLimit()) {// ??????
+           bb.setLimit(size);
+       }
+    }
+
+
+    public void reset() {
+
+        //count=0;
+        bb.recycle();
+        bytesWritten = 0;
+        cb.recycle();
+        charsWritten = 0;
+        gotEnc = false;
+        enc = null;
+        state = INITIAL_STATE;
+    }
+
+
+    public int getBufferSize() {
+       return bb.getLimit();
+    }
+
+
+    public static MessageWriter getWriter(Request req, Response res, int size) 
+    {        
+        MessageWriter 
out=(MessageWriter)req.getNote(MessageWriter.WRITER_NOTE);
+        if( out == null ) {
+            if( size<=0 ) {
+                out=new MessageWriter();
+            } else {
+                out=new MessageWriter(size);
+            }
+            out.setResponse(res);
+            req.setNote(MessageWriter.WRITER_NOTE, out );
+        }
+        return out;
+    }
+
+
+}

Added: 
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/SimpleFileAdapter.java
URL: 
http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/SimpleFileAdapter.java?rev=640852&view=auto
==============================================================================
--- 
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/SimpleFileAdapter.java
 (added)
+++ 
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/SimpleFileAdapter.java
 Tue Mar 25 08:17:03 2008
@@ -0,0 +1,138 @@
+package org.apache.coyote.adapters;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Properties;
+import java.util.logging.Logger;
+
+import org.apache.coyote.Adapter;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.C2BConverter;
+import org.apache.tomcat.util.net.SocketStatus;
+
+/**
+ * Serve a static file. This is the traditional method, a separate adapter 
could
+ * use Sendfile.
+ * 
+ * No fancy things. Not sure if it should have dir support even.
+ */
+public class SimpleFileAdapter implements Adapter {
+    Logger log = Logger.getLogger("coyote.file");
+
+    private String baseDir = "html/";
+
+    private File baseDirF;
+
+    public SimpleFileAdapter() {
+        init();
+    }
+
+    public void setBaseDir(String s) {
+        baseDir = s;
+    }
+
+    public void init() {
+        baseDirF = new File(baseDir);
+        try {
+            baseDir = baseDirF.getCanonicalPath();
+        } catch (IOException e) {
+        }
+    }
+
+    public void service(Request req, final Response res) throws Exception {
+
+        String uri = req.requestURI().toString();
+        if (uri.indexOf("..") >= 0) {
+            // not supported, too dangerous
+            // what else to escape ?
+            log.info("Invalid .. in  " + uri);
+            res.setStatus(404);
+            return;
+        }
+
+        // local file
+        File f = new File(baseDirF, uri);
+
+        // extra check
+        if (!f.getCanonicalPath().startsWith(baseDir)) {
+            log.info("File outside basedir " + baseDir + " " + f);
+            res.setStatus(404);
+            return;
+        }
+
+        if (f.isDirectory()) {
+            // check for index.html, redirect if exists
+            // list dir if not
+
+            f = new File(f, "index.html");
+        }
+
+        if (!f.exists()) {
+            log.info("File not found  " + f);
+            res.setStatus(404);
+            return;
+        }
+
+        res.setStatus(200);
+
+        // TODO: read from a resources in classpath !
+        // TODO: refactor to allow sendfile
+        // TODO: read mime types
+
+        int dot=uri.lastIndexOf(".");
+        if( dot > 0 ) {
+            String ext=uri.substring(dot+1);
+            String ct=getContentType(ext);
+            if( ct!=null) {
+                res.setContentType(ct);
+            }
+        }
+
+        res.setContentLength(f.length());
+
+        res.sendHeaders();
+
+        // not used - writes directly to response
+        // MessageWriter out = MessageWriter.getWriter(req, res, 0);
+
+        FileInputStream fis = new FileInputStream(f);
+        byte b[] = new byte[4096];
+        ByteChunk mb = new ByteChunk();
+        int rd = 0;
+        while ((rd = fis.read(b)) > 0) {
+            mb.setBytes(b, 0, rd);
+            res.doWrite(mb);
+        }
+
+    }
+
+    static Properties contentTypes=new Properties();
+    static {
+        initContentTypes();
+    }
+    static void initContentTypes() {
+        contentTypes.put("xhtml", "text/html");
+        contentTypes.put("html", "text/html");
+        contentTypes.put("txt", "text/plain");
+        contentTypes.put("css", "text/css");
+        contentTypes.put("xul", "application/vnd.mozilla.xul+xml");
+    }
+    
+    public String getContentType( String ext ) {
+        return contentTypes.getProperty( ext, "text/plain" );
+    }
+
+    public boolean event(Request req, Response res, boolean error) throws 
Exception {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    public boolean event(Request req, Response res, SocketStatus status)
+        throws Exception {
+      return false;
+    }
+    
+}
\ No newline at end of file

Added: 
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/StaticAdapter.java
URL: 
http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/StaticAdapter.java?rev=640852&view=auto
==============================================================================
--- 
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/StaticAdapter.java
 (added)
+++ 
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/StaticAdapter.java
 Tue Mar 25 08:17:03 2008
@@ -0,0 +1,85 @@
+package org.apache.coyote.adapters;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.logging.Logger;
+
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.Adapter;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.net.SocketStatus;
+
+/**
+ * Serve a static file. This is the traditional method, a separate adapter 
could
+ * use Sendfile.
+ * 
+ * No fancy things. Not sure if it should have dir support even.
+ */
+public class StaticAdapter implements Adapter {
+    Logger log = Logger.getLogger("coyote.static");
+    ByteChunk mb = new ByteChunk();
+    int len = 4096;
+    byte[] data;
+    
+    boolean chunked = false;
+    
+    String contentType = "text/plain";
+
+
+    public StaticAdapter() {
+        init();
+    }
+    
+    public StaticAdapter chunked() {
+      chunked = true;
+      return this;
+    }
+
+    public void setFile(String path) {
+      try {
+        FileInputStream fis = new FileInputStream(path);
+        mb.recycle();
+        mb.allocate(4096, -1);
+        
+        byte b[] = new byte[4096];
+        int rd = 0;
+        while ((rd = fis.read(b)) > 0) {
+            mb.append(b, 0, rd);
+        }
+        len = mb.getLength();
+      } catch (IOException e) {
+        throw new RuntimeException(e);
+      }
+    }
+    
+    public void init() {
+      data = new byte[len];
+      for (int i = 0; i < len; i++) {
+        data[i] = 'A';
+      }
+      mb.setBytes(data, 0, len);
+    }
+    
+    public void service(Request req, final Response res) throws Exception {
+
+      res.setStatus(200);
+      if (!chunked) {
+        res.setContentLength(len);
+      }
+      res.setContentType(contentType);
+      res.sendHeaders();
+      if (chunked) {
+        res.doWrite(mb);
+        res.action(ActionCode.ACTION_CLIENT_FLUSH, res);
+      }
+      res.doWrite(mb);
+    }
+
+    public boolean event(Request req, Response res, SocketStatus status)
+        throws Exception {
+      return false;
+    }
+    
+}
\ No newline at end of file

Added: 
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/StaticMain.java
URL: 
http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/StaticMain.java?rev=640852&view=auto
==============================================================================
--- 
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/StaticMain.java
 (added)
+++ 
tomcat/sandbox/tomcat-lite/coyote-extensions/org/apache/coyote/adapters/StaticMain.java
 Tue Mar 25 08:17:03 2008
@@ -0,0 +1,31 @@
+package org.apache.coyote.adapters;
+
+import org.apache.tomcat.util.IntrospectionUtils;
+
+
+
+/** 
+ * Serve a static resource, all in RAM. This should be the fastest way to 
+ * send data over HTTP with tomcat - no overhead except the coyote layer.
+ * 
+ * Used to benchmark and for files/resources that are extremely popular. 
+ */
+public class StaticMain extends CoyoteServer {
+  
+  public StaticMain() {        
+  }
+
+  protected void initAdapters() {
+    adapter = new StaticAdapter();
+  }
+
+  // ------------------- Main ---------------------
+  public static void main( String args[]) throws Exception {
+    StaticMain sa=new StaticMain();
+    IntrospectionUtils.processArgs(sa, args);
+    sa.setArgs(args);
+    sa.run();
+  }
+
+
+}
\ No newline at end of file



---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to