Return-Path: Mailing-List: contact tomcat-dev-help@jakarta.apache.org; run by ezmlm Delivered-To: mailing list tomcat-dev@jakarta.apache.org Received: (qmail 70732 invoked by uid 500); 17 Nov 2000 03:30:44 -0000 Delivered-To: apmail-jakarta-tomcat-4.0-cvs@apache.org Received: (qmail 70726 invoked by uid 1135); 17 Nov 2000 03:30:44 -0000 Date: 17 Nov 2000 03:30:44 -0000 Message-ID: <20001117033044.70725.qmail@locus.apache.org> From: remm@locus.apache.org To: jakarta-tomcat-4.0-cvs@apache.org Subject: cvs commit: jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http HttpHeader.java HttpRequestLine.java SocketInputStream.java HttpProcessor.java LocalStrings.properties remm 00/11/16 19:30:43 Modified: catalina/src/share/org/apache/catalina/connector/http HttpProcessor.java LocalStrings.properties Added: catalina/src/share/org/apache/catalina/connector/http HttpHeader.java HttpRequestLine.java SocketInputStream.java Log: - Optimized the socket byte reading, which results in a big boost of the connector performance. Overall servlet performance is increased by around 30%, and is on par with the current CVS version of Tomcat 3.3. - Add some recyclable specilized buffer objects for the handling of the request line and the headers. - HttpProcessor currently do not take advantage of these objects, and wrap them into String objects immediately after reading them. Very little changes have been made to the HTTP processor, so far. - The request header parsing is now done inside the socket stream, to get maximum performance. - The new buffer objects will eventually allow to have 0 GC inside the connector. Revision Changes Path 1.12 +57 -76 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http/HttpProcessor.java Index: HttpProcessor.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http/HttpProcessor.java,v retrieving revision 1.11 retrieving revision 1.12 diff -u -r1.11 -r1.12 --- HttpProcessor.java 2000/11/11 01:38:09 1.11 +++ HttpProcessor.java 2000/11/17 03:30:43 1.12 @@ -1,7 +1,7 @@ /* - * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http/HttpProcessor.java,v 1.11 2000/11/11 01:38:09 craigmcc Exp $ - * $Revision: 1.11 $ - * $Date: 2000/11/11 01:38:09 $ + * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http/HttpProcessor.java,v 1.12 2000/11/17 03:30:43 remm Exp $ + * $Revision: 1.12 $ + * $Date: 2000/11/17 03:30:43 $ * * ==================================================================== * @@ -106,7 +106,7 @@ * * @author Craig R. McClanahan * @author Remy Maucherat - * @version $Revision: 1.11 $ $Date: 2000/11/11 01:38:09 $ + * @version $Revision: 1.12 $ $Date: 2000/11/17 03:30:43 $ */ final class HttpProcessor @@ -272,6 +272,24 @@ private static final byte[] CRLF = (new String("\r\n")).getBytes(); + /** + * Line buffer. + */ + private char[] lineBuffer = new char[4096]; + + + /** + * Request line buffer. + */ + private HttpRequestLine requestLine = new HttpRequestLine(); + + + /** + * HTTP header. + */ + private HttpHeader header = new HttpHeader(); + + // -------------------------------------------------------- Package Methods @@ -497,27 +515,33 @@ * @exception IOException if an input/output error occurs * @exception ServletException if a parsing error occurs */ - private void parseHeaders(InputStream input) + private void parseHeaders(SocketInputStream input) throws IOException, ServletException { while (true) { - - // Read the next header line - String line = read(input); - if ((line == null) || (line.length() < 1)) - break; - // Parse the header name and value - int colon = line.indexOf(":"); - if (colon < 0) - throw new ServletException - (sm.getString("httpProcessor.parseHeaders.colon")); - String name = line.substring(0, colon).trim(); - String match = name.toLowerCase(); - String value = line.substring(colon + 1).trim(); + try { + // Read the next header + input.readHeader(header); + } catch (Throwable t) { + t.printStackTrace(); + } + if (header.nameEnd == 0) { + if (header.valueEnd == 0) { + return; + } else { + throw new ServletException + (sm.getString("httpProcessor.parseHeaders.colon")); + } + } + + String name = new String(header.name, 0, header.nameEnd); + String match = name; + String value = new String(header.value, 0, header.valueEnd); + //System.out.println(" Header:" + name + "_ Value:" + value + "_"); if (debug >= 1) log(" Header " + name + " = " + value); - + // Set the corresponding request headers if (match.equals("authorization")) { request.setAuthorization(value); @@ -603,26 +627,20 @@ * @exception IOException if an input/output error occurs * @exception ServletException if a parsing error occurs */ - private void parseRequest(InputStream input, OutputStream output) + private void parseRequest(SocketInputStream input, OutputStream output) throws IOException, ServletException { // Parse the incoming request line - String line = read(input); - if (line == null) - throw new EOFException - (sm.getString("httpProcessor.parseRequest.read")); - parser.setString(line); - - int methodStart = parser.skipWhite(); - int methodEnd = parser.findWhite(); - int uriStart = parser.skipWhite(); - int uriEnd = parser.findWhite(); - int protocolStart = parser.skipWhite(); - int protocolEnd = parser.findWhite(); - - String method = parser.extract(methodStart, methodEnd); - String uri = parser.extract(uriStart, uriEnd); - String protocol = parser.extract(protocolStart, protocolEnd); + input.readRequestLine(requestLine); + String method = + new String(requestLine.method, 0, requestLine.methodEnd); + String uri = new String(requestLine.uri, 0, requestLine.uriEnd); + String protocol = new String(requestLine.protocol, 0, + requestLine.protocolEnd); + + //System.out.println(" Method:" + method + "_ Uri:" + uri + // + "_ Protocol:" + protocol); + if (protocol.length() == 0) protocol = "HTTP/0.9"; @@ -720,13 +738,13 @@ private void process(Socket socket) { boolean ok = true; - InputStream input = null; + SocketInputStream input = null; OutputStream output = null; // Construct and initialize the objects we will need try { - input = new BufferedInputStream(socket.getInputStream(), - connector.getBufferSize()); + input = new SocketInputStream(socket.getInputStream(), + connector.getBufferSize()); } catch (Exception e) { log("process.create", e); ok = false; @@ -844,43 +862,6 @@ ; } socket = null; - - } - - - /** - * Read a line from the specified input stream, and strip off the - * trailing carriage return and newline (if any). Return the remaining - * characters that were read as a String. - * - * @param input The input stream connected to our socket - * - * @return The line that was read, or null if end-of-file - * was encountered - * - * @exception IOException if an input/output error occurs - */ - private String read(InputStream input) throws IOException { - - StringBuffer sb = new StringBuffer(); - while (true) { - int ch = input.read(); - if (ch < 0) { - if (sb.length() == 0) { - return (null); - } else { - break; - } - } else if (ch == '\r') { - continue; - } else if (ch == '\n') { - break; - } - sb.append((char) ch); - } - if (debug >= 2) - log(" Read: " + sb.toString()); - return (sb.toString()); } 1.2 +3 -1 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http/LocalStrings.properties Index: LocalStrings.properties =================================================================== RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http/LocalStrings.properties,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- LocalStrings.properties 2000/08/11 22:40:53 1.1 +++ LocalStrings.properties 2000/11/17 03:30:43 1.2 @@ -23,4 +23,6 @@ httpProcessor.stopping=Stopping background thread requestStream.close.closed=Request stream has already been closed requestStream.read.closed=Unable to read from a closed stream -requestStream.read.error=Unexpected end of stream \ No newline at end of file +requestStream.read.error=Unexpected end of stream +requestStream.readline.error=Couldn't read line +requestStream.readline.toolong=Line too long \ No newline at end of file 1.1 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http/HttpHeader.java Index: HttpHeader.java =================================================================== /* * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http/HttpHeader.java,v 1.1 2000/11/17 03:30:43 remm Exp $ * $Revision: 1.1 $ * $Date: 2000/11/17 03:30:43 $ * * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 1999 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.catalina.connector.http; import java.io.IOException; import java.net.InetAddress; import javax.servlet.ServletInputStream; import org.apache.catalina.connector.HttpRequestBase; /** * HTTP header enum type. * * @author Remy Maucherat * @version $Revision: 1.1 $ $Date: 2000/11/17 03:30:43 $ */ final class HttpHeader { // -------------------------------------------------------------- Constants public static final int INITIAL_NAME_SIZE = 32; public static final int INITIAL_VALUE_SIZE = 64; public static final int MAX_NAME_SIZE = 128; public static final int MAX_VALUE_SIZE = 4096; // ----------------------------------------------------------- Constructors public HttpHeader() { this(new char[INITIAL_NAME_SIZE], 0, new char[INITIAL_VALUE_SIZE], 0); } public HttpHeader(char[] name, int nameEnd, char[] value, int valueEnd) { this.name = name; this.nameEnd = nameEnd; this.value = value; this.valueEnd = valueEnd; } // ----------------------------------------------------- Instance Variables public char[] name; public int nameEnd; public char[] value; public int valueEnd; // ------------------------------------------------------------- Properties // --------------------------------------------------------- Public Methods /** * Release all object references, and initialize instance variables, in * preparation for reuse of this object. */ public void recycle() { nameEnd = 0; valueEnd = 0; } // --------------------------------------------------------- Object Methods public int hashCode() { // FIXME return 0; } public boolean equals(Object obj) { return false; } } 1.1 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http/HttpRequestLine.java Index: HttpRequestLine.java =================================================================== /* * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http/HttpRequestLine.java,v 1.1 2000/11/17 03:30:43 remm Exp $ * $Revision: 1.1 $ * $Date: 2000/11/17 03:30:43 $ * * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 1999 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.catalina.connector.http; import java.io.IOException; import java.net.InetAddress; import javax.servlet.ServletInputStream; import org.apache.catalina.connector.HttpRequestBase; /** * HTTP request line enum type. * * @author Remy Maucherat * @version $Revision: 1.1 $ $Date: 2000/11/17 03:30:43 $ */ final class HttpRequestLine { // -------------------------------------------------------------- Constants public static final int INITIAL_METHOD_SIZE = 8; public static final int INITIAL_URI_SIZE = 64; public static final int INITIAL_PROTOCOL_SIZE = 8; public static final int MAX_METHOD_SIZE = 128; public static final int MAX_URI_SIZE = 4096; public static final int MAX_PROTOCOL_SIZE = 32; // ----------------------------------------------------------- Constructors public HttpRequestLine() { this(new char[INITIAL_METHOD_SIZE], 0, new char[INITIAL_URI_SIZE], 0, new char[INITIAL_URI_SIZE], 0); } public HttpRequestLine(char[] method, int methodEnd, char[] uri, int uriEnd, char[] protocol, int protocolEnd) { this.method = method; this.methodEnd = methodEnd; this.uri = uri; this.uriEnd = uriEnd; this.protocol = protocol; this.protocolEnd = protocolEnd; } // ----------------------------------------------------- Instance Variables public char[] method; public int methodEnd; public char[] uri; public int uriEnd; public char[] protocol; public int protocolEnd; // ------------------------------------------------------------- Properties // --------------------------------------------------------- Public Methods /** * Release all object references, and initialize instance variables, in * preparation for reuse of this object. */ public void recycle() { methodEnd = 0; uriEnd = 0; protocolEnd = 0; } // --------------------------------------------------------- Object Methods public int hashCode() { // FIXME return 0; } public boolean equals(Object obj) { return false; } } 1.1 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http/SocketInputStream.java Index: SocketInputStream.java =================================================================== /* * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http/SocketInputStream.java,v 1.1 2000/11/17 03:30:43 remm Exp $ * $Revision: 1.1 $ * $Date: 2000/11/17 03:30:43 $ * * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 1999 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.catalina.connector.http; import java.io.IOException; import java.io.BufferedInputStream; import java.io.InputStream; import org.apache.catalina.util.StringManager; /** * Extends BufferedInputStream to be more efficient reading lines during HTTP * header processing. * * @author Remy Maucherat */ public class SocketInputStream extends BufferedInputStream { // -------------------------------------------------------------- Constants /** * CR. */ private static final byte CR = (byte) '\r'; /** * LF. */ private static final byte LF = (byte) '\n'; /** * SP. */ private static final byte SP = (byte) ' '; /** * COLON. */ private static final byte COLON = (byte) ':'; /** * Lower case offset. */ private static final int LC_OFFSET = 'A' - 'a'; // ----------------------------------------------------------- Constructors /** * Construct a servlet input stream associated with the specified socket * input. * * @param is socket input stream * @param bufferSize size of the internal buffer */ public SocketInputStream(InputStream is, int bufferSize) { super(is, bufferSize); } // -------------------------------------------------------------- Variables /** * The string manager for this package. */ protected static StringManager sm = StringManager.getManager(Constants.Package); // ----------------------------------------------------- Instance Variables // --------------------------------------------------------- Public Methods /** * Read the request line, and copies it to the given buffer. This * function is meant to be used during the HTTP request header parsing. * Do NOT attempt to read the request body using it. * * @param requestLine Request line object * @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 readRequestLine(HttpRequestLine requestLine) throws IOException { // Recycling check if (requestLine.methodEnd != 0) requestLine.recycle(); // Reading the method name int maxRead = requestLine.method.length; int readStart = pos; int readCount = 0; boolean space = false; while (!space) { // if the buffer is full, extend it if (readCount >= maxRead) { if ((2 * maxRead) <= HttpRequestLine.MAX_METHOD_SIZE) { char[] newBuffer = new char[2 * maxRead]; System.arraycopy(requestLine.method, 0, newBuffer, 0, maxRead); requestLine.method = newBuffer; maxRead = requestLine.method.length; } else { throw new IOException (sm.getString("requestStream.readline.toolong")); } } // We're at the end of the internal buffer if (pos >= count) { int val = read(); if (val == -1) { throw new IOException (sm.getString("requestStream.readline.error")); } pos = 0; readStart = 0; } if (buf[pos] == SP) { space = true; } requestLine.method[readCount] = (char) buf[pos]; readCount++; pos++; } requestLine.methodEnd = readCount - 1; // Reading URI maxRead = requestLine.uri.length; readStart = pos; readCount = 0; space = false; while (!space) { // if the buffer is full, extend it if (readCount >= maxRead) { if ((2 * maxRead) <= HttpRequestLine.MAX_URI_SIZE) { char[] newBuffer = new char[2 * maxRead]; System.arraycopy(requestLine.uri, 0, newBuffer, 0, maxRead); requestLine.uri = newBuffer; maxRead = requestLine.uri.length; } else { throw new IOException (sm.getString("requestStream.readline.toolong")); } } // We're at the end of the internal buffer if (pos >= count) { int val = read(); if (val == -1) throw new IOException (sm.getString("requestStream.readline.error")); pos = 0; readStart = 0; } if (buf[pos] == SP) { space = true; } requestLine.uri[readCount] = (char) buf[pos]; readCount++; pos++; } requestLine.uriEnd = readCount - 1; // Reading protocol maxRead = requestLine.protocol.length; readStart = pos; readCount = 0; int crPos = -2; boolean eol = false; while (!eol) { // if the buffer is full, extend it if (readCount >= maxRead) { if ((2 * maxRead) <= HttpRequestLine.MAX_PROTOCOL_SIZE) { char[] newBuffer = new char[2 * maxRead]; System.arraycopy(requestLine.protocol, 0, newBuffer, 0, maxRead); requestLine.protocol = newBuffer; maxRead = requestLine.protocol.length; } else { throw new IOException (sm.getString("requestStream.readline.toolong")); } } // We're at the end of the internal buffer if (pos >= count) { // Copying part (or all) of the internal buffer to the line // buffer if (pos != readStart) { // Hack to reintialize the internal buffer. We pretend the // first character was never read and we'll reread it from // the buffer. crPos = crPos - count; } int val = read(); if (val == -1) throw new IOException (sm.getString("requestStream.readline.error")); pos = 0; readStart = 0; } if (buf[pos] == CR) { // We found a CR. Next character has to be a LF. crPos = pos; } else if ((buf[pos] == LF) && (crPos == (pos - 1))) { eol = true; } requestLine.protocol[readCount] = (char) buf[pos]; readCount++; pos++; } requestLine.protocolEnd = readCount - 2; } /** * Read a header, and copies it to the given buffer. This * function is meant to be used during the HTTP request header parsing. * Do NOT attempt to read the request body using it. * * @param requestLine Request line object * @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 readHeader(HttpHeader header) throws IOException { // Recycling check if (header.nameEnd != 0) header.recycle(); // Checking for a blank line if (read() == CR) { // Skipping CR read(); // Skipping LF header.nameEnd = 0; header.valueEnd = 0; return; } else { pos--; } // Reading the header name int maxRead = header.name.length; int readStart = pos; int readCount = 0; boolean colon = false; while (!colon) { // if the buffer is full, extend it if (readCount >= maxRead) { if ((2 * maxRead) <= HttpHeader.MAX_NAME_SIZE) { char[] newBuffer = new char[2 * maxRead]; System.arraycopy(header.name, 0, newBuffer, 0, maxRead); header.name = newBuffer; maxRead = header.name.length; } else { throw new IOException (sm.getString("requestStream.readline.toolong")); } } // We're at the end of the internal buffer if (pos >= count) { int val = read(); if (val == -1) { throw new IOException (sm.getString("requestStream.readline.error")); } pos = 0; readStart = 0; } if (buf[pos] == COLON) { colon = true; } char val = (char) buf[pos]; if ((val >= 'A') && (val <= 'Z')) { val = (char) (val - LC_OFFSET); } header.name[readCount] = val; readCount++; pos++; } header.nameEnd = readCount - 1; // Reading the header value (which can be spanned over multiple lines) maxRead = header.value.length; readStart = pos; readCount = 0; int crPos = -2; boolean eol = false; boolean validLine = true; while (validLine) { boolean space = true; // Skipping spaces // Note : Only leading white spaces are removed. Trailing white // spaces are not. while (space) { // We're at the end of the internal buffer if (pos >= count) { // Copying part (or all) of the internal buffer to the line // buffer int val = read(); if (val == -1) throw new IOException (sm.getString("requestStream.readline.error")); pos = 0; readStart = 0; } if (buf[pos] == SP) { pos++; } else { space = false; } } while (!eol) { // if the buffer is full, extend it if (readCount >= maxRead) { if ((2 * maxRead) <= HttpHeader.MAX_VALUE_SIZE) { char[] newBuffer = new char[2 * maxRead]; System.arraycopy(header.value, 0, newBuffer, 0, maxRead); header.value = newBuffer; maxRead = header.value.length; } else { throw new IOException (sm.getString("requestStream.readline.toolong")); } } // We're at the end of the internal buffer if (pos >= count) { // Copying part (or all) of the internal buffer to the line // buffer if (pos != readStart) { // Hack to reintialize the internal buffer. // We pretend the first character was never read and // we'll reread it from the buffer. crPos = crPos - count; } int val = read(); if (val == -1) throw new IOException (sm.getString("requestStream.readline.error")); pos = 0; readStart = 0; } if (buf[pos] == CR) { // We found a CR. Next character has to be a LF. crPos = pos; } else if ((buf[pos] == LF) && (crPos == (pos - 1))) { eol = true; } else { // FIXME : Check if binary conversion is working fine header.value[readCount] = (char) (buf[pos] & 0xff); readCount++; } pos++; } if (read() != SP) { pos--; validLine = false; } else { eol = false; // if the buffer is full, extend it if (readCount >= maxRead) { if ((2 * maxRead) <= HttpHeader.MAX_VALUE_SIZE) { char[] newBuffer = new char[2 * maxRead]; System.arraycopy(header.value, 0, newBuffer, 0, maxRead); header.value = newBuffer; maxRead = header.value.length; } else { throw new IOException (sm.getString("requestStream.readline.toolong")); } } header.value[readCount] = ' '; readCount++; } } header.valueEnd = readCount; } /** * Read a line, and copies it to the given buffer. This function is meant * to be used during the HTTP request header parsing. Do NOT attempt to * read the request body using it. * * @param buffer char buffer * @return the number of bytes read * @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 int readLine(char[] buffer) throws IOException { return readLine(buffer, 0); } /** * Read a line, and copies it to the given buffer. This function is meant * to be used during the HTTP request header parsing. Do NOT attempt to * read the request body using it. Read data must be US-ASCII. * * @param buffer char buffer * @param startPos position in the buffer from which the read character * will be copied * @return the number of bytes read * @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 int readLine(char[] buffer, int startPos) throws IOException { int maxRead = buffer.length - startPos; int readStart = pos; int readCount = 0; int bufferPos = startPos; int crPos = -2; boolean eol = false; while (!eol && (readCount < maxRead)) { // We're at the end of the internal buffer if (pos >= count) { // Copying part (or all) of the internal buffer to the line // buffer if (pos != readStart) { // Hack to reintialize the internal buffer. We pretend the // first character was never read and we'll reread it from // the buffer. crPos = crPos - count; } int val = read(); if (val == -1) throw new IOException (sm.getString("requestStream.readline.error")); pos = 0; readStart = 0; } if (buf[pos] == CR) { // We found a CR. Next character has to be a LF. crPos = pos; } else if ((buf[pos] == LF) && (crPos == (pos - 1))) { eol = true; } buffer[startPos + readCount] = (char) buf[pos]; readCount++; pos++; } if (readCount == maxRead) throw new IOException (sm.getString("requestStream.readline.toolong")); return readCount - 2; } /** * Read a line, and copies it to the given buffer. This function is meant * to be used during the HTTP request header parsing. Do NOT attempt to * read the request body using it. * * @param buffer byte buffer * @return the number of bytes read * @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 int readLine(byte[] buffer) throws IOException { return readLine(buffer, 0); } /** * Read a line, and copies it to the given buffer. This function is meant * to be used during the HTTP request header parsing. Do NOT attempt to * read the request body using it. * * @param buffer byte buffer * @param startPos position in the buffer from which the read character * will be copied * @return the number of bytes read * @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 int readLine(byte[] buffer, int startPos) throws IOException { int maxRead = buffer.length - startPos; int readStart = pos; int readCount = 0; int bufferPos = startPos; int crPos = -2; boolean eol = false; while (!eol && (readCount < maxRead)) { // We're at the end of the internal buffer if (pos >= count) { // Copying part (or all) of the internal buffer to the line // buffer if (pos != readStart) { System.arraycopy(buf, readStart, buffer, bufferPos, pos - readStart); // Hack to reintialize the internal buffer. We pretend the // first character was never read and we'll reread it from // the buffer. crPos = crPos - count; bufferPos += pos - readStart; } int val = read(); if (val == -1) throw new IOException (sm.getString("requestStream.readline.error")); pos = 0; readStart = 0; } if (buf[pos] == CR) { // We found a CR. Next character has to be a LF. crPos = pos; } else if ((buf[pos] == LF) && (crPos == (pos - 1))) { eol = true; // Copy the line from the buffer if ((pos - 1 - readStart) > 0) System.arraycopy (buf, readStart, buffer, bufferPos, pos - 1 - readStart); } readCount++; pos++; } if (readCount == maxRead) throw new IOException (sm.getString("requestStream.readline.toolong")); return readCount - 2; } // ------------------------------------------------------ Protected Methods }