You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by co...@apache.org on 2005/11/19 18:48:29 UTC

svn commit: r345661 [3/3] - /tomcat/sandbox/java/org/apache/coyote/http11/

Added: tomcat/sandbox/java/org/apache/coyote/http11/InternalAprInputBuffer.java
URL: http://svn.apache.org/viewcvs/tomcat/sandbox/java/org/apache/coyote/http11/InternalAprInputBuffer.java?rev=345661&view=auto
==============================================================================
--- tomcat/sandbox/java/org/apache/coyote/http11/InternalAprInputBuffer.java (added)
+++ tomcat/sandbox/java/org/apache/coyote/http11/InternalAprInputBuffer.java Sat Nov 19 09:48:24 2005
@@ -0,0 +1,854 @@
+/*
+ *  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.http11;
+
+import java.io.IOException;
+import java.io.EOFException;
+import java.nio.ByteBuffer;
+
+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.MessageBytes;
+import org.apache.tomcat.util.http.MimeHeaders;
+import org.apache.tomcat.util.res.StringManager;
+
+import org.apache.coyote.InputBuffer;
+import org.apache.coyote.Request;
+
+/**
+ * Implementation of InputBuffer which provides HTTP request header parsing as
+ * well as transfer decoding.
+ *
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ */
+public class InternalAprInputBuffer implements InputBuffer {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Alternate constructor.
+     */
+    public InternalAprInputBuffer(Request request, int headerBufferSize, 
+                                  long readTimeout) {
+
+        this.request = request;
+        headers = request.getMimeHeaders();
+
+        headerBuffer1 = new byte[headerBufferSize];
+        headerBuffer2 = new byte[headerBufferSize];
+        bodyBuffer = new byte[headerBufferSize];
+        buf = headerBuffer1;
+        bbuf = ByteBuffer.allocateDirect(headerBufferSize);
+
+        headerBuffer = new char[headerBufferSize];
+        ascbuf = headerBuffer;
+
+        inputStreamInputBuffer = new SocketInputBuffer();
+
+        filterLibrary = new InputFilter[0];
+        activeFilters = new InputFilter[0];
+        lastActiveFilter = -1;
+
+        parsingHeader = true;
+        swallowInput = true;
+        
+        this.readTimeout = readTimeout * 1000;
+
+    }
+
+
+    // -------------------------------------------------------------- Variables
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Associated Coyote request.
+     */
+    protected Request request;
+
+
+    /**
+     * Headers of the associated request.
+     */
+    protected MimeHeaders headers;
+
+
+    /**
+     * State.
+     */
+    protected boolean parsingHeader;
+
+
+    /**
+     * Swallow input ? (in the case of an expectation)
+     */
+    protected boolean swallowInput;
+
+
+    /**
+     * Pointer to the current read buffer.
+     */
+    protected byte[] buf;
+
+
+    /**
+     * Pointer to the US-ASCII header buffer.
+     */
+    protected char[] ascbuf;
+
+
+    /**
+     * Last valid byte.
+     */
+    protected int lastValid;
+
+
+    /**
+     * Position in the buffer.
+     */
+    protected int pos;
+
+
+    /**
+     * HTTP header buffer no 1.
+     */
+    protected byte[] headerBuffer1;
+
+
+    /**
+     * HTTP header buffer no 2.
+     */
+    protected byte[] headerBuffer2;
+
+
+    /**
+     * HTTP body buffer.
+     */
+    protected byte[] bodyBuffer;
+
+
+    /**
+     * US-ASCII header buffer.
+     */
+    protected char[] headerBuffer;
+
+
+    /**
+     * Direct byte buffer used to perform actual reading.
+     */
+    protected ByteBuffer bbuf;
+
+
+    /**
+     * Underlying socket.
+     */
+    protected long socket;
+
+
+    /**
+     * Underlying input buffer.
+     */
+    protected InputBuffer inputStreamInputBuffer;
+
+
+    /**
+     * Filter library.
+     * Note: Filter[0] is always the "chunked" filter.
+     */
+    protected InputFilter[] filterLibrary;
+
+
+    /**
+     * Active filters (in order).
+     */
+    protected InputFilter[] activeFilters;
+
+
+    /**
+     * Index of the last active filter.
+     */
+    protected int lastActiveFilter;
+
+
+    /**
+     * The socket timeout used when reading the first block of the request
+     * header.
+     */
+    protected long readTimeout;
+    
+    
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Set the underlying socket.
+     */
+    public void setSocket(long socket) {
+        this.socket = socket;
+        Socket.setrbb(this.socket, bbuf);
+    }
+
+
+    /**
+     * Get the underlying socket input stream.
+     */
+    public long getSocket() {
+        return socket;
+    }
+
+
+    /**
+     * Add an input filter to the filter library.
+     */
+    public void addFilter(InputFilter filter) {
+
+        InputFilter[] newFilterLibrary = 
+            new InputFilter[filterLibrary.length + 1];
+        for (int i = 0; i < filterLibrary.length; i++) {
+            newFilterLibrary[i] = filterLibrary[i];
+        }
+        newFilterLibrary[filterLibrary.length] = filter;
+        filterLibrary = newFilterLibrary;
+
+        activeFilters = new InputFilter[filterLibrary.length];
+
+    }
+
+
+    /**
+     * Get filters.
+     */
+    public InputFilter[] getFilters() {
+
+        return filterLibrary;
+
+    }
+
+
+    /**
+     * Clear filters.
+     */
+    public void clearFilters() {
+
+        filterLibrary = new InputFilter[0];
+        lastActiveFilter = -1;
+
+    }
+
+
+    /**
+     * Add an input filter to the filter library.
+     */
+    public void addActiveFilter(InputFilter filter) {
+
+        if (lastActiveFilter == -1) {
+            filter.setBuffer(inputStreamInputBuffer);
+        } else {
+            for (int i = 0; i <= lastActiveFilter; i++) {
+                if (activeFilters[i] == filter)
+                    return;
+            }
+            filter.setBuffer(activeFilters[lastActiveFilter]);
+        }
+
+        activeFilters[++lastActiveFilter] = filter;
+
+        filter.setRequest(request);
+
+    }
+
+
+    /**
+     * Set the swallow input flag.
+     */
+    public void setSwallowInput(boolean swallowInput) {
+        this.swallowInput = swallowInput;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Recycle the input buffer. This should be called when closing the 
+     * connection.
+     */
+    public void recycle() {
+
+        // Recycle Request object
+        request.recycle();
+
+        socket = 0;
+        buf = headerBuffer1;
+        lastValid = 0;
+        pos = 0;
+        lastActiveFilter = -1;
+        parsingHeader = true;
+        swallowInput = true;
+
+    }
+
+
+    /**
+     * End processing of current HTTP request.
+     * Note: All bytes of the current request should have been already 
+     * consumed. This method only resets all the pointers so that we are ready
+     * to parse the next HTTP request.
+     */
+    public void nextRequest()
+        throws IOException {
+
+        // Recycle Request object
+        request.recycle();
+
+        // Determine the header buffer used for next request
+        byte[] newHeaderBuf = null;
+        if (buf == headerBuffer1) {
+            newHeaderBuf = headerBuffer2;
+        } else {
+            newHeaderBuf = headerBuffer1;
+        }
+
+        // Copy leftover bytes from buf to newHeaderBuf
+        System.arraycopy(buf, pos, newHeaderBuf, 0, lastValid - pos);
+
+        // Swap buffers
+        buf = newHeaderBuf;
+
+        // Recycle filters
+        for (int i = 0; i <= lastActiveFilter; i++) {
+            activeFilters[i].recycle();
+        }
+
+        // Reset pointers
+        lastValid = lastValid - pos;
+        pos = 0;
+        lastActiveFilter = -1;
+        parsingHeader = true;
+        swallowInput = true;
+
+    }
+
+
+    /**
+     * End request (consumes leftover bytes).
+     * 
+     * @throws IOException an undelying I/O error occured
+     */
+    public void endRequest()
+        throws IOException {
+
+        if (swallowInput && (lastActiveFilter != -1)) {
+            int extraBytes = (int) activeFilters[lastActiveFilter].end();
+            pos = pos - extraBytes;
+        }
+
+    }
+
+
+    /**
+     * Read the request line. This function is meant to be used during the 
+     * HTTP request header parsing. Do NOT attempt to read the request body 
+     * using it.
+     *
+     * @throws IOException If an exception occurs during the underlying socket
+     * read operations, or if the given buffer is not big enough to accomodate
+     * the whole line.
+     * @return true if data is properly fed; false if no data is available 
+     * immediately and thread should be freed
+     */
+    public boolean parseRequestLine(boolean useAvailableData)
+        throws IOException {
+
+        int start = 0;
+
+        //
+        // Skipping blank lines
+        //
+
+        byte chr = 0;
+        do {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (useAvailableData) {
+                    return false;
+                }
+                // Do a simple read with a short timeout
+                bbuf.clear();
+                int nRead = Socket.recvbbt
+                    (socket, 0, buf.length - lastValid, readTimeout);
+                if (nRead > 0) {
+                    bbuf.limit(nRead);
+                    bbuf.get(buf, pos, nRead);
+                    lastValid = pos + nRead;
+                } else {
+                    if ((-nRead) == Status.ETIMEDOUT || (-nRead) == Status.TIMEUP) {
+                        return false;
+                    } else {
+                        throw new IOException(sm.getString("iib.failedread"));
+                    }
+                }
+            }
+
+            chr = buf[pos++];
+
+        } while ((chr == Constants.CR) || (chr == Constants.LF));
+
+        pos--;
+
+        // Mark the current buffer position
+        start = pos;
+
+        if (pos >= lastValid) {
+            if (useAvailableData) {
+                return false;
+            }
+            // Do a simple read with a short timeout
+            bbuf.clear();
+            int nRead = Socket.recvbbt
+                (socket, 0, buf.length - lastValid, readTimeout);
+            if (nRead > 0) {
+                bbuf.limit(nRead);
+                bbuf.get(buf, pos, nRead);
+                lastValid = pos + nRead;
+            } else {
+                if ((-nRead) == Status.ETIMEDOUT || (-nRead) == Status.TIMEUP) {
+                    return false;
+                } else {
+                    throw new IOException(sm.getString("iib.failedread"));
+                }
+            }
+        }
+
+        //
+        // Reading the method name
+        // Method name is always US-ASCII
+        //
+
+        boolean space = false;
+
+        while (!space) {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill())
+                    throw new EOFException(sm.getString("iib.eof.error"));
+            }
+
+            ascbuf[pos] = (char) buf[pos];
+
+            if (buf[pos] == Constants.SP) {
+                space = true;
+                request.method().setChars(ascbuf, start, pos - start);
+            }
+
+            pos++;
+
+        }
+
+        // Mark the current buffer position
+        start = pos;
+        int end = 0;
+        int questionPos = -1;
+
+        //
+        // Reading the URI
+        //
+
+        space = false;
+        boolean eol = false;
+
+        while (!space) {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill())
+                    throw new EOFException(sm.getString("iib.eof.error"));
+            }
+
+            if (buf[pos] == Constants.SP) {
+                space = true;
+                end = pos;
+            } else if ((buf[pos] == Constants.CR) 
+                       || (buf[pos] == Constants.LF)) {
+                // HTTP/0.9 style request
+                eol = true;
+                space = true;
+                end = pos;
+            } else if ((buf[pos] == Constants.QUESTION) 
+                       && (questionPos == -1)) {
+                questionPos = pos;
+            }
+
+            pos++;
+
+        }
+
+        request.unparsedURI().setBytes(buf, start, end - start);
+        if (questionPos >= 0) {
+            request.queryString().setBytes(buf, questionPos + 1, 
+                                           end - questionPos - 1);
+            request.requestURI().setBytes(buf, start, questionPos - start);
+        } else {
+            request.requestURI().setBytes(buf, start, end - start);
+        }
+
+        // Mark the current buffer position
+        start = pos;
+        end = 0;
+
+        //
+        // Reading the protocol
+        // Protocol is always US-ASCII
+        //
+
+        while (!eol) {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill())
+                    throw new EOFException(sm.getString("iib.eof.error"));
+            }
+
+            ascbuf[pos] = (char) buf[pos];
+
+            if (buf[pos] == Constants.CR) {
+                end = pos;
+            } else if (buf[pos] == Constants.LF) {
+                if (end == 0)
+                    end = pos;
+                eol = true;
+            }
+
+            pos++;
+
+        }
+
+        if ((end - start) > 0) {
+            request.protocol().setChars(ascbuf, start, end - start);
+        } else {
+            request.protocol().setString("");
+        }
+        
+        return true;
+
+    }
+
+
+    /**
+     * Parse the HTTP headers.
+     */
+    public void parseHeaders()
+        throws IOException {
+
+        while (parseHeader()) {
+        }
+
+        parsingHeader = false;
+
+    }
+
+
+    /**
+     * Parse an HTTP header.
+     * 
+     * @return false after reading a blank line (which indicates that the
+     * HTTP header parsing is done
+     */
+    public boolean parseHeader()
+        throws IOException {
+
+        //
+        // Check for blank line
+        //
+
+        byte chr = 0;
+        while (true) {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill())
+                    throw new EOFException(sm.getString("iib.eof.error"));
+            }
+
+            chr = buf[pos];
+
+            if ((chr == Constants.CR) || (chr == Constants.LF)) {
+                if (chr == Constants.LF) {
+                    pos++;
+                    return false;
+                }
+            } else {
+                break;
+            }
+
+            pos++;
+
+        }
+
+        // Mark the current buffer position
+        int start = pos;
+
+        //
+        // Reading the header name
+        // Header name is always US-ASCII
+        //
+
+        boolean colon = false;
+        MessageBytes headerValue = null;
+
+        while (!colon) {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill())
+                    throw new EOFException(sm.getString("iib.eof.error"));
+            }
+
+            if (buf[pos] == Constants.COLON) {
+                colon = true;
+                headerValue = headers.addValue(ascbuf, start, pos - start);
+            }
+            chr = buf[pos];
+            if ((chr >= Constants.A) && (chr <= Constants.Z)) {
+                buf[pos] = (byte) (chr - Constants.LC_OFFSET);
+            }
+
+            ascbuf[pos] = (char) buf[pos];
+
+            pos++;
+
+        }
+
+        // Mark the current buffer position
+        start = pos;
+        int realPos = pos;
+
+        //
+        // Reading the header value (which can be spanned over multiple lines)
+        //
+
+        boolean eol = false;
+        boolean validLine = true;
+
+        while (validLine) {
+
+            boolean space = true;
+
+            // Skipping spaces
+            while (space) {
+
+                // Read new bytes if needed
+                if (pos >= lastValid) {
+                    if (!fill())
+                        throw new EOFException(sm.getString("iib.eof.error"));
+                }
+
+                if ((buf[pos] == Constants.SP) || (buf[pos] == Constants.HT)) {
+                    pos++;
+                } else {
+                    space = false;
+                }
+
+            }
+
+            int lastSignificantChar = realPos;
+
+            // Reading bytes until the end of the line
+            while (!eol) {
+
+                // Read new bytes if needed
+                if (pos >= lastValid) {
+                    if (!fill())
+                        throw new EOFException(sm.getString("iib.eof.error"));
+                }
+
+                if (buf[pos] == Constants.CR) {
+                } else if (buf[pos] == Constants.LF) {
+                    eol = true;
+                } else if (buf[pos] == Constants.SP) {
+                    buf[realPos] = buf[pos];
+                    realPos++;
+                } else {
+                    buf[realPos] = buf[pos];
+                    realPos++;
+                    lastSignificantChar = realPos;
+                }
+
+                pos++;
+
+            }
+
+            realPos = lastSignificantChar;
+
+            // Checking the first character of the new line. If the character
+            // is a LWS, then it's a multiline header
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill())
+                    throw new EOFException(sm.getString("iib.eof.error"));
+            }
+
+            chr = buf[pos];
+            if ((chr != Constants.SP) && (chr != Constants.HT)) {
+                validLine = false;
+            } else {
+                eol = false;
+                // Copying one extra space in the buffer (since there must
+                // be at least one space inserted between the lines)
+                buf[realPos] = chr;
+                realPos++;
+            }
+
+        }
+
+        // Set the header value
+        headerValue.setBytes(buf, start, realPos - start);
+
+        return true;
+
+    }
+
+
+    // ---------------------------------------------------- InputBuffer Methods
+
+
+    /**
+     * Read some bytes.
+     */
+    public int doRead(ByteChunk chunk, Request req) 
+        throws IOException {
+
+        if (lastActiveFilter == -1)
+            return inputStreamInputBuffer.doRead(chunk, req);
+        else
+            return activeFilters[lastActiveFilter].doRead(chunk,req);
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Fill the internal buffer using data from the undelying input stream.
+     * 
+     * @return false if at end of stream
+     */
+    protected boolean fill()
+        throws IOException {
+
+        int nRead = 0;
+
+        if (parsingHeader) {
+
+            if (lastValid == buf.length) {
+                throw new IOException
+                    (sm.getString("iib.requestheadertoolarge.error"));
+            }
+
+            bbuf.clear();
+            nRead = Socket.recvbb
+                (socket, 0, buf.length - lastValid);
+            if (nRead > 0) {
+                bbuf.limit(nRead);
+                bbuf.get(buf, pos, nRead);
+                lastValid = pos + nRead;
+            } else {
+                if ((-nRead) == Status.EAGAIN) {
+                    return false;
+                } else {
+                    throw new IOException(sm.getString("iib.failedread"));
+                }
+            }
+
+        } else {
+
+            buf = bodyBuffer;
+            pos = 0;
+            lastValid = 0;
+            bbuf.clear();
+            nRead = Socket.recvbb
+                (socket, 0, buf.length);
+            if (nRead > 0) {
+                bbuf.limit(nRead);
+                bbuf.get(buf, 0, nRead);
+                lastValid = nRead;
+            } else {
+                throw new IOException(sm.getString("iib.failedread"));
+            }
+
+        }
+
+        return (nRead > 0);
+
+    }
+
+
+    // ------------------------------------- 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 (pos >= lastValid) {
+                if (!fill())
+                    return -1;
+            }
+
+            int length = lastValid - pos;
+            chunk.setBytes(buf, pos, length);
+            pos = lastValid;
+
+            return (length);
+
+        }
+
+
+    }
+
+
+}

