Added: tomcat/sandbox/java/org/apache/tomcat/util/net/http11/InternalInputBuffer.java URL: http://svn.apache.org/viewvc/tomcat/sandbox/java/org/apache/tomcat/util/net/http11/InternalInputBuffer.java?rev=409042&view=auto ============================================================================== --- tomcat/sandbox/java/org/apache/tomcat/util/net/http11/InternalInputBuffer.java (added) +++ tomcat/sandbox/java/org/apache/tomcat/util/net/http11/InternalInputBuffer.java Tue May 23 19:52:29 2006 @@ -0,0 +1,795 @@ +/* + * 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.tomcat.util.net.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; +import org.apache.coyote.http11.Constants; +import org.apache.coyote.http11.InputFilter; + +/** + * Implementation of InputBuffer which provides HTTP request header parsing as + * well as transfer decoding. + * + * @author <a href="mailto:[EMAIL PROTECTED]">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/tomcat/util/net/http11/InternalOutputBuffer.java URL: http://svn.apache.org/viewvc/tomcat/sandbox/java/org/apache/tomcat/util/net/http11/InternalOutputBuffer.java?rev=409042&view=auto ============================================================================== --- tomcat/sandbox/java/org/apache/tomcat/util/net/http11/InternalOutputBuffer.java (added) +++ tomcat/sandbox/java/org/apache/tomcat/util/net/http11/InternalOutputBuffer.java Tue May 23 19:52:29 2006 @@ -0,0 +1,785 @@ +/* + * 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.tomcat.util.net.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; +import org.apache.coyote.http11.Constants; +import org.apache.coyote.http11.OutputFilter; + +/** + * Output buffer. + * + * @author <a href="mailto:[EMAIL PROTECTED]">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(); + + } + + + } + + +} --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]