From 327d71a0dbfe3d287d22b82591e2af9bbe6be3d9 Mon Sep 17 00:00:00 2001 From: fhanik Date: Fri, 9 Oct 2009 16:24:43 +0000 Subject: [PATCH] Abstract out code for output buffer, much easier to read git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@823608 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/coyote/http11/AbstractOutputBuffer.java | 566 +++++++++++++++++++++ .../apache/coyote/http11/Http11NioProcessor.java | 12 - java/org/apache/coyote/http11/Http11Processor.java | 9 +- .../coyote/http11/InternalNioOutputBuffer.java | 507 +----------------- .../apache/coyote/http11/InternalOutputBuffer.java | 514 +------------------ 5 files changed, 594 insertions(+), 1014 deletions(-) create mode 100644 java/org/apache/coyote/http11/AbstractOutputBuffer.java diff --git a/java/org/apache/coyote/http11/AbstractOutputBuffer.java b/java/org/apache/coyote/http11/AbstractOutputBuffer.java new file mode 100644 index 000000000..73ad1f203 --- /dev/null +++ b/java/org/apache/coyote/http11/AbstractOutputBuffer.java @@ -0,0 +1,566 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.security.AccessController; +import java.security.PrivilegedAction; + +import org.apache.coyote.ActionCode; +import org.apache.coyote.OutputBuffer; +import org.apache.coyote.Response; +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; + +public abstract class AbstractOutputBuffer implements OutputBuffer{ + + // ----------------------------------------------------- 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; + + + /** + * The buffer used for header composition. + */ + protected byte[] buf; + + + /** + * Position in the buffer. + */ + protected int pos; + + + + /** + * 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; + + /** + * Underlying output buffer. + */ + protected OutputBuffer outputStreamOutputBuffer; + + // -------------------------------------------------------------- Variables + + + /** + * The string manager for this package. + */ + protected static StringManager sm = + StringManager.getManager(Constants.Package); + + // ------------------------------------------------------------- Properties + + + + + + /** + * 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); + + } + + + + + // --------------------------------------------------- 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); + + } + + // --------------------------------------------------------- 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); + + } + } + + /** + * 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 filters + for (int i = 0; i <= lastActiveFilter; i++) { + activeFilters[i].recycle(); + } + // Recycle Request object + response.recycle(); + 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(); + // 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(); + finished = true; + + } + + public abstract void sendAck() throws IOException; + + protected abstract void commit() throws IOException; + + + /** + * 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 = null; + if (org.apache.coyote.Constants.USE_CUSTOM_STATUS_MSG_IN_HEADER) { + message = response.getMessage(); + } + if (message == null) { + write(HttpMessages.getMessage(status)); + } else { + write(message.replace('\n', ' ').replace('\r', ' ')); + } + + // End the response status line + if (org.apache.coyote.Constants.IS_SECURITY_ENABLED){ + AccessController.doPrivileged( + new PrivilegedAction(){ + public Void run(){ + buf[pos++] = Constants.CR; + buf[pos++] = Constants.LF; + return null; + } + } + ); + } else { + 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; + + } + + + /** + * 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 + int length = bc.getLength(); + System.arraycopy(bc.getBytes(), bc.getStart(), buf, pos, length); + pos = pos + length; + + } + + + /** + * 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)); + + } + + + +} diff --git a/java/org/apache/coyote/http11/Http11NioProcessor.java b/java/org/apache/coyote/http11/Http11NioProcessor.java index 27b520672..bc9d89896 100644 --- a/java/org/apache/coyote/http11/Http11NioProcessor.java +++ b/java/org/apache/coyote/http11/Http11NioProcessor.java @@ -66,18 +66,6 @@ public class Http11NioProcessor extends AbstractHttp11Processor implements Actio /** - * Logger. - */ - protected static org.apache.juli.logging.Log log - = org.apache.juli.logging.LogFactory.getLog(Http11NioProcessor.class); - - /** - * The string manager for this package. - */ - protected static StringManager sm = - StringManager.getManager(Constants.Package); - - /** * SSL information. */ protected SSLSupport sslSupport; diff --git a/java/org/apache/coyote/http11/Http11Processor.java b/java/org/apache/coyote/http11/Http11Processor.java index 1a2331fd1..a69304e67 100644 --- a/java/org/apache/coyote/http11/Http11Processor.java +++ b/java/org/apache/coyote/http11/Http11Processor.java @@ -56,14 +56,7 @@ import org.apache.tomcat.util.net.SocketWrapper; */ public class Http11Processor extends AbstractHttp11Processor implements ActionHook { - - /** - * Logger. - */ - protected static org.apache.juli.logging.Log log - = org.apache.juli.logging.LogFactory.getLog(Http11Processor.class); - - // ------------------------------------------------------------ Constructor + // ------------------------------------------------------------ Constructor public Http11Processor(int headerBufferSize, JIoEndpoint endpoint) { diff --git a/java/org/apache/coyote/http11/InternalNioOutputBuffer.java b/java/org/apache/coyote/http11/InternalNioOutputBuffer.java index e8a5bba18..ac49a0d1d 100644 --- a/java/org/apache/coyote/http11/InternalNioOutputBuffer.java +++ b/java/org/apache/coyote/http11/InternalNioOutputBuffer.java @@ -22,19 +22,14 @@ import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; -import org.apache.coyote.ActionCode; import org.apache.coyote.OutputBuffer; import org.apache.coyote.Response; +import org.apache.tomcat.util.MutableInteger; 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.net.NioChannel; import org.apache.tomcat.util.net.NioEndpoint; import org.apache.tomcat.util.net.NioSelectorPool; -import org.apache.tomcat.util.res.StringManager; -import org.apache.tomcat.util.MutableInteger; /** * Output buffer. @@ -42,8 +37,7 @@ import org.apache.tomcat.util.MutableInteger; * @author Remy Maucherat * @author Filip Hanik */ -public class InternalNioOutputBuffer - implements OutputBuffer { +public class InternalNioOutputBuffer extends AbstractOutputBuffer { // -------------------------------------------------------------- Constants @@ -93,54 +87,6 @@ public class InternalNioOutputBuffer } - // -------------------------------------------------------------- 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 write buffer. - */ - protected byte[] buf; - - - /** - * Position in the buffer. - */ - protected int pos; - /** * Number of bytes last written */ @@ -158,29 +104,6 @@ public class InternalNioOutputBuffer - /** - * 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; // ------------------------------------------------------------- Properties @@ -207,67 +130,6 @@ public class InternalNioOutputBuffer return pool; } - /** - * 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 @@ -275,19 +137,12 @@ public class InternalNioOutputBuffer * 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); - - } + @Override + public void flush() throws IOException { + super.flush(); // Flush the current buffer flushBuffer(); @@ -295,66 +150,15 @@ public class InternalNioOutputBuffer /** - * 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. */ + @Override public void recycle() { - // Recycle filters - for (int i = 0; i <= lastActiveFilter; i++) { - activeFilters[i].recycle(); - } - - // Recycle Request object - response.recycle(); + super.recycle(); socket.getBufHandler().getWriteBuffer().clear(); - socket = null; - pos = 0; - lastActiveFilter = -1; - committed = false; - finished = false; lastWrite.set(1); - - } - - - /** - * 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(); - - // Recycle filters - for (int i = 0; i <= lastActiveFilter; i++) { - activeFilters[i].recycle(); - } - - // Reset pointers - pos = 0; - lastActiveFilter = -1; - committed = false; - finished = false; - } @@ -363,28 +167,10 @@ public class InternalNioOutputBuffer * * @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(); - + @Override + public void endRequest() throws IOException { + super.endRequest(); flushBuffer(); - - finished = true; - } public boolean isWritable() { @@ -396,8 +182,7 @@ public class InternalNioOutputBuffer /** * Send an acknoledgement. */ - public void sendAck() - throws IOException { + public void sendAck() throws IOException { if (!committed) { //Socket.send(socket, Constants.ACK_BYTES, 0, Constants.ACK_BYTES.length) < 0 @@ -443,145 +228,6 @@ public class InternalNioOutputBuffer } - /** - * 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 = null; - if (org.apache.coyote.Constants.USE_CUSTOM_STATUS_MSG_IN_HEADER) { - message = response.getMessage(); - } - if (message == null) { - write(HttpMessages.getMessage(status)); - } else { - write(message.replace('\n', ' ').replace('\r', ' ')); - } - - // 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 @@ -628,135 +274,6 @@ public class InternalNioOutputBuffer /** - * 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 - int length = bc.getLength(); - System.arraycopy(bc.getBytes(), bc.getStart(), buf, pos, length); - pos = pos + length; - - } - - - /** - * 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() diff --git a/java/org/apache/coyote/http11/InternalOutputBuffer.java b/java/org/apache/coyote/http11/InternalOutputBuffer.java index 310f01a5e..4c1e4bf8c 100644 --- a/java/org/apache/coyote/http11/InternalOutputBuffer.java +++ b/java/org/apache/coyote/http11/InternalOutputBuffer.java @@ -19,27 +19,18 @@ 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; +import org.apache.tomcat.util.buf.ByteChunk; /** * Output buffer. * * @author Remy Maucherat */ -public class InternalOutputBuffer - implements OutputBuffer, ByteChunk.ByteOutputChannel { +public class InternalOutputBuffer extends AbstractOutputBuffer + implements ByteChunk.ByteOutputChannel { // -------------------------------------------------------------- Constants @@ -80,56 +71,6 @@ public class InternalOutputBuffer } - - // -------------------------------------------------------------- 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; - - - /** - * The buffer used for header composition. - */ - protected byte[] buf; - - - /** - * Position in the buffer. - */ - protected int pos; - - /** * Underlying output stream. */ @@ -137,31 +78,6 @@ public class InternalOutputBuffer /** - * 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; @@ -170,12 +86,8 @@ public class InternalOutputBuffer /** * Socket buffer (extra buffering to reduce number of packets sent). */ - protected boolean useSocketBuffer = false; - - - // ------------------------------------------------------------- Properties - - + protected boolean useSocketBuffer = false; + /** * Set the underlying socket output stream. */ @@ -211,68 +123,9 @@ public class InternalOutputBuffer } } + - /** - * 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 @@ -280,20 +133,13 @@ public class InternalOutputBuffer /** * Flush the response. * - * @throws IOException an undelying I/O error occured + * @throws IOException an underlying I/O error occurred */ 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); - - } - + super.flush(); + // Flush the current buffer if (useSocketBuffer) { socketBuffer.flushBuffer(); @@ -302,20 +148,7 @@ public class InternalOutputBuffer } - /** - * 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(); - - } + /** @@ -323,17 +156,9 @@ public class InternalOutputBuffer * connection. */ public void recycle() { - - // Recycle Request object - response.recycle(); + super.recycle(); socketBuffer.recycle(); - outputStream = null; - pos = 0; - lastActiveFilter = -1; - committed = false; - finished = false; - } @@ -344,54 +169,22 @@ public class InternalOutputBuffer * to parse the next HTTP request. */ public void nextRequest() { - - // Recycle Request object - response.recycle(); + super.nextRequest(); socketBuffer.recycle(); - - // 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 + * @throws IOException an underlying I/O error occurred */ 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(); - + super.endRequest(); if (useSocketBuffer) { socketBuffer.flushBuffer(); } - - finished = true; - } @@ -410,156 +203,6 @@ public class InternalOutputBuffer } - /** - * 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 = null; - if (org.apache.coyote.Constants.USE_CUSTOM_STATUS_MSG_IN_HEADER) { - message = response.getMessage(); - } - if (message == null) { - write(HttpMessages.getMessage(status)); - } else { - write(message.replace('\n', ' ').replace('\r', ' ')); - } - - // End the response status line - if (org.apache.coyote.Constants.IS_SECURITY_ENABLED){ - AccessController.doPrivileged( - new PrivilegedAction(){ - public Void run(){ - buf[pos++] = Constants.CR; - buf[pos++] = Constants.LF; - return null; - } - } - ); - } else { - 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 @@ -588,134 +231,7 @@ public class InternalOutputBuffer } - /** - * 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 - int length = bc.getLength(); - System.arraycopy(bc.getBytes(), bc.getStart(), buf, pos, length); - pos = pos + length; - - } - - - /** - * 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. -- 2.11.0