Added: tomcat/sandbox/java/org/apache/coyote/http11/InternalAprOutputBuffer.java
URL: http://svn.apache.org/viewcvs/tomcat/sandbox/java/org/apache/coyote/http11/InternalAprOutputBuffer.java?rev=345661&view=auto
==============================================================================
--- tomcat/sandbox/java/org/apache/coyote/http11/InternalAprOutputBuffer.java (added)
+++ tomcat/sandbox/java/org/apache/coyote/http11/InternalAprOutputBuffer.java Sat Nov 19 09:48:24 2005
@@ -0,0 +1,745 @@
+/*
+ *  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.http11;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import org.apache.tomcat.jni.Socket;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.CharChunk;
+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.res.StringManager;
+
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.OutputBuffer;
+import org.apache.coyote.Response;
+
+/**
+ * Output buffer.
+ * 
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ */
+public class InternalAprOutputBuffer 
+    implements OutputBuffer {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Default constructor.
+     */
+    public InternalAprOutputBuffer(Response response) {
+        this(response, Constants.DEFAULT_HTTP_HEADER_BUFFER_SIZE);
+    }
+
+
+    /**
+     * Alternate constructor.
+     */
+    public InternalAprOutputBuffer(Response response, int headerBufferSize) {
+
+        this.response = response;
+        headers = response.getMimeHeaders();
+
+        headerBuffer = new byte[headerBufferSize];
+        buf = headerBuffer;
+
+        bbuf = ByteBuffer.allocateDirect((headerBufferSize / 1500 + 1) * 1500);
+
+        outputStreamOutputBuffer = new SocketOutputBuffer();
+
+        filterLibrary = new OutputFilter[0];
+        activeFilters = new OutputFilter[0];
+        lastActiveFilter = -1;
+
+        committed = false;
+        finished = false;
+
+        // Cause loading of HttpMessages
+        HttpMessages.getMessage(200);
+
+    }
+
+
+    // -------------------------------------------------------------- Variables
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Associated Coyote response.
+     */
+    protected Response response;
+
+
+    /**
+     * Headers of the associated request.
+     */
+    protected MimeHeaders headers;
+
+
+    /**
+     * Committed flag.
+     */
+    protected boolean committed;
+
+
+    /**
+     * Finished flag.
+     */
+    protected boolean finished;
+
+
+    /**
+     * Pointer to the current read buffer.
+     */
+    protected byte[] buf;
+
+
+    /**
+     * Position in the buffer.
+     */
+    protected int pos;
+
+
+    /**
+     * HTTP header buffer.
+     */
+    protected byte[] headerBuffer;
+
+
+    /**
+     * Underlying socket.
+     */
+    protected long socket;
+
+
+    /**
+     * Underlying output buffer.
+     */
+    protected OutputBuffer outputStreamOutputBuffer;
+
+
+    /**
+     * Filter library.
+     * Note: Filter[0] is always the "chunked" filter.
+     */
+    protected OutputFilter[] filterLibrary;
+
+
+    /**
+     * Active filter (which is actually the top of the pipeline).
+     */
+    protected OutputFilter[] activeFilters;
+
+
+    /**
+     * Index of the last active filter.
+     */
+    protected int lastActiveFilter;
+
+
+    /**
+     * Direct byte buffer used for writing.
+     */
+    protected ByteBuffer bbuf = null;
+
+    
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Set the underlying socket.
+     */
+    public void setSocket(long socket) {
+        this.socket = socket;
+        Socket.setsbb(this.socket, bbuf);
+    }
+
+
+    /**
+     * Get the underlying socket input stream.
+     */
+    public long getSocket() {
+        return socket;
+    }
+
+
+    /**
+     * Set the socket buffer size.
+     */
+    public void setSocketBuffer(int socketBufferSize) {
+        // FIXME: Remove
+    }
+
+
+    /**
+     * Add an output filter to the filter library.
+     */
+    public void addFilter(OutputFilter filter) {
+
+        OutputFilter[] newFilterLibrary = 
+            new OutputFilter[filterLibrary.length + 1];
+        for (int i = 0; i < filterLibrary.length; i++) {
+            newFilterLibrary[i] = filterLibrary[i];
+        }
+        newFilterLibrary[filterLibrary.length] = filter;
+        filterLibrary = newFilterLibrary;
+
+        activeFilters = new OutputFilter[filterLibrary.length];
+
+    }
+
+
+    /**
+     * Get filters.
+     */
+    public OutputFilter[] getFilters() {
+
+        return filterLibrary;
+
+    }
+
+
+    /**
+     * Clear filters.
+     */
+    public void clearFilters() {
+
+        filterLibrary = new OutputFilter[0];
+        lastActiveFilter = -1;
+
+    }
+
+
+    /**
+     * Add an output filter to the filter library.
+     */
+    public void addActiveFilter(OutputFilter filter) {
+
+        if (lastActiveFilter == -1) {
+            filter.setBuffer(outputStreamOutputBuffer);
+        } else {
+            for (int i = 0; i <= lastActiveFilter; i++) {
+                if (activeFilters[i] == filter)
+                    return;
+            }
+            filter.setBuffer(activeFilters[lastActiveFilter]);
+        }
+
+        activeFilters[++lastActiveFilter] = filter;
+
+        filter.setResponse(response);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Flush the response.
+     * 
+     * @throws IOException an undelying I/O error occured
+     */
+    public void flush()
+        throws IOException {
+
+        if (!committed) {
+
+            // 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);
+
+        }
+
+        // Flush the current buffer
+        flushBuffer();
+
+    }
+
+
+    /**
+     * Reset current response.
+     * 
+     * @throws IllegalStateException if the response has already been committed
+     */
+    public void reset() {
+
+        if (committed)
+            throw new IllegalStateException(/*FIXME:Put an error message*/);
+
+        // Recycle Request object
+        response.recycle();
+
+    }
+
+
+    /**
+     * Recycle the output buffer. This should be called when closing the 
+     * connection.
+     */
+    public void recycle() {
+
+        // Recycle Request object
+        response.recycle();
+        bbuf.clear();
+
+        socket = 0;
+        buf = headerBuffer;
+        pos = 0;
+        lastActiveFilter = -1;
+        committed = false;
+        finished = false;
+
+    }
+
+
+    /**
+     * End processing of current HTTP request.
+     * Note: All bytes of the current request should have been already 
+     * consumed. This method only resets all the pointers so that we are ready
+     * to parse the next HTTP request.
+     */
+    public void nextRequest() {
+
+        // Recycle Request object
+        response.recycle();
+
+        // Determine the header buffer used for next request
+        buf = headerBuffer;
+
+        // Recycle filters
+        for (int i = 0; i <= lastActiveFilter; i++) {
+            activeFilters[i].recycle();
+        }
+
+        // Reset pointers
+        pos = 0;
+        lastActiveFilter = -1;
+        committed = false;
+        finished = false;
+
+    }
+
+
+    /**
+     * End request.
+     * 
+     * @throws IOException an undelying I/O error occured
+     */
+    public void endRequest()
+        throws IOException {
+
+        if (!committed) {
+
+            // 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);
+
+        }
+
+        if (finished)
+            return;
+
+        if (lastActiveFilter != -1)
+            activeFilters[lastActiveFilter].end();
+
+        flushBuffer();
+
+        finished = true;
+
+    }
+
+
+    // ------------------------------------------------ HTTP/1.1 Output Methods
+
+
+    /**
+     * Send an acknoledgement.
+     */
+    public void sendAck()
+        throws IOException {
+
+        if (!committed) {
+            if (Socket.send(socket, Constants.ACK_BYTES, 0, Constants.ACK_BYTES.length) < 0)
+                throw new IOException(sm.getString("iib.failedwrite"));
+        }
+
+    }
+
+
+    /**
+     * Send the response status line.
+     */
+    public void sendStatus() {
+
+        // Write protocol name
+        write(Constants.HTTP_11_BYTES);
+        buf[pos++] = Constants.SP;
+
+        // Write status code
+        int status = response.getStatus();
+        switch (status) {
+        case 200:
+            write(Constants._200_BYTES);
+            break;
+        case 400:
+            write(Constants._400_BYTES);
+            break;
+        case 404:
+            write(Constants._404_BYTES);
+            break;
+        default:
+            write(status);
+        }
+
+        buf[pos++] = Constants.SP;
+
+        // Write message
+        String message = response.getMessage();
+        if (message == null) {
+            write(HttpMessages.getMessage(status));
+        } else {
+            write(message);
+        }
+
+        // End the response status line
+        buf[pos++] = Constants.CR;
+        buf[pos++] = Constants.LF;
+
+    }
+
+
+    /**
+     * Send a header.
+     * 
+     * @param name Header name
+     * @param value Header value
+     */
+    public void sendHeader(MessageBytes name, MessageBytes value) {
+
+        write(name);
+        buf[pos++] = Constants.COLON;
+        buf[pos++] = Constants.SP;
+        write(value);
+        buf[pos++] = Constants.CR;
+        buf[pos++] = Constants.LF;
+
+    }
+
+
+    /**
+     * Send a header.
+     * 
+     * @param name Header name
+     * @param value Header value
+     */
+    public void sendHeader(ByteChunk name, ByteChunk value) {
+
+        write(name);
+        buf[pos++] = Constants.COLON;
+        buf[pos++] = Constants.SP;
+        write(value);
+        buf[pos++] = Constants.CR;
+        buf[pos++] = Constants.LF;
+
+    }
+
+
+    /**
+     * Send a header.
+     * 
+     * @param name Header name
+     * @param value Header value
+     */
+    public void sendHeader(String name, String value) {
+
+        write(name);
+        buf[pos++] = Constants.COLON;
+        buf[pos++] = Constants.SP;
+        write(value);
+        buf[pos++] = Constants.CR;
+        buf[pos++] = Constants.LF;
+
+    }
+
+
+    /**
+     * End the header block.
+     */
+    public void endHeaders() {
+
+        buf[pos++] = Constants.CR;
+        buf[pos++] = Constants.LF;
+
+    }
+
+
+    // --------------------------------------------------- OutputBuffer Methods
+
+
+    /**
+     * Write the contents of a byte chunk.
+     * 
+     * @param chunk byte chunk
+     * @return number of bytes written
+     * @throws IOException an undelying I/O error occured
+     */
+    public int doWrite(ByteChunk chunk, Response res) 
+        throws IOException {
+
+        if (!committed) {
+
+            // Send the connector a request for commit. The connector should
+            // then validate the headers, send them (using sendHeaders) and 
+            // set the filters accordingly.
+            response.action(ActionCode.ACTION_COMMIT, null);
+
+        }
+
+        if (lastActiveFilter == -1)
+            return outputStreamOutputBuffer.doWrite(chunk, res);
+        else
+            return activeFilters[lastActiveFilter].doWrite(chunk, res);
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Commit the response.
+     * 
+     * @throws IOException an undelying I/O error occured
+     */
+    protected void commit()
+        throws IOException {
+
+        // The response is now committed
+        committed = true;
+        response.setCommitted(true);
+
+        if (pos > 0) {
+            // Sending the response header buffer
+            bbuf.put(buf, 0, pos);
+        }
+
+    }
+
+
+    /**
+     * This method will write the contents of the specyfied message bytes 
+     * buffer to the output stream, without filtering. This method is meant to
+     * be used to write the response header.
+     * 
+     * @param mb data to be written
+     */
+    protected void write(MessageBytes mb) {
+
+        if (mb.getType() == MessageBytes.T_BYTES) {
+            ByteChunk bc = mb.getByteChunk();
+            write(bc);
+        } else if (mb.getType() == MessageBytes.T_CHARS) {
+            CharChunk cc = mb.getCharChunk();
+            write(cc);
+        } else {
+            write(mb.toString());
+        }
+
+    }
+
+
+    /**
+     * This method will write the contents of the specyfied message bytes 
+     * buffer to the output stream, without filtering. This method is meant to
+     * be used to write the response header.
+     * 
+     * @param bc data to be written
+     */
+    protected void write(ByteChunk bc) {
+
+        // Writing the byte chunk to the output buffer
+        System.arraycopy(bc.getBytes(), bc.getStart(), buf, pos,
+                         bc.getLength());
+        pos = pos + bc.getLength();
+
+    }
+
+
+    /**
+     * This method will write the contents of the specyfied char 
+     * buffer to the output stream, without filtering. This method is meant to
+     * be used to write the response header.
+     * 
+     * @param cc data to be written
+     */
+    protected void write(CharChunk cc) {
+
+        int start = cc.getStart();
+        int end = cc.getEnd();
+        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 = ' ';
+            }
+            buf[pos++] = (byte) c;
+        }
+
+    }
+
+
+    /**
+     * This method will write the contents of the specyfied byte 
+     * buffer to the output stream, without filtering. This method is meant to
+     * be used to write the response header.
+     * 
+     * @param b data to be written
+     */
+    public void write(byte[] b) {
+
+        // Writing the byte chunk to the output buffer
+        System.arraycopy(b, 0, buf, pos, b.length);
+        pos = pos + b.length;
+
+    }
+
+
+    /**
+     * This method will write the contents of the specyfied String to the 
+     * output stream, without filtering. This method is meant to be used to 
+     * write the response header.
+     * 
+     * @param s data to be written
+     */
+    protected void write(String s) {
+
+        if (s == null)
+            return;
+
+        // From the Tomcat 3.3 HTTP/1.0 connector
+        int len = s.length();
+        for (int i = 0; i < len; i++) {
+            char c = s.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 = ' ';
+            }
+            buf[pos++] = (byte) c;
+        }
+
+    }
+
+
+    /**
+     * This method will print the specified integer to the output stream, 
+     * without filtering. This method is meant to be used to write the 
+     * response header.
+     * 
+     * @param i data to be written
+     */
+    protected void write(int i) {
+
+        write(String.valueOf(i));
+
+    }
+
+
+    /**
+     * Callback to write data from the buffer.
+     */
+    protected void flushBuffer()
+        throws IOException {
+        if (bbuf.position() > 0) {
+            if (Socket.sendbb(socket, 0, bbuf.position()) < 0) {
+                throw new IOException(sm.getString("iib.failedwrite"));
+            }
+            bbuf.clear();
+        }
+    }
+
+
+    // ----------------------------------- 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 {
+
+            // FIXME: It would likely be more efficient to do a number of writes
+            // through the direct BB; however, the case should happen very rarely.
+            // An algorithm similar to ByteChunk.append may also be better.
+            if (chunk.getLength() > bbuf.capacity()) {
+                if (Socket.send(socket, chunk.getBuffer(), chunk.getStart(), 
+                        chunk.getLength()) < 0) {
+                    throw new IOException(sm.getString("iib.failedwrite"));
+                }
+            } else {
+                if (bbuf.position() + chunk.getLength() > bbuf.capacity()) {
+                    flushBuffer();
+                }
+                bbuf.put(chunk.getBuffer(), chunk.getStart(), chunk.getLength());
+            }
+            return chunk.getLength();
+
+        }
+
+
+    }
+
+
+}

