Added: tomcat/tc6.0.x/trunk/java/org/apache/coyote/http11/InternalNioInputBuffer.java URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/coyote/http11/InternalNioInputBuffer.java?rev=416187&view=auto ============================================================================== --- tomcat/tc6.0.x/trunk/java/org/apache/coyote/http11/InternalNioInputBuffer.java (added) +++ tomcat/tc6.0.x/trunk/java/org/apache/coyote/http11/InternalNioInputBuffer.java Wed Jun 21 17:48:53 2006 @@ -0,0 +1,827 @@ +/* + * 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.EOFException; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.SocketChannel; + +import org.apache.coyote.InputBuffer; +import org.apache.coyote.Request; +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; + +/** + * Implementation of InputBuffer which provides HTTP request header parsing as + * well as transfer decoding. + * + * @author <a href="mailto:[EMAIL PROTECTED]">Remy Maucherat</a> + * @author Filip Hanik + */ +public class InternalNioInputBuffer implements InputBuffer { + + + // -------------------------------------------------------------- Constants + + + // ----------------------------------------------------------- Constructors + + + /** + * Alternate constructor. + */ + public InternalNioInputBuffer(Request request, int headerBufferSize, + long readTimeout) { + + this.request = request; + headers = request.getMimeHeaders(); + + buf = new byte[headerBufferSize]; + if (headerBufferSize < (8 * 1024)) { + bbuf = ByteBuffer.allocateDirect(6 * 1500); + } else { + bbuf = ByteBuffer.allocateDirect((headerBufferSize / 1500 + 1) * 1500); + } + + inputStreamInputBuffer = new SocketInputBuffer(); + + filterLibrary = new InputFilter[0]; + activeFilters = new InputFilter[0]; + lastActiveFilter = -1; + + parsingHeader = true; + swallowInput = true; + + if (readTimeout < 0) { + this.readTimeout = -1; + } else { + this.readTimeout = readTimeout; + } + + } + + + // -------------------------------------------------------------- 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; + + + /** + * Last valid byte. + */ + protected int lastValid; + + + /** + * Position in the buffer. + */ + protected int pos; + + + /** + * Pos of the end of the header in the buffer, which is also the + * start of the body. + */ + protected int end; + + + /** + * Direct byte buffer used to perform actual reading. + */ + protected ByteBuffer bbuf; + + + /** + * Underlying socket. + */ + protected SocketChannel 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(SocketChannel socket) { + this.socket = socket; + } + + + /** + * Get the underlying socket input stream. + */ + public SocketChannel 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 = null; + 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() { + + // Recycle Request object + request.recycle(); + + //System.out.println("LV-pos: " + (lastValid - pos)); + // Copy leftover bytes to the beginning of the buffer + if (lastValid - pos > 0) { + int npos = 0; + int opos = pos; + while (lastValid - opos > opos - npos) { + System.arraycopy(buf, opos, buf, npos, opos - npos); + npos += pos; + opos += pos; + } + System.arraycopy(buf, opos, buf, npos, lastValid - opos); + } + + // 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; + } + if (readTimeout == -1) { + if (!fill()) + throw new EOFException(sm.getString("iib.eof.error")); + } else { + // Do a simple read with a short timeout + if ( !readSocket(true) ) return false; + } + } + + 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; + } + if (readTimeout == -1) { + if (!fill()) + throw new EOFException(sm.getString("iib.eof.error")); + } else { + // Do a simple read with a short timeout + if ( !readSocket(true) ) return false; + } + } + + // + // 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")); + } + + if (buf[pos] == Constants.SP) { + space = true; + request.method().setBytes(buf, 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")); + } + + 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().setBytes(buf, start, end - start); + } else { + request.protocol().setString(""); + } + + return true; + + } + + private void expand(int newsize) { + if ( newsize > buf.length ) { + byte[] tmp = new byte[newsize]; + System.arraycopy(buf,0,tmp,0,buf.length); + buf = tmp; + tmp = null; + } + } + /** + * Perform blocking read with a timeout if desired + * @param timeout boolean - set to true if the system will time out + * @return boolean - true if data was read, false is EOF is reached + * @throws IOException + */ + private boolean readSocket(boolean timeout) throws IOException { + int nRead = 0; + long start = System.currentTimeMillis(); + boolean timedOut = false; + do { + bbuf.clear(); + nRead = socket.read(bbuf); + if (nRead > 0) { + bbuf.flip(); + bbuf.limit(nRead); + expand(nRead + pos); + bbuf.get(buf, pos, nRead); + lastValid = pos + nRead; + return true; + } else if (nRead == -1) { + return false; + } + timedOut = (readTimeout != -1) && ((System.currentTimeMillis()-start)>this.readTimeout); + if ( !timedOut && nRead == 0 ) try {Thread.sleep(25);}catch ( Exception x ) {} + }while ( nRead == 0 && (!timedOut) ); + //else throw new IOException(sm.getString("iib.failedread")); + return false; //timeout + } + + + /** + * Parse the HTTP headers. + */ + public void parseHeaders() + throws IOException { + + while (parseHeader()) { + } + + parsingHeader = false; + end = pos; + + } + + + /** + * 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(buf, start, pos - start); + } + chr = buf[pos]; + if ((chr >= Constants.A) && (chr <= Constants.Z)) { + buf[pos] = (byte) (chr - Constants.LC_OFFSET); + } + + 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 { + + boolean read = false; + + if (parsingHeader) { + + if (lastValid == buf.length) { + throw new IOException + (sm.getString("iib.requestheadertoolarge.error")); + } + + // Do a simple read with a short timeout + read = readSocket(true); + } else { + + if (buf.length - end < 4500) { + // In this case, the request header was really large, so we allocate a + // brand new one; the old one will get GCed when subsequent requests + // clear all references + buf = new byte[buf.length]; + end = 0; + } + pos = end; + lastValid = pos; + // Do a simple read with a short timeout + read = readSocket(true); + } + return read; + } + + + // ------------------------------------- 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/tc6.0.x/trunk/java/org/apache/coyote/http11/InternalNioOutputBuffer.java URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/coyote/http11/InternalNioOutputBuffer.java?rev=416187&view=auto ============================================================================== --- tomcat/tc6.0.x/trunk/java/org/apache/coyote/http11/InternalNioOutputBuffer.java (added) +++ tomcat/tc6.0.x/trunk/java/org/apache/coyote/http11/InternalNioOutputBuffer.java Wed Jun 21 17:48:53 2006 @@ -0,0 +1,783 @@ +/* + * 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 java.nio.channels.SocketChannel; + +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; +import java.nio.channels.SelectionKey; +import org.apache.tomcat.util.net.NioEndpoint; +import java.nio.channels.Selector; + +/** + * Output buffer. + * + * @author <a href="mailto:[EMAIL PROTECTED]">Remy Maucherat</a> + * @author Filip Hanik + */ +public class InternalNioOutputBuffer + implements OutputBuffer { + + + // -------------------------------------------------------------- Constants + + + // ----------------------------------------------------------- Constructors + int bbufLimit = 0; + + Selector selector; + + /** + * Default constructor. + */ + public InternalNioOutputBuffer(Response response) { + this(response, Constants.DEFAULT_HTTP_HEADER_BUFFER_SIZE); + } + + + /** + * Alternate constructor. + */ + public InternalNioOutputBuffer(Response response, int headerBufferSize) { + + this.response = response; + headers = response.getMimeHeaders(); + + buf = new byte[headerBufferSize]; + + if (headerBufferSize < (8 * 1024)) { + bbufLimit = 6 * 1500; + } else { + bbufLimit = (headerBufferSize / 1500 + 1) * 1500; + } + bbuf = ByteBuffer.allocateDirect(bbufLimit); + + 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 write buffer. + */ + protected byte[] buf; + + + /** + * Position in the buffer. + */ + protected int pos; + + + /** + * Underlying socket. + */ + protected SocketChannel 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(SocketChannel socket) { + this.socket = socket; + } + + public void setSelector(Selector selector) { + this.selector = selector; + } + + /** + * Get the underlying socket input stream. + */ + public SocketChannel 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 = null; + 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(); + + flushBuffer(); + + finished = true; + + } + + + // ------------------------------------------------ HTTP/1.1 Output Methods + + + /** + * Send an acknoledgement. + */ + public void sendAck() + throws IOException { + + if (!committed) { + //Socket.send(socket, Constants.ACK_BYTES, 0, Constants.ACK_BYTES.length) < 0 + ByteBuffer buf = ByteBuffer.wrap(Constants.ACK_BYTES,0,Constants.ACK_BYTES.length); + writeToSocket(buf); + } + + } + + private void writeToSocket(ByteBuffer bytebuffer) throws IOException { + int limit = bytebuffer.position(); + bytebuffer.rewind(); + bytebuffer.limit(limit); + int remaining = limit; + while ( remaining > 0 ) { + int written = socket.write(bytebuffer); + remaining -= written; + } + bbuf.clear(); + bbuf.rewind(); + bbuf.limit(bbufLimit); + + //System.out.println("Written:"+limit); + this.total = 0; + } + + + /** + * 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 + addToBB(buf, 0, pos); + } + + } + + int total = 0; + private synchronized void addToBB(byte[] buf, int offset, int length) throws IOException { + try { + if (bbuf.capacity() <= (offset + length)) { + flushBuffer(); + } + bbuf.put(buf, offset, length); + total += length; + }catch ( Exception x ) { + x.printStackTrace(); + } + //System.out.println("Total:"+total); + } + + + /** + * 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 { + + //prevent timeout for async, + SelectionKey key = socket.keyFor(selector); + if (key != null) { + NioEndpoint.KeyAttachment attach = (NioEndpoint.KeyAttachment) key.attachment(); + attach.access(); + } + + //write to the socket, if there is anything to write + if (bbuf.position() > 0) { + writeToSocket(bbuf); + } + } + + + // ----------------------------------- 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 { + + int len = chunk.getLength(); + int start = chunk.getStart(); + byte[] b = chunk.getBuffer(); + while (len > 0) { + int thisTime = len; + if (bbuf.position() == bbuf.capacity()) { + flushBuffer(); + } + if (thisTime > bbuf.capacity() - bbuf.position()) { + thisTime = bbuf.capacity() - bbuf.position(); + } + addToBB(b,start,thisTime); + len = len - thisTime; + start = start + thisTime; + } + return chunk.getLength(); + + } + + + } + + +} --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]