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