Added: tomcat/sandbox/java/org/apache/coyote/http11/InternalInputBuffer.java
URL: http://svn.apache.org/viewcvs/tomcat/sandbox/java/org/apache/coyote/http11/InternalInputBuffer.java?rev=345661&view=auto
==============================================================================
--- tomcat/sandbox/java/org/apache/coyote/http11/InternalInputBuffer.java (added)
+++ tomcat/sandbox/java/org/apache/coyote/http11/InternalInputBuffer.java Sat Nov 19 09:48:24 2005
@@ -0,0 +1,793 @@
+/*
+ *  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.http11;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.EOFException;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.MimeHeaders;
+import org.apache.tomcat.util.res.StringManager;
+
+import org.apache.coyote.InputBuffer;
+import org.apache.coyote.Request;
+
+/**
+ * Implementation of InputBuffer which provides HTTP request header parsing as
+ * well as transfer decoding.
+ *
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ */
+public class InternalInputBuffer implements InputBuffer {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Default constructor.
+     */
+    public InternalInputBuffer(Request request) {
+        this(request, Constants.DEFAULT_HTTP_HEADER_BUFFER_SIZE);
+    }
+
+
+    /**
+     * Alternate constructor.
+     */
+    public InternalInputBuffer(Request request, int headerBufferSize) {
+
+        this.request = request;
+        headers = request.getMimeHeaders();
+
+        headerBuffer1 = new byte[headerBufferSize];
+        headerBuffer2 = new byte[headerBufferSize];
+        bodyBuffer = new byte[headerBufferSize];
+        buf = headerBuffer1;
+
+        headerBuffer = new char[headerBufferSize];
+        ascbuf = headerBuffer;
+
+        inputStreamInputBuffer = new InputStreamInputBuffer();
+
+        filterLibrary = new InputFilter[0];
+        activeFilters = new InputFilter[0];
+        lastActiveFilter = -1;
+
+        parsingHeader = true;
+        swallowInput = true;
+
+    }
+
+
+    // -------------------------------------------------------------- Variables
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Associated Coyote request.
+     */
+    protected Request request;
+
+
+    /**
+     * Headers of the associated request.
+     */
+    protected MimeHeaders headers;
+
+
+    /**
+     * State.
+     */
+    protected boolean parsingHeader;
+
+
+    /**
+     * Swallow input ? (in the case of an expectation)
+     */
+    protected boolean swallowInput;
+
+
+    /**
+     * Pointer to the current read buffer.
+     */
+    protected byte[] buf;
+
+
+    /**
+     * Pointer to the US-ASCII header buffer.
+     */
+    protected char[] ascbuf;
+
+
+    /**
+     * Last valid byte.
+     */
+    protected int lastValid;
+
+
+    /**
+     * Position in the buffer.
+     */
+    protected int pos;
+
+
+    /**
+     * HTTP header buffer no 1.
+     */
+    protected byte[] headerBuffer1;
+
+
+    /**
+     * HTTP header buffer no 2.
+     */
+    protected byte[] headerBuffer2;
+
+
+    /**
+     * HTTP body buffer.
+     */
+    protected byte[] bodyBuffer;
+
+
+    /**
+     * US-ASCII header buffer.
+     */
+    protected char[] headerBuffer;
+
+
+    /**
+     * Underlying input stream.
+     */
+    protected InputStream inputStream;
+
+
+    /**
+     * Underlying input buffer.
+     */
+    protected InputBuffer inputStreamInputBuffer;
+
+
+    /**
+     * Filter library.
+     * Note: Filter[0] is always the "chunked" filter.
+     */
+    protected InputFilter[] filterLibrary;
+
+
+    /**
+     * Active filters (in order).
+     */
+    protected InputFilter[] activeFilters;
+
+
+    /**
+     * Index of the last active filter.
+     */
+    protected int lastActiveFilter;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Set the underlying socket input stream.
+     */
+    public void setInputStream(InputStream inputStream) {
+
+        // FIXME: Check for null ?
+
+        this.inputStream = inputStream;
+
+    }
+
+
+    /**
+     * Get the underlying socket input stream.
+     */
+    public InputStream getInputStream() {
+
+        return inputStream;
+
+    }
+
+
+    /**
+     * Add an input filter to the filter library.
+     */
+    public void addFilter(InputFilter filter) {
+
+        // FIXME: Check for null ?
+
+        InputFilter[] newFilterLibrary = 
+            new InputFilter[filterLibrary.length + 1];
+        for (int i = 0; i < filterLibrary.length; i++) {
+            newFilterLibrary[i] = filterLibrary[i];
+        }
+        newFilterLibrary[filterLibrary.length] = filter;
+        filterLibrary = newFilterLibrary;
+
+        activeFilters = new InputFilter[filterLibrary.length];
+
+    }
+
+
+    /**
+     * Get filters.
+     */
+    public InputFilter[] getFilters() {
+
+        return filterLibrary;
+
+    }
+
+
+    /**
+     * Clear filters.
+     */
+    public void clearFilters() {
+
+        filterLibrary = new InputFilter[0];
+        lastActiveFilter = -1;
+
+    }
+
+
+    /**
+     * Add an input filter to the filter library.
+     */
+    public void addActiveFilter(InputFilter filter) {
+
+        if (lastActiveFilter == -1) {
+            filter.setBuffer(inputStreamInputBuffer);
+        } else {
+            for (int i = 0; i <= lastActiveFilter; i++) {
+                if (activeFilters[i] == filter)
+                    return;
+            }
+            filter.setBuffer(activeFilters[lastActiveFilter]);
+        }
+
+        activeFilters[++lastActiveFilter] = filter;
+
+        filter.setRequest(request);
+
+    }
+
+
+    /**
+     * Set the swallow input flag.
+     */
+    public void setSwallowInput(boolean swallowInput) {
+        this.swallowInput = swallowInput;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Recycle the input buffer. This should be called when closing the 
+     * connection.
+     */
+    public void recycle() {
+
+        // Recycle Request object
+        request.recycle();
+
+        inputStream = null;
+        buf = headerBuffer1;
+        lastValid = 0;
+        pos = 0;
+        lastActiveFilter = -1;
+        parsingHeader = true;
+        swallowInput = true;
+
+    }
+
+
+    /**
+     * End processing of current HTTP request.
+     * Note: All bytes of the current request should have been already 
+     * consumed. This method only resets all the pointers so that we are ready
+     * to parse the next HTTP request.
+     */
+    public void nextRequest()
+        throws IOException {
+
+        // Recycle Request object
+        request.recycle();
+
+        // Determine the header buffer used for next request
+        byte[] newHeaderBuf = null;
+        if (buf == headerBuffer1) {
+            newHeaderBuf = headerBuffer2;
+        } else {
+            newHeaderBuf = headerBuffer1;
+        }
+
+        // Copy leftover bytes from buf to newHeaderBuf
+        System.arraycopy(buf, pos, newHeaderBuf, 0, lastValid - pos);
+
+        // Swap buffers
+        buf = newHeaderBuf;
+
+        // Recycle filters
+        for (int i = 0; i <= lastActiveFilter; i++) {
+            activeFilters[i].recycle();
+        }
+
+        // Reset pointers
+        lastValid = lastValid - pos;
+        pos = 0;
+        lastActiveFilter = -1;
+        parsingHeader = true;
+        swallowInput = true;
+
+    }
+
+
+    /**
+     * End request (consumes leftover bytes).
+     * 
+     * @throws IOException an undelying I/O error occured
+     */
+    public void endRequest()
+        throws IOException {
+
+        if (swallowInput && (lastActiveFilter != -1)) {
+            int extraBytes = (int) activeFilters[lastActiveFilter].end();
+            pos = pos - extraBytes;
+        }
+
+    }
+
+
+    /**
+     * Read the request line. This function is meant to be used during the 
+     * HTTP request header parsing. Do NOT attempt to read the request body 
+     * using it.
+     *
+     * @throws IOException If an exception occurs during the underlying socket
+     * read operations, or if the given buffer is not big enough to accomodate
+     * the whole line.
+     */
+    public void parseRequestLine()
+        throws IOException {
+
+        int start = 0;
+
+        //
+        // Skipping blank lines
+        //
+
+        byte chr = 0;
+        do {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill())
+                    throw new EOFException(sm.getString("iib.eof.error"));
+            }
+
+            chr = buf[pos++];
+
+        } while ((chr == Constants.CR) || (chr == Constants.LF));
+
+        pos--;
+
+        // Mark the current buffer position
+        start = pos;
+
+        //
+        // Reading the method name
+        // Method name is always US-ASCII
+        //
+
+        boolean space = false;
+
+        while (!space) {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill())
+                    throw new EOFException(sm.getString("iib.eof.error"));
+            }
+
+            ascbuf[pos] = (char) buf[pos];
+
+            if (buf[pos] == Constants.SP) {
+                space = true;
+                request.method().setChars(ascbuf, start, pos - start);
+            }
+
+            pos++;
+
+        }
+
+        // Mark the current buffer position
+        start = pos;
+        int end = 0;
+        int questionPos = -1;
+
+        //
+        // Reading the URI
+        //
+
+        space = false;
+        boolean eol = false;
+
+        while (!space) {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill())
+                    throw new EOFException(sm.getString("iib.eof.error"));
+            }
+
+            if (buf[pos] == Constants.SP) {
+                space = true;
+                end = pos;
+            } else if ((buf[pos] == Constants.CR) 
+                       || (buf[pos] == Constants.LF)) {
+                // HTTP/0.9 style request
+                eol = true;
+                space = true;
+                end = pos;
+            } else if ((buf[pos] == Constants.QUESTION) 
+                       && (questionPos == -1)) {
+                questionPos = pos;
+            }
+
+            pos++;
+
+        }
+
+        request.unparsedURI().setBytes(buf, start, end - start);
+        if (questionPos >= 0) {
+            request.queryString().setBytes(buf, questionPos + 1, 
+                                           end - questionPos - 1);
+            request.requestURI().setBytes(buf, start, questionPos - start);
+        } else {
+            request.requestURI().setBytes(buf, start, end - start);
+        }
+
+        // Mark the current buffer position
+        start = pos;
+        end = 0;
+
+        //
+        // Reading the protocol
+        // Protocol is always US-ASCII
+        //
+
+        while (!eol) {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill())
+                    throw new EOFException(sm.getString("iib.eof.error"));
+            }
+
+            ascbuf[pos] = (char) buf[pos];
+
+            if (buf[pos] == Constants.CR) {
+                end = pos;
+            } else if (buf[pos] == Constants.LF) {
+                if (end == 0)
+                    end = pos;
+                eol = true;
+            }
+
+            pos++;
+
+        }
+
+        if ((end - start) > 0) {
+            request.protocol().setChars(ascbuf, start, end - start);
+        } else {
+            request.protocol().setString("");
+        }
+
+    }
+
+
+    /**
+     * Parse the HTTP headers.
+     */
+    public void parseHeaders()
+        throws IOException {
+
+        while (parseHeader()) {
+        }
+
+        parsingHeader = false;
+
+    }
+
+
+    /**
+     * Parse an HTTP header.
+     * 
+     * @return false after reading a blank line (which indicates that the
+     * HTTP header parsing is done
+     */
+    public boolean parseHeader()
+        throws IOException {
+
+        //
+        // Check for blank line
+        //
+
+        byte chr = 0;
+        while (true) {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill())
+                    throw new EOFException(sm.getString("iib.eof.error"));
+            }
+
+            chr = buf[pos];
+
+            if ((chr == Constants.CR) || (chr == Constants.LF)) {
+                if (chr == Constants.LF) {
+                    pos++;
+                    return false;
+                }
+            } else {
+                break;
+            }
+
+            pos++;
+
+        }
+
+        // Mark the current buffer position
+        int start = pos;
+
+        //
+        // Reading the header name
+        // Header name is always US-ASCII
+        //
+
+        boolean colon = false;
+        MessageBytes headerValue = null;
+
+        while (!colon) {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill())
+                    throw new EOFException(sm.getString("iib.eof.error"));
+            }
+
+            if (buf[pos] == Constants.COLON) {
+                colon = true;
+                headerValue = headers.addValue(ascbuf, start, pos - start);
+            }
+            chr = buf[pos];
+            if ((chr >= Constants.A) && (chr <= Constants.Z)) {
+                buf[pos] = (byte) (chr - Constants.LC_OFFSET);
+            }
+
+            ascbuf[pos] = (char) buf[pos];
+
+            pos++;
+
+        }
+
+        // Mark the current buffer position
+        start = pos;
+        int realPos = pos;
+
+        //
+        // Reading the header value (which can be spanned over multiple lines)
+        //
+
+        boolean eol = false;
+        boolean validLine = true;
+
+        while (validLine) {
+
+            boolean space = true;
+
+            // Skipping spaces
+            while (space) {
+
+                // Read new bytes if needed
+                if (pos >= lastValid) {
+                    if (!fill())
+                        throw new EOFException(sm.getString("iib.eof.error"));
+                }
+
+                if ((buf[pos] == Constants.SP) || (buf[pos] == Constants.HT)) {
+                    pos++;
+                } else {
+                    space = false;
+                }
+
+            }
+
+            int lastSignificantChar = realPos;
+
+            // Reading bytes until the end of the line
+            while (!eol) {
+
+                // Read new bytes if needed
+                if (pos >= lastValid) {
+                    if (!fill())
+                        throw new EOFException(sm.getString("iib.eof.error"));
+                }
+
+                if (buf[pos] == Constants.CR) {
+                } else if (buf[pos] == Constants.LF) {
+                    eol = true;
+                } else if (buf[pos] == Constants.SP) {
+                    buf[realPos] = buf[pos];
+                    realPos++;
+                } else {
+                    buf[realPos] = buf[pos];
+                    realPos++;
+                    lastSignificantChar = realPos;
+                }
+
+                pos++;
+
+            }
+
+            realPos = lastSignificantChar;
+
+            // Checking the first character of the new line. If the character
+            // is a LWS, then it's a multiline header
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill())
+                    throw new EOFException(sm.getString("iib.eof.error"));
+            }
+
+            chr = buf[pos];
+            if ((chr != Constants.SP) && (chr != Constants.HT)) {
+                validLine = false;
+            } else {
+                eol = false;
+                // Copying one extra space in the buffer (since there must
+                // be at least one space inserted between the lines)
+                buf[realPos] = chr;
+                realPos++;
+            }
+
+        }
+
+        // Set the header value
+        headerValue.setBytes(buf, start, realPos - start);
+
+        return true;
+
+    }
+
+
+    // ---------------------------------------------------- InputBuffer Methods
+
+
+    /**
+     * Read some bytes.
+     */
+    public int doRead(ByteChunk chunk, Request req) 
+        throws IOException {
+
+        if (lastActiveFilter == -1)
+            return inputStreamInputBuffer.doRead(chunk, req);
+        else
+            return activeFilters[lastActiveFilter].doRead(chunk,req);
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Fill the internal buffer using data from the undelying input stream.
+     * 
+     * @return false if at end of stream
+     */
+    protected boolean fill()
+        throws IOException {
+
+        int nRead = 0;
+
+        if (parsingHeader) {
+
+            if (lastValid == buf.length) {
+                throw new IOException
+                    (sm.getString("iib.requestheadertoolarge.error"));
+            }
+
+            nRead = inputStream.read(buf, pos, buf.length - lastValid);
+            if (nRead > 0) {
+                lastValid = pos + nRead;
+            }
+
+        } else {
+
+            buf = bodyBuffer;
+            pos = 0;
+            lastValid = 0;
+            nRead = inputStream.read(buf, 0, buf.length);
+            if (nRead > 0) {
+                lastValid = nRead;
+            }
+
+        }
+
+        return (nRead > 0);
+
+    }
+
+
+    // ------------------------------------- InputStreamInputBuffer Inner Class
+
+
+    /**
+     * This class is an input buffer which will read its data from an input
+     * stream.
+     */
+    protected class InputStreamInputBuffer 
+        implements InputBuffer {
+
+
+        /**
+         * Read bytes into the specified chunk.
+         */
+        public int doRead(ByteChunk chunk, Request req ) 
+            throws IOException {
+
+            if (pos >= lastValid) {
+                if (!fill())
+                    return -1;
+            }
+
+            int length = lastValid - pos;
+            chunk.setBytes(buf, pos, length);
+            pos = lastValid;
+
+            return (length);
+
+        }
+
+
+    }
+
+
+}

Added: tomcat/sandbox/java/org/apache/coyote/http11/InternalOutputBuffer.java
URL: http://svn.apache.org/viewcvs/tomcat/sandbox/java/org/apache/coyote/http11/InternalOutputBuffer.java?rev=345661&view=auto
==============================================================================
--- tomcat/sandbox/java/org/apache/coyote/http11/InternalOutputBuffer.java (added)
+++ tomcat/sandbox/java/org/apache/coyote/http11/InternalOutputBuffer.java Sat Nov 19 09:48:24 2005
@@ -0,0 +1,783 @@
+/*
+ *  Copyright 1999-2005 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.http11;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.CharChunk;
+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.res.StringManager;
+
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.OutputBuffer;
+import org.apache.coyote.Response;
+
+/**
+ * Output buffer.
+ * 
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ */
+public class InternalOutputBuffer 
+    implements OutputBuffer, ByteChunk.ByteOutputChannel {
+
+    // -------------------------------------------------------------- Constants
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Default constructor.
+     */
+    public InternalOutputBuffer(Response response) {
+        this(response, Constants.DEFAULT_HTTP_HEADER_BUFFER_SIZE);
+    }
+
+
+    /**
+     * Alternate constructor.
+     */
+    public InternalOutputBuffer(Response response, int headerBufferSize) {
+
+        this.response = response;
+        headers = response.getMimeHeaders();
+
+        headerBuffer = new byte[headerBufferSize];
+        buf = headerBuffer;
+
+        outputStreamOutputBuffer = new OutputStreamOutputBuffer();
+
+        filterLibrary = new OutputFilter[0];
+        activeFilters = new OutputFilter[0];
+        lastActiveFilter = -1;
+
+        socketBuffer = new ByteChunk();
+        socketBuffer.setByteOutputChannel(this);
+
+        committed = false;
+        finished = false;
+
+    }
+
+
+    // -------------------------------------------------------------- Variables
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Associated Coyote response.
+     */
+    protected Response response;
+
+
+    /**
+     * Headers of the associated request.
+     */
+    protected MimeHeaders headers;
+
+
+    /**
+     * Committed flag.
+     */
+    protected boolean committed;
+
+
+    /**
+     * Finished flag.
+     */
+    protected boolean finished;
+
+
+    /**
+     * Pointer to the current read buffer.
+     */
+    protected byte[] buf;
+
+
+    /**
+     * Position in the buffer.
+     */
+    protected int pos;
+
+
+    /**
+     * HTTP header buffer.
+     */
+    protected byte[] headerBuffer;
+
+
+    /**
+     * Underlying output stream.
+     */
+    protected OutputStream outputStream;
+
+
+    /**
+     * Underlying output buffer.
+     */
+    protected OutputBuffer outputStreamOutputBuffer;
+
+
+    /**
+     * Filter library.
+     * Note: Filter[0] is always the "chunked" filter.
+     */
+    protected OutputFilter[] filterLibrary;
+
+
+    /**
+     * Active filter (which is actually the top of the pipeline).
+     */
+    protected OutputFilter[] activeFilters;
+
+
+    /**
+     * Index of the last active filter.
+     */
+    protected int lastActiveFilter;
+
+
+    /**
+     * Socket buffer.
+     */
+    protected ByteChunk socketBuffer;
+
+
+    /**
+     * Socket buffer (extra buffering to reduce number of packets sent).
+     */
+    protected boolean useSocketBuffer = false;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Set the underlying socket output stream.
+     */
+    public void setOutputStream(OutputStream outputStream) {
+
+        // FIXME: Check for null ?
+
+        this.outputStream = outputStream;
+
+    }
+
+
+    /**
+     * Get the underlying socket output stream.
+     */
+    public OutputStream getOutputStream() {
+
+        return outputStream;
+
+    }
+
+
+    /**
+     * Set the socket buffer size.
+     */
+    public void setSocketBuffer(int socketBufferSize) {
+
+        if (socketBufferSize > 500) {
+            useSocketBuffer = true;
+            socketBuffer.allocate(socketBufferSize, socketBufferSize);
+        } else {
+            useSocketBuffer = false;
+        }
+
+    }
+
+
+    /**
+     * Add an output filter to the filter library.
+     */
+    public void addFilter(OutputFilter filter) {
+
+        OutputFilter[] newFilterLibrary = 
+            new OutputFilter[filterLibrary.length + 1];
+        for (int i = 0; i < filterLibrary.length; i++) {
+            newFilterLibrary[i] = filterLibrary[i];
+        }
+        newFilterLibrary[filterLibrary.length] = filter;
+        filterLibrary = newFilterLibrary;
+
+        activeFilters = new OutputFilter[filterLibrary.length];
+
+    }
+
+
+    /**
+     * Get filters.
+     */
+    public OutputFilter[] getFilters() {
+
+        return filterLibrary;
+
+    }
+
+
+    /**
+     * Clear filters.
+     */
+    public void clearFilters() {
+
+        filterLibrary = new OutputFilter[0];
+        lastActiveFilter = -1;
+
+    }
+
+
+    /**
+     * Add an output filter to the filter library.
+     */
+    public void addActiveFilter(OutputFilter filter) {
+
+        if (lastActiveFilter == -1) {
+            filter.setBuffer(outputStreamOutputBuffer);
+        } else {
+            for (int i = 0; i <= lastActiveFilter; i++) {
+                if (activeFilters[i] == filter)
+                    return;
+            }
+            filter.setBuffer(activeFilters[lastActiveFilter]);
+        }
+
+        activeFilters[++lastActiveFilter] = filter;
+
+        filter.setResponse(response);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Flush the response.
+     * 
+     * @throws IOException an undelying I/O error occured
+     */
+    public void flush()
+        throws IOException {
+
+        if (!committed) {
+
+            // 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);
+
+        }
+
+        // Flush the current buffer
+        if (useSocketBuffer) {
+            socketBuffer.flushBuffer();
+        }
+
+    }
+
+
+    /**
+     * Reset current response.
+     * 
+     * @throws IllegalStateException if the response has already been committed
+     */
+    public void reset() {
+
+        if (committed)
+            throw new IllegalStateException(/*FIXME:Put an error message*/);
+
+        // Recycle Request object
+        response.recycle();
+
+    }
+
+
+    /**
+     * Recycle the output buffer. This should be called when closing the 
+     * connection.
+     */
+    public void recycle() {
+
+        // Recycle Request object
+        response.recycle();
+        socketBuffer.recycle();
+
+        outputStream = null;
+        buf = headerBuffer;
+        pos = 0;
+        lastActiveFilter = -1;
+        committed = false;
+        finished = false;
+
+    }
+
+
+    /**
+     * End processing of current HTTP request.
+     * Note: All bytes of the current request should have been already 
+     * consumed. This method only resets all the pointers so that we are ready
+     * to parse the next HTTP request.
+     */
+    public void nextRequest() {
+
+        // Recycle Request object
+        response.recycle();
+        socketBuffer.recycle();
+
+        // Determine the header buffer used for next request
+        buf = headerBuffer;
+
+        // Recycle filters
+        for (int i = 0; i <= lastActiveFilter; i++) {
+            activeFilters[i].recycle();
+        }
+
+        // Reset pointers
+        pos = 0;
+        lastActiveFilter = -1;
+        committed = false;
+        finished = false;
+
+    }
+
+
+    /**
+     * End request.
+     * 
+     * @throws IOException an undelying I/O error occured
+     */
+    public void endRequest()
+        throws IOException {
+
+        if (!committed) {
+
+            // 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);
+
+        }
+
+        if (finished)
+            return;
+
+        if (lastActiveFilter != -1)
+            activeFilters[lastActiveFilter].end();
+
+        if (useSocketBuffer) {
+            socketBuffer.flushBuffer();
+        }
+
+        finished = true;
+
+    }
+
+
+    // ------------------------------------------------ HTTP/1.1 Output Methods
+
+
+    /**
+     * Send an acknoledgement.
+     */
+    public void sendAck()
+        throws IOException {
+
+        if (!committed)
+            outputStream.write(Constants.ACK_BYTES);
+
+    }
+
+
+    /**
+     * Send the response status line.
+     */
+    public void sendStatus() {
+
+        // Write protocol name
+        write(Constants.HTTP_11_BYTES);
+        buf[pos++] = Constants.SP;
+
+        // Write status code
+        int status = response.getStatus();
+        switch (status) {
+        case 200:
+            write(Constants._200_BYTES);
+            break;
+        case 400:
+            write(Constants._400_BYTES);
+            break;
+        case 404:
+            write(Constants._404_BYTES);
+            break;
+        default:
+            write(status);
+        }
+
+        buf[pos++] = Constants.SP;
+
+        // Write message
+        String message = response.getMessage();
+        if (message == null) {
+            write(getMessage(status));
+        } else {
+            write(message);
+        }
+
+        // End the response status line
+        if (System.getSecurityManager() != null){
+           AccessController.doPrivileged(
+                new PrivilegedAction(){
+                    public Object run(){
+                        buf[pos++] = Constants.CR;
+                        buf[pos++] = Constants.LF;
+                        return null;
+                    }
+                }
+           );
+        } else {
+            buf[pos++] = Constants.CR;
+            buf[pos++] = Constants.LF;
+        }
+
+    }
+
+    private String getMessage(final int message){
+        if (System.getSecurityManager() != null){
+           return (String)AccessController.doPrivileged(
+                new PrivilegedAction(){
+                    public Object run(){
+                        return HttpMessages.getMessage(message); 
+                    }
+                }
+           );
+        } else {
+            return HttpMessages.getMessage(message);
+        }
+    }
+
+    /**
+     * Send a header.
+     * 
+     * @param name Header name
+     * @param value Header value
+     */
+    public void sendHeader(MessageBytes name, MessageBytes value) {
+
+        write(name);
+        buf[pos++] = Constants.COLON;
+        buf[pos++] = Constants.SP;
+        write(value);
+        buf[pos++] = Constants.CR;
+        buf[pos++] = Constants.LF;
+
+    }
+
+
+    /**
+     * Send a header.
+     * 
+     * @param name Header name
+     * @param value Header value
+     */
+    public void sendHeader(ByteChunk name, ByteChunk value) {
+
+        write(name);
+        buf[pos++] = Constants.COLON;
+        buf[pos++] = Constants.SP;
+        write(value);
+        buf[pos++] = Constants.CR;
+        buf[pos++] = Constants.LF;
+
+    }
+
+
+    /**
+     * Send a header.
+     * 
+     * @param name Header name
+     * @param value Header value
+     */
+    public void sendHeader(String name, String value) {
+
+        write(name);
+        buf[pos++] = Constants.COLON;
+        buf[pos++] = Constants.SP;
+        write(value);
+        buf[pos++] = Constants.CR;
+        buf[pos++] = Constants.LF;
+
+    }
+
+
+    /**
+     * End the header block.
+     */
+    public void endHeaders() {
+
+        buf[pos++] = Constants.CR;
+        buf[pos++] = Constants.LF;
+
+    }
+
+
+    // --------------------------------------------------- OutputBuffer Methods
+
+
+    /**
+     * Write the contents of a byte chunk.
+     * 
+     * @param chunk byte chunk
+     * @return number of bytes written
+     * @throws IOException an undelying I/O error occured
+     */
+    public int doWrite(ByteChunk chunk, Response res) 
+        throws IOException {
+
+        if (!committed) {
+
+            // Send the connector a request for commit. The connector should
+            // then validate the headers, send them (using sendHeaders) and 
+            // set the filters accordingly.
+            response.action(ActionCode.ACTION_COMMIT, null);
+
+        }
+
+        if (lastActiveFilter == -1)
+            return outputStreamOutputBuffer.doWrite(chunk, res);
+        else
+            return activeFilters[lastActiveFilter].doWrite(chunk, res);
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Commit the response.
+     * 
+     * @throws IOException an undelying I/O error occured
+     */
+    protected void commit()
+        throws IOException {
+
+        // The response is now committed
+        committed = true;
+        response.setCommitted(true);
+
+        if (pos > 0) {
+            // Sending the response header buffer
+            if (useSocketBuffer) {
+                socketBuffer.append(buf, 0, pos);
+            } else {
+                outputStream.write(buf, 0, pos);
+            }
+        }
+
+    }
+
+
+    /**
+     * This method will write the contents of the specyfied message bytes 
+     * buffer to the output stream, without filtering. This method is meant to
+     * be used to write the response header.
+     * 
+     * @param mb data to be written
+     */
+    protected void write(MessageBytes mb) {
+
+        if (mb.getType() == MessageBytes.T_BYTES) {
+            ByteChunk bc = mb.getByteChunk();
+            write(bc);
+        } else if (mb.getType() == MessageBytes.T_CHARS) {
+            CharChunk cc = mb.getCharChunk();
+            write(cc);
+        } else {
+            write(mb.toString());
+        }
+
+    }
+
+
+    /**
+     * This method will write the contents of the specyfied message bytes 
+     * buffer to the output stream, without filtering. This method is meant to
+     * be used to write the response header.
+     * 
+     * @param bc data to be written
+     */
+    protected void write(ByteChunk bc) {
+
+        // Writing the byte chunk to the output buffer
+        System.arraycopy(bc.getBytes(), bc.getStart(), buf, pos,
+                         bc.getLength());
+        pos = pos + bc.getLength();
+
+    }
+
+
+    /**
+     * This method will write the contents of the specyfied char 
+     * buffer to the output stream, without filtering. This method is meant to
+     * be used to write the response header.
+     * 
+     * @param cc data to be written
+     */
+    protected void write(CharChunk cc) {
+
+        int start = cc.getStart();
+        int end = cc.getEnd();
+        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 = ' ';
+            }
+            buf[pos++] = (byte) c;
+        }
+
+    }
+
+
+    /**
+     * This method will write the contents of the specyfied byte 
+     * buffer to the output stream, without filtering. This method is meant to
+     * be used to write the response header.
+     * 
+     * @param b data to be written
+     */
+    public void write(byte[] b) {
+
+        // Writing the byte chunk to the output buffer
+        System.arraycopy(b, 0, buf, pos, b.length);
+        pos = pos + b.length;
+
+    }
+
+
+    /**
+     * This method will write the contents of the specyfied String to the 
+     * output stream, without filtering. This method is meant to be used to 
+     * write the response header.
+     * 
+     * @param s data to be written
+     */
+    protected void write(String s) {
+
+        if (s == null)
+            return;
+
+        // From the Tomcat 3.3 HTTP/1.0 connector
+        int len = s.length();
+        for (int i = 0; i < len; i++) {
+            char c = s.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 = ' ';
+            }
+            buf[pos++] = (byte) c;
+        }
+
+    }
+
+
+    /**
+     * This method will print the specified integer to the output stream, 
+     * without filtering. This method is meant to be used to write the 
+     * response header.
+     * 
+     * @param i data to be written
+     */
+    protected void write(int i) {
+
+        write(String.valueOf(i));
+
+    }
+
+
+    /**
+     * Callback to write data from the buffer.
+     */
+    public void realWriteBytes(byte cbuf[], int off, int len)
+        throws IOException {
+        if (len > 0) {
+            outputStream.write(cbuf, off, len);
+        }
+    }
+
+
+    // ----------------------------------- OutputStreamOutputBuffer Inner Class
+
+
+    /**
+     * This class is an output buffer which will write data to an output
+     * stream.
+     */
+    protected class OutputStreamOutputBuffer 
+        implements OutputBuffer {
+
+
+        /**
+         * Write chunk.
+         */
+        public int doWrite(ByteChunk chunk, Response res) 
+            throws IOException {
+
+            if (useSocketBuffer) {
+                socketBuffer.append(chunk.getBuffer(), chunk.getStart(), 
+                                   chunk.getLength());
+            } else {
+                outputStream.write(chunk.getBuffer(), chunk.getStart(), 
+                                   chunk.getLength());
+            }
+            return chunk.getLength();
+
+        }
+
+
+    }
+
+
+}

Added: tomcat/sandbox/java/org/apache/coyote/http11/OutputFilter.java
URL: http://svn.apache.org/viewcvs/tomcat/sandbox/java/org/apache/coyote/http11/OutputFilter.java?rev=345661&view=auto
==============================================================================
--- tomcat/sandbox/java/org/apache/coyote/http11/OutputFilter.java (added)
+++ tomcat/sandbox/java/org/apache/coyote/http11/OutputFilter.java Sat Nov 19 09:48:24 2005
@@ -0,0 +1,82 @@
+/*
+ *  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.http11;
+
+import java.io.IOException;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+
+import org.apache.coyote.OutputBuffer;
+import org.apache.coyote.Response;
+
+/**
+ * Output filter.
+ * 
+ * @author Remy Maucherat
+ */
+public interface OutputFilter extends OutputBuffer {
+
+
+    /**
+     * Write some bytes.
+     * 
+     * @return number of bytes written by the filter
+     */
+    public int doWrite(ByteChunk chunk, Response unused)
+        throws IOException;
+
+
+    /**
+     * Some filters need additional parameters from the response. All the 
+     * necessary reading can occur in that method, as this method is called
+     * after the response header processing is complete.
+     */
+    public void setResponse(Response response);
+
+
+    /**
+     * Make the filter ready to process the next request.
+     */
+    public void recycle();
+
+
+    /**
+     * Get the name of the encoding handled by this filter.
+     */
+    public ByteChunk getEncodingName();
+
+
+    /**
+     * Set the next buffer in the filter pipeline.
+     */
+    public void setBuffer(OutputBuffer buffer);
+
+
+    /**
+     * End the current request. It is acceptable to write extra bytes using
+     * buffer.doWrite during the execution of this method.
+     * 
+     * @return Should return 0 unless the filter does some content length 
+     * delimitation, in which case the number is the amount of extra bytes or
+     * missing bytes, which would indicate an error. 
+     * Note: It is recommended that extra bytes be swallowed by the filter.
+     */
+    public long end()
+        throws IOException;
+
+
+}



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