You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by re...@apache.org on 2014/04/17 18:10:14 UTC

svn commit: r1588299 - in /tomcat/trunk: java/org/apache/coyote/http11/AbstractNioInputBuffer.java java/org/apache/coyote/http11/InternalNio2InputBuffer.java java/org/apache/coyote/http11/InternalNioInputBuffer.java webapps/docs/changelog.xml

Author: remm
Date: Thu Apr 17 16:10:14 2014
New Revision: 1588299

URL: http://svn.apache.org/r1588299
Log:
- The input buffer is the place where there was code duplication between the two NIO connectors. Resolve that, there should now be no meaningful code duplication elsewhere.
- Note that the NIO2 code is the one shared (some additional "1" state for the parsing state - should be transparent for NIO - and use flush(boolean) in the shared code).
- Testsuite for NIO and NIO 2 looks ok.

Added:
    tomcat/trunk/java/org/apache/coyote/http11/AbstractNioInputBuffer.java
Modified:
    tomcat/trunk/java/org/apache/coyote/http11/InternalNio2InputBuffer.java
    tomcat/trunk/java/org/apache/coyote/http11/InternalNioInputBuffer.java
    tomcat/trunk/webapps/docs/changelog.xml

Added: tomcat/trunk/java/org/apache/coyote/http11/AbstractNioInputBuffer.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/AbstractNioInputBuffer.java?rev=1588299&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http11/AbstractNioInputBuffer.java (added)
+++ tomcat/trunk/java/org/apache/coyote/http11/AbstractNioInputBuffer.java Thu Apr 17 16:10:14 2014
@@ -0,0 +1,660 @@
+/*
+ *  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.nio.charset.StandardCharsets;
+
+import org.apache.coyote.Request;
+import org.apache.tomcat.util.buf.MessageBytes;
+
+public abstract class AbstractNioInputBuffer<S> extends AbstractInputBuffer<S> {
+
+    // -------------------------------------------------------------- Constants
+
+    enum HeaderParseStatus {
+        DONE, HAVE_MORE_HEADERS, NEED_MORE_DATA
+    }
+
+    enum HeaderParsePosition {
+        /**
+         * Start of a new header. A CRLF here means that there are no more
+         * headers. Any other character starts a header name.
+         */
+        HEADER_START,
+        /**
+         * Reading a header name. All characters of header are HTTP_TOKEN_CHAR.
+         * Header name is followed by ':'. No whitespace is allowed.<br />
+         * Any non-HTTP_TOKEN_CHAR (this includes any whitespace) encountered
+         * before ':' will result in the whole line being ignored.
+         */
+        HEADER_NAME,
+        /**
+         * Skipping whitespace before text of header value starts, either on the
+         * first line of header value (just after ':') or on subsequent lines
+         * when it is known that subsequent line starts with SP or HT.
+         */
+        HEADER_VALUE_START,
+        /**
+         * Reading the header value. We are inside the value. Either on the
+         * first line or on any subsequent line. We come into this state from
+         * HEADER_VALUE_START after the first non-SP/non-HT byte is encountered
+         * on the line.
+         */
+        HEADER_VALUE,
+        /**
+         * Before reading a new line of a header. Once the next byte is peeked,
+         * the state changes without advancing our position. The state becomes
+         * either HEADER_VALUE_START (if that first byte is SP or HT), or
+         * HEADER_START (otherwise).
+         */
+        HEADER_MULTI_LINE,
+        /**
+         * Reading all bytes until the next CRLF. The line is being ignored.
+         */
+        HEADER_SKIPLINE
+    }
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Alternate constructor.
+     */
+    public AbstractNioInputBuffer(Request request, int headerBufferSize) {
+
+        this.request = request;
+        headers = request.getMimeHeaders();
+
+        this.headerBufferSize = headerBufferSize;
+
+        filterLibrary = new InputFilter[0];
+        activeFilters = new InputFilter[0];
+        lastActiveFilter = -1;
+
+        parsingHeader = true;
+        parsingRequestLine = true;
+        parsingRequestLinePhase = 0;
+        parsingRequestLineEol = false;
+        parsingRequestLineStart = 0;
+        parsingRequestLineQPos = -1;
+        headerParsePos = HeaderParsePosition.HEADER_START;
+        headerData.recycle();
+        swallowInput = true;
+
+    }
+
+    /**
+     * Parsing state - used for non blocking parsing so that
+     * when more data arrives, we can pick up where we left off.
+     */
+    private boolean parsingRequestLine;
+    private int parsingRequestLinePhase = 0;
+    private boolean parsingRequestLineEol = false;
+    private int parsingRequestLineStart = 0;
+    private int parsingRequestLineQPos = -1;
+    private HeaderParsePosition headerParsePos;
+
+    /**
+     * Maximum allowed size of the HTTP request line plus headers plus any
+     * leading blank lines.
+     */
+    protected final int headerBufferSize;
+
+    /**
+     * Known size of the NioChannel read buffer.
+     */
+    protected int socketReadBufferSize;
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Recycle the input buffer. This should be called when closing the
+     * connection.
+     */
+    @Override
+    public void recycle() {
+        super.recycle();
+        headerParsePos = HeaderParsePosition.HEADER_START;
+        parsingRequestLine = true;
+        parsingRequestLinePhase = 0;
+        parsingRequestLineEol = false;
+        parsingRequestLineStart = 0;
+        parsingRequestLineQPos = -1;
+        headerData.recycle();
+    }
+
+
+    /**
+     * 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.
+     */
+    @Override
+    public void nextRequest() {
+        super.nextRequest();
+        headerParsePos = HeaderParsePosition.HEADER_START;
+        parsingRequestLine = true;
+        parsingRequestLinePhase = 0;
+        parsingRequestLineEol = false;
+        parsingRequestLineStart = 0;
+        parsingRequestLineQPos = -1;
+        headerData.recycle();
+    }
+
+    /**
+     * 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 accommodate
+     * the whole line.
+     * @return true if data is properly fed; false if no data is available
+     * immediately and thread should be freed
+     */
+    @Override
+    public boolean parseRequestLine(boolean useAvailableDataOnly)
+        throws IOException {
+
+        //check state
+        if ( !parsingRequestLine ) return true;
+        //
+        // Skipping blank lines
+        //
+        if ( parsingRequestLinePhase < 2 ) {
+            byte chr = 0;
+            do {
+
+                // Read new bytes if needed
+                if (pos >= lastValid) {
+                    if (useAvailableDataOnly) {
+                        return false;
+                    }
+                    // Do a simple read with a short timeout
+                    if (!fill(false)) {
+                        // A read is pending, so no longer in initial state
+                        parsingRequestLinePhase = 1;
+                        return false;
+                    }
+                }
+                chr = buf[pos++];
+            } while ((chr == Constants.CR) || (chr == Constants.LF));
+            pos--;
+
+            parsingRequestLineStart = pos;
+            parsingRequestLinePhase = 2;
+            if (getLog().isDebugEnabled()) {
+                getLog().debug("Received ["
+                        + new String(buf, pos, lastValid - pos,
+                                StandardCharsets.ISO_8859_1)
+                        + "]");
+            }
+        }
+        if ( parsingRequestLinePhase == 2 ) {
+            //
+            // 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(false)) //request line parsing
+                        return false;
+                }
+                // Spec says no CR or LF in method name
+                if (buf[pos] == Constants.CR || buf[pos] == Constants.LF) {
+                    throw new IllegalArgumentException(
+                            sm.getString("iib.invalidmethod"));
+                }
+                if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
+                    space = true;
+                    request.method().setBytes(buf, parsingRequestLineStart, pos - parsingRequestLineStart);
+                }
+                pos++;
+            }
+            parsingRequestLinePhase = 3;
+        }
+        if ( parsingRequestLinePhase == 3 ) {
+            // Spec says single SP but also be tolerant of multiple and/or HT
+            boolean space = true;
+            while (space) {
+                // Read new bytes if needed
+                if (pos >= lastValid) {
+                    if (!fill(false)) //request line parsing
+                        return false;
+                }
+                if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
+                    pos++;
+                } else {
+                    space = false;
+                }
+            }
+            parsingRequestLineStart = pos;
+            parsingRequestLinePhase = 4;
+        }
+        if (parsingRequestLinePhase == 4) {
+            // Mark the current buffer position
+
+            int end = 0;
+            //
+            // Reading the URI
+            //
+            boolean space = false;
+            while (!space) {
+                // Read new bytes if needed
+                if (pos >= lastValid) {
+                    if (!fill(false)) //request line parsing
+                        return false;
+                }
+                if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
+                    space = true;
+                    end = pos;
+                } else if ((buf[pos] == Constants.CR)
+                           || (buf[pos] == Constants.LF)) {
+                    // HTTP/0.9 style request
+                    parsingRequestLineEol = true;
+                    space = true;
+                    end = pos;
+                } else if ((buf[pos] == Constants.QUESTION)
+                           && (parsingRequestLineQPos == -1)) {
+                    parsingRequestLineQPos = pos;
+                }
+                pos++;
+            }
+            request.unparsedURI().setBytes(buf, parsingRequestLineStart, end - parsingRequestLineStart);
+            if (parsingRequestLineQPos >= 0) {
+                request.queryString().setBytes(buf, parsingRequestLineQPos + 1,
+                                               end - parsingRequestLineQPos - 1);
+                request.requestURI().setBytes(buf, parsingRequestLineStart, parsingRequestLineQPos - parsingRequestLineStart);
+            } else {
+                request.requestURI().setBytes(buf, parsingRequestLineStart, end - parsingRequestLineStart);
+            }
+            parsingRequestLinePhase = 5;
+        }
+        if ( parsingRequestLinePhase == 5 ) {
+            // Spec says single SP but also be tolerant of multiple and/or HT
+            boolean space = true;
+            while (space) {
+                // Read new bytes if needed
+                if (pos >= lastValid) {
+                    if (!fill(false)) //request line parsing
+                        return false;
+                }
+                if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
+                    pos++;
+                } else {
+                    space = false;
+                }
+            }
+            parsingRequestLineStart = pos;
+            parsingRequestLinePhase = 6;
+
+            // Mark the current buffer position
+            end = 0;
+        }
+        if (parsingRequestLinePhase == 6) {
+            //
+            // Reading the protocol
+            // Protocol is always US-ASCII
+            //
+            while (!parsingRequestLineEol) {
+                // Read new bytes if needed
+                if (pos >= lastValid) {
+                    if (!fill(false)) //request line parsing
+                        return false;
+                }
+
+                if (buf[pos] == Constants.CR) {
+                    end = pos;
+                } else if (buf[pos] == Constants.LF) {
+                    if (end == 0)
+                        end = pos;
+                    parsingRequestLineEol = true;
+                }
+                pos++;
+            }
+
+            if ( (end - parsingRequestLineStart) > 0) {
+                request.protocol().setBytes(buf, parsingRequestLineStart, end - parsingRequestLineStart);
+            } else {
+                request.protocol().setString("");
+            }
+            parsingRequestLine = false;
+            parsingRequestLinePhase = 0;
+            parsingRequestLineEol = false;
+            parsingRequestLineStart = 0;
+            return true;
+        }
+        throw new IllegalStateException("Invalid request line parse phase:"+parsingRequestLinePhase);
+    }
+
+    protected void expand(int newsize) {
+        if ( newsize > buf.length ) {
+            if (parsingHeader) {
+                throw new IllegalArgumentException(
+                        sm.getString("iib.requestheadertoolarge.error"));
+            }
+            // Should not happen
+            getLog().warn("Expanding buffer size. Old size: " + buf.length
+                    + ", new size: " + newsize, new Exception());
+            byte[] tmp = new byte[newsize];
+            System.arraycopy(buf,0,tmp,0,buf.length);
+            buf = tmp;
+        }
+    }
+
+    /**
+     * Parse the HTTP headers.
+     */
+    @Override
+    public boolean parseHeaders()
+        throws IOException {
+        if (!parsingHeader) {
+            throw new IllegalStateException(
+                    sm.getString("iib.parseheaders.ise.error"));
+        }
+
+        HeaderParseStatus status = HeaderParseStatus.HAVE_MORE_HEADERS;
+
+        do {
+            status = parseHeader();
+            // Checking that
+            // (1) Headers plus request line size does not exceed its limit
+            // (2) There are enough bytes to avoid expanding the buffer when
+            // reading body
+            // Technically, (2) is technical limitation, (1) is logical
+            // limitation to enforce the meaning of headerBufferSize
+            // From the way how buf is allocated and how blank lines are being
+            // read, it should be enough to check (1) only.
+            if (pos > headerBufferSize
+                    || buf.length - pos < socketReadBufferSize) {
+                throw new IllegalArgumentException(
+                        sm.getString("iib.requestheadertoolarge.error"));
+            }
+        } while ( status == HeaderParseStatus.HAVE_MORE_HEADERS );
+        if (status == HeaderParseStatus.DONE) {
+            parsingHeader = false;
+            end = pos;
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Parse an HTTP header.
+     *
+     * @return false after reading a blank line (which indicates that the
+     * HTTP header parsing is done
+     */
+    private HeaderParseStatus parseHeader()
+        throws IOException {
+
+        //
+        // Check for blank line
+        //
+
+        byte chr = 0;
+        while (headerParsePos == HeaderParsePosition.HEADER_START) {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill(false)) {//parse header
+                    headerParsePos = HeaderParsePosition.HEADER_START;
+                    return HeaderParseStatus.NEED_MORE_DATA;
+                }
+            }
+
+            chr = buf[pos];
+
+            if (chr == Constants.CR) {
+                // Skip
+            } else if (chr == Constants.LF) {
+                pos++;
+                return HeaderParseStatus.DONE;
+            } else {
+                break;
+            }
+
+            pos++;
+
+        }
+
+        if ( headerParsePos == HeaderParsePosition.HEADER_START ) {
+            // Mark the current buffer position
+            headerData.start = pos;
+            headerParsePos = HeaderParsePosition.HEADER_NAME;
+        }
+
+        //
+        // Reading the header name
+        // Header name is always US-ASCII
+        //
+
+        while (headerParsePos == HeaderParsePosition.HEADER_NAME) {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill(false)) { //parse header
+                    return HeaderParseStatus.NEED_MORE_DATA;
+                }
+            }
+
+            chr = buf[pos];
+            if (chr == Constants.COLON) {
+                headerParsePos = HeaderParsePosition.HEADER_VALUE_START;
+                headerData.headerValue = headers.addValue(buf, headerData.start, pos - headerData.start);
+                pos++;
+                // Mark the current buffer position
+                headerData.start = pos;
+                headerData.realPos = pos;
+                headerData.lastSignificantChar = pos;
+                break;
+            } else if (!HTTP_TOKEN_CHAR[chr]) {
+                // If a non-token header is detected, skip the line and
+                // ignore the header
+                headerData.lastSignificantChar = pos;
+                return skipLine();
+            }
+
+            // chr is next byte of header name. Convert to lowercase.
+            if ((chr >= Constants.A) && (chr <= Constants.Z)) {
+                buf[pos] = (byte) (chr - Constants.LC_OFFSET);
+            }
+            pos++;
+        }
+
+        // Skip the line and ignore the header
+        if (headerParsePos == HeaderParsePosition.HEADER_SKIPLINE) {
+            return skipLine();
+        }
+
+        //
+        // Reading the header value (which can be spanned over multiple lines)
+        //
+
+        while (headerParsePos == HeaderParsePosition.HEADER_VALUE_START ||
+               headerParsePos == HeaderParsePosition.HEADER_VALUE ||
+               headerParsePos == HeaderParsePosition.HEADER_MULTI_LINE) {
+
+            if ( headerParsePos == HeaderParsePosition.HEADER_VALUE_START ) {
+                // Skipping spaces
+                while (true) {
+                    // Read new bytes if needed
+                    if (pos >= lastValid) {
+                        if (!fill(false)) {//parse header
+                            //HEADER_VALUE_START
+                            return HeaderParseStatus.NEED_MORE_DATA;
+                        }
+                    }
+
+                    chr = buf[pos];
+                    if (chr == Constants.SP || chr == Constants.HT) {
+                        pos++;
+                    } else {
+                        headerParsePos = HeaderParsePosition.HEADER_VALUE;
+                        break;
+                    }
+                }
+            }
+            if ( headerParsePos == HeaderParsePosition.HEADER_VALUE ) {
+
+                // Reading bytes until the end of the line
+                boolean eol = false;
+                while (!eol) {
+
+                    // Read new bytes if needed
+                    if (pos >= lastValid) {
+                        if (!fill(false)) {//parse header
+                            //HEADER_VALUE
+                            return HeaderParseStatus.NEED_MORE_DATA;
+                        }
+                    }
+
+                    chr = buf[pos];
+                    if (chr == Constants.CR) {
+                        // Skip
+                    } else if (chr == Constants.LF) {
+                        eol = true;
+                    } else if (chr == Constants.SP || chr == Constants.HT) {
+                        buf[headerData.realPos] = chr;
+                        headerData.realPos++;
+                    } else {
+                        buf[headerData.realPos] = chr;
+                        headerData.realPos++;
+                        headerData.lastSignificantChar = headerData.realPos;
+                    }
+
+                    pos++;
+                }
+
+                // Ignore whitespaces at the end of the line
+                headerData.realPos = headerData.lastSignificantChar;
+
+                // Checking the first character of the new line. If the character
+                // is a LWS, then it's a multiline header
+                headerParsePos = HeaderParsePosition.HEADER_MULTI_LINE;
+            }
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill(false)) {//parse header
+                    //HEADER_MULTI_LINE
+                    return HeaderParseStatus.NEED_MORE_DATA;
+                }
+            }
+
+            chr = buf[pos];
+            if ( headerParsePos == HeaderParsePosition.HEADER_MULTI_LINE ) {
+                if ( (chr != Constants.SP) && (chr != Constants.HT)) {
+                    headerParsePos = HeaderParsePosition.HEADER_START;
+                    break;
+                } else {
+                    // Copying one extra space in the buffer (since there must
+                    // be at least one space inserted between the lines)
+                    buf[headerData.realPos] = chr;
+                    headerData.realPos++;
+                    headerParsePos = HeaderParsePosition.HEADER_VALUE_START;
+                }
+            }
+        }
+        // Set the header value
+        headerData.headerValue.setBytes(buf, headerData.start,
+                headerData.lastSignificantChar - headerData.start);
+        headerData.recycle();
+        return HeaderParseStatus.HAVE_MORE_HEADERS;
+    }
+
+    public int getParsingRequestLinePhase() {
+        return parsingRequestLinePhase;
+    }
+
+    private HeaderParseStatus skipLine() throws IOException {
+        headerParsePos = HeaderParsePosition.HEADER_SKIPLINE;
+        boolean eol = false;
+
+        // Reading bytes until the end of the line
+        while (!eol) {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill(false)) {
+                    return HeaderParseStatus.NEED_MORE_DATA;
+                }
+            }
+
+            if (buf[pos] == Constants.CR) {
+                // Skip
+            } else if (buf[pos] == Constants.LF) {
+                eol = true;
+            } else {
+                headerData.lastSignificantChar = pos;
+            }
+
+            pos++;
+        }
+        if (getLog().isDebugEnabled()) {
+            getLog().debug(sm.getString("iib.invalidheader", new String(buf,
+                    headerData.start,
+                    headerData.lastSignificantChar - headerData.start + 1,
+                    StandardCharsets.ISO_8859_1)));
+        }
+
+        headerParsePos = HeaderParsePosition.HEADER_START;
+        return HeaderParseStatus.HAVE_MORE_HEADERS;
+    }
+
+    private final HeaderParseData headerData = new HeaderParseData();
+    public static class HeaderParseData {
+        /**
+         * When parsing header name: first character of the header.<br />
+         * When skipping broken header line: first character of the header.<br />
+         * When parsing header value: first character after ':'.
+         */
+        int start = 0;
+        /**
+         * When parsing header name: not used (stays as 0).<br />
+         * When skipping broken header line: not used (stays as 0).<br />
+         * When parsing header value: starts as the first character after ':'.
+         * Then is increased as far as more bytes of the header are harvested.
+         * Bytes from buf[pos] are copied to buf[realPos]. Thus the string from
+         * [start] to [realPos-1] is the prepared value of the header, with
+         * whitespaces removed as needed.<br />
+         */
+        int realPos = 0;
+        /**
+         * When parsing header name: not used (stays as 0).<br />
+         * When skipping broken header line: last non-CR/non-LF character.<br />
+         * When parsing header value: position after the last not-LWS character.<br />
+         */
+        int lastSignificantChar = 0;
+        /**
+         * MB that will store the value of the header. It is null while parsing
+         * header name and is created after the name has been parsed.
+         */
+        MessageBytes headerValue = null;
+        public void recycle() {
+            start = 0;
+            realPos = 0;
+            lastSignificantChar = 0;
+            headerValue = null;
+        }
+    }
+
+}

Modified: tomcat/trunk/java/org/apache/coyote/http11/InternalNio2InputBuffer.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/InternalNio2InputBuffer.java?rev=1588299&r1=1588298&r2=1588299&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http11/InternalNio2InputBuffer.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http11/InternalNio2InputBuffer.java Thu Apr 17 16:10:14 2014
@@ -22,7 +22,6 @@ import java.net.SocketTimeoutException;
 import java.nio.ByteBuffer;
 import java.nio.channels.CompletionHandler;
 import java.nio.channels.ReadPendingException;
-import java.nio.charset.StandardCharsets;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
@@ -34,7 +33,6 @@ import org.apache.coyote.Request;
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.util.buf.ByteChunk;
-import org.apache.tomcat.util.buf.MessageBytes;
 import org.apache.tomcat.util.net.AbstractEndpoint;
 import org.apache.tomcat.util.net.Nio2Channel;
 import org.apache.tomcat.util.net.Nio2Endpoint;
@@ -44,115 +42,25 @@ import org.apache.tomcat.util.net.Socket
 /**
  * Output buffer implementation for NIO2.
  */
-public class InternalNio2InputBuffer extends AbstractInputBuffer<Nio2Channel> {
+public class InternalNio2InputBuffer extends AbstractNioInputBuffer<Nio2Channel> {
 
     private static final Log log =
             LogFactory.getLog(InternalNio2InputBuffer.class);
 
-    // -------------------------------------------------------------- Constants
-
-    enum HeaderParseStatus {
-        DONE, HAVE_MORE_HEADERS, NEED_MORE_DATA
-    }
-
-    enum HeaderParsePosition {
-        /**
-         * Start of a new header. A CRLF here means that there are no more
-         * headers. Any other character starts a header name.
-         */
-        HEADER_START,
-        /**
-         * Reading a header name. All characters of header are HTTP_TOKEN_CHAR.
-         * Header name is followed by ':'. No whitespace is allowed.<br />
-         * Any non-HTTP_TOKEN_CHAR (this includes any whitespace) encountered
-         * before ':' will result in the whole line being ignored.
-         */
-        HEADER_NAME,
-        /**
-         * Skipping whitespace before text of header value starts, either on the
-         * first line of header value (just after ':') or on subsequent lines
-         * when it is known that subsequent line starts with SP or HT.
-         */
-        HEADER_VALUE_START,
-        /**
-         * Reading the header value. We are inside the value. Either on the
-         * first line or on any subsequent line. We come into this state from
-         * HEADER_VALUE_START after the first non-SP/non-HT byte is encountered
-         * on the line.
-         */
-        HEADER_VALUE,
-        /**
-         * Before reading a new line of a header. Once the next byte is peeked,
-         * the state changes without advancing our position. The state becomes
-         * either HEADER_VALUE_START (if that first byte is SP or HT), or
-         * HEADER_START (otherwise).
-         */
-        HEADER_MULTI_LINE,
-        /**
-         * Reading all bytes until the next CRLF. The line is being ignored.
-         */
-        HEADER_SKIPLINE
-    }
-
     // ----------------------------------------------------------- Constructors
 
 
-    /**
-     * Alternate constructor.
-     */
     public InternalNio2InputBuffer(Request request, int headerBufferSize) {
-
-        this.request = request;
-        headers = request.getMimeHeaders();
-
-        this.headerBufferSize = headerBufferSize;
-
+        super(request, headerBufferSize);
         inputStreamInputBuffer = new SocketInputBuffer();
-
-        filterLibrary = new InputFilter[0];
-        activeFilters = new InputFilter[0];
-        lastActiveFilter = -1;
-
-        parsingHeader = true;
-        parsingRequestLine = true;
-        parsingRequestLinePhase = 0;
-        parsingRequestLineEol = false;
-        parsingRequestLineStart = 0;
-        parsingRequestLineQPos = -1;
-        headerParsePos = HeaderParsePosition.HEADER_START;
-        headerData.recycle();
-        swallowInput = true;
-
     }
 
     /**
-     * Parsing state - used for non blocking parsing so that
-     * when more data arrives, we can pick up where we left off.
-     */
-    private boolean parsingRequestLine;
-    private int parsingRequestLinePhase = 0;
-    private boolean parsingRequestLineEol = false;
-    private int parsingRequestLineStart = 0;
-    private int parsingRequestLineQPos = -1;
-    private HeaderParsePosition headerParsePos;
-
-    /**
      * Underlying socket.
      */
     private SocketWrapper<Nio2Channel> socket;
 
     /**
-     * Maximum allowed size of the HTTP request line plus headers plus any
-     * leading blank lines.
-     */
-    private final int headerBufferSize;
-
-    /**
-     * Known size of the NioChannel read buffer.
-     */
-    private int socketReadBufferSize;
-
-    /**
      * Track write interest
      */
     protected volatile boolean interest = false;
@@ -198,13 +106,6 @@ public class InternalNio2InputBuffer ext
     public void recycle() {
         super.recycle();
         socket = null;
-        headerParsePos = HeaderParsePosition.HEADER_START;
-        parsingRequestLine = true;
-        parsingRequestLinePhase = 0;
-        parsingRequestLineEol = false;
-        parsingRequestLineStart = 0;
-        parsingRequestLineQPos = -1;
-        headerData.recycle();
         readPending = false;
         flipped = false;
         interest = false;
@@ -221,518 +122,9 @@ public class InternalNio2InputBuffer ext
     @Override
     public void nextRequest() {
         super.nextRequest();
-        headerParsePos = HeaderParsePosition.HEADER_START;
-        parsingRequestLine = true;
-        parsingRequestLinePhase = 0;
-        parsingRequestLineEol = false;
-        parsingRequestLineStart = 0;
-        parsingRequestLineQPos = -1;
-        headerData.recycle();
         interest = false;
     }
 
-    /**
-     * 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 accommodate
-     * the whole line.
-     * @return true if data is properly fed; false if no data is available
-     * immediately and thread should be freed
-     */
-    @Override
-    public boolean parseRequestLine(boolean useAvailableDataOnly)
-        throws IOException {
-
-        //check state
-        if ( !parsingRequestLine ) return true;
-        //
-        // Skipping blank lines
-        //
-        if ( parsingRequestLinePhase < 2 ) {
-            byte chr = 0;
-            do {
-
-                // Read new bytes if needed
-                if (pos >= lastValid) {
-                    if (useAvailableDataOnly) {
-                        return false;
-                    }
-                    // Do a simple read with a short timeout
-                    if (!fill(false)) {
-                        // A read is pending, so no longer in initial state
-                        parsingRequestLinePhase = 1;
-                        return false;
-                    }
-                }
-                chr = buf[pos++];
-            } while ((chr == Constants.CR) || (chr == Constants.LF));
-            pos--;
-
-            parsingRequestLineStart = pos;
-            parsingRequestLinePhase = 2;
-            if (log.isDebugEnabled()) {
-                log.debug("Received ["
-                        + new String(buf, pos, lastValid - pos,
-                                StandardCharsets.ISO_8859_1)
-                        + "]");
-            }
-        }
-        if ( parsingRequestLinePhase == 2 ) {
-            //
-            // 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(false)) //request line parsing
-                        return false;
-                }
-                // Spec says no CR or LF in method name
-                if (buf[pos] == Constants.CR || buf[pos] == Constants.LF) {
-                    throw new IllegalArgumentException(
-                            sm.getString("iib.invalidmethod"));
-                }
-                if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
-                    space = true;
-                    request.method().setBytes(buf, parsingRequestLineStart, pos - parsingRequestLineStart);
-                }
-                pos++;
-            }
-            parsingRequestLinePhase = 3;
-        }
-        if ( parsingRequestLinePhase == 3 ) {
-            // Spec says single SP but also be tolerant of multiple and/or HT
-            boolean space = true;
-            while (space) {
-                // Read new bytes if needed
-                if (pos >= lastValid) {
-                    if (!fill(false)) //request line parsing
-                        return false;
-                }
-                if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
-                    pos++;
-                } else {
-                    space = false;
-                }
-            }
-            parsingRequestLineStart = pos;
-            parsingRequestLinePhase = 4;
-        }
-        if (parsingRequestLinePhase == 4) {
-            // Mark the current buffer position
-
-            int end = 0;
-            //
-            // Reading the URI
-            //
-            boolean space = false;
-            while (!space) {
-                // Read new bytes if needed
-                if (pos >= lastValid) {
-                    if (!fill(false)) //request line parsing
-                        return false;
-                }
-                if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
-                    space = true;
-                    end = pos;
-                } else if ((buf[pos] == Constants.CR)
-                           || (buf[pos] == Constants.LF)) {
-                    // HTTP/0.9 style request
-                    parsingRequestLineEol = true;
-                    space = true;
-                    end = pos;
-                } else if ((buf[pos] == Constants.QUESTION)
-                           && (parsingRequestLineQPos == -1)) {
-                    parsingRequestLineQPos = pos;
-                }
-                pos++;
-            }
-            request.unparsedURI().setBytes(buf, parsingRequestLineStart, end - parsingRequestLineStart);
-            if (parsingRequestLineQPos >= 0) {
-                request.queryString().setBytes(buf, parsingRequestLineQPos + 1,
-                                               end - parsingRequestLineQPos - 1);
-                request.requestURI().setBytes(buf, parsingRequestLineStart, parsingRequestLineQPos - parsingRequestLineStart);
-            } else {
-                request.requestURI().setBytes(buf, parsingRequestLineStart, end - parsingRequestLineStart);
-            }
-            parsingRequestLinePhase = 5;
-        }
-        if ( parsingRequestLinePhase == 5 ) {
-            // Spec says single SP but also be tolerant of multiple and/or HT
-            boolean space = true;
-            while (space) {
-                // Read new bytes if needed
-                if (pos >= lastValid) {
-                    if (!fill(false)) //request line parsing
-                        return false;
-                }
-                if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
-                    pos++;
-                } else {
-                    space = false;
-                }
-            }
-            parsingRequestLineStart = pos;
-            parsingRequestLinePhase = 6;
-
-            // Mark the current buffer position
-            end = 0;
-        }
-        if (parsingRequestLinePhase == 6) {
-            //
-            // Reading the protocol
-            // Protocol is always US-ASCII
-            //
-            while (!parsingRequestLineEol) {
-                // Read new bytes if needed
-                if (pos >= lastValid) {
-                    if (!fill(false)) //request line parsing
-                        return false;
-                }
-
-                if (buf[pos] == Constants.CR) {
-                    end = pos;
-                } else if (buf[pos] == Constants.LF) {
-                    if (end == 0)
-                        end = pos;
-                    parsingRequestLineEol = true;
-                }
-                pos++;
-            }
-
-            if ( (end - parsingRequestLineStart) > 0) {
-                request.protocol().setBytes(buf, parsingRequestLineStart, end - parsingRequestLineStart);
-            } else {
-                request.protocol().setString("");
-            }
-            parsingRequestLine = false;
-            parsingRequestLinePhase = 0;
-            parsingRequestLineEol = false;
-            parsingRequestLineStart = 0;
-            return true;
-        }
-        throw new IllegalStateException("Invalid request line parse phase:"+parsingRequestLinePhase);
-    }
-
-    private void expand(int newsize) {
-        if ( newsize > buf.length ) {
-            if (parsingHeader) {
-                throw new IllegalArgumentException(
-                        sm.getString("iib.requestheadertoolarge.error"));
-            }
-            // Should not happen
-            log.warn("Expanding buffer size. Old size: " + buf.length
-                    + ", new size: " + newsize, new Exception());
-            byte[] tmp = new byte[newsize];
-            System.arraycopy(buf,0,tmp,0,buf.length);
-            buf = tmp;
-        }
-    }
-
-    /**
-     * Parse the HTTP headers.
-     */
-    @Override
-    public boolean parseHeaders()
-        throws IOException {
-        if (!parsingHeader) {
-            throw new IllegalStateException(
-                    sm.getString("iib.parseheaders.ise.error"));
-        }
-
-        HeaderParseStatus status = HeaderParseStatus.HAVE_MORE_HEADERS;
-
-        do {
-            status = parseHeader();
-            // Checking that
-            // (1) Headers plus request line size does not exceed its limit
-            // (2) There are enough bytes to avoid expanding the buffer when
-            // reading body
-            // Technically, (2) is technical limitation, (1) is logical
-            // limitation to enforce the meaning of headerBufferSize
-            // From the way how buf is allocated and how blank lines are being
-            // read, it should be enough to check (1) only.
-            if (pos > headerBufferSize
-                    || buf.length - pos < socketReadBufferSize) {
-                throw new IllegalArgumentException(
-                        sm.getString("iib.requestheadertoolarge.error"));
-            }
-        } while ( status == HeaderParseStatus.HAVE_MORE_HEADERS );
-        if (status == HeaderParseStatus.DONE) {
-            parsingHeader = false;
-            end = pos;
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-
-    /**
-     * Parse an HTTP header.
-     *
-     * @return false after reading a blank line (which indicates that the
-     * HTTP header parsing is done
-     */
-    private HeaderParseStatus parseHeader()
-        throws IOException {
-
-        //
-        // Check for blank line
-        //
-
-        byte chr = 0;
-        while (headerParsePos == HeaderParsePosition.HEADER_START) {
-
-            // Read new bytes if needed
-            if (pos >= lastValid) {
-                if (!fill(false)) {//parse header
-                    headerParsePos = HeaderParsePosition.HEADER_START;
-                    return HeaderParseStatus.NEED_MORE_DATA;
-                }
-            }
-
-            chr = buf[pos];
-
-            if (chr == Constants.CR) {
-                // Skip
-            } else if (chr == Constants.LF) {
-                pos++;
-                return HeaderParseStatus.DONE;
-            } else {
-                break;
-            }
-
-            pos++;
-
-        }
-
-        if ( headerParsePos == HeaderParsePosition.HEADER_START ) {
-            // Mark the current buffer position
-            headerData.start = pos;
-            headerParsePos = HeaderParsePosition.HEADER_NAME;
-        }
-
-        //
-        // Reading the header name
-        // Header name is always US-ASCII
-        //
-
-        while (headerParsePos == HeaderParsePosition.HEADER_NAME) {
-
-            // Read new bytes if needed
-            if (pos >= lastValid) {
-                if (!fill(false)) { //parse header
-                    return HeaderParseStatus.NEED_MORE_DATA;
-                }
-            }
-
-            chr = buf[pos];
-            if (chr == Constants.COLON) {
-                headerParsePos = HeaderParsePosition.HEADER_VALUE_START;
-                headerData.headerValue = headers.addValue(buf, headerData.start, pos - headerData.start);
-                pos++;
-                // Mark the current buffer position
-                headerData.start = pos;
-                headerData.realPos = pos;
-                headerData.lastSignificantChar = pos;
-                break;
-            } else if (!HTTP_TOKEN_CHAR[chr]) {
-                // If a non-token header is detected, skip the line and
-                // ignore the header
-                headerData.lastSignificantChar = pos;
-                return skipLine();
-            }
-
-            // chr is next byte of header name. Convert to lowercase.
-            if ((chr >= Constants.A) && (chr <= Constants.Z)) {
-                buf[pos] = (byte) (chr - Constants.LC_OFFSET);
-            }
-            pos++;
-        }
-
-        // Skip the line and ignore the header
-        if (headerParsePos == HeaderParsePosition.HEADER_SKIPLINE) {
-            return skipLine();
-        }
-
-        //
-        // Reading the header value (which can be spanned over multiple lines)
-        //
-
-        while (headerParsePos == HeaderParsePosition.HEADER_VALUE_START ||
-               headerParsePos == HeaderParsePosition.HEADER_VALUE ||
-               headerParsePos == HeaderParsePosition.HEADER_MULTI_LINE) {
-
-            if ( headerParsePos == HeaderParsePosition.HEADER_VALUE_START ) {
-                // Skipping spaces
-                while (true) {
-                    // Read new bytes if needed
-                    if (pos >= lastValid) {
-                        if (!fill(false)) {//parse header
-                            //HEADER_VALUE_START
-                            return HeaderParseStatus.NEED_MORE_DATA;
-                        }
-                    }
-
-                    chr = buf[pos];
-                    if (chr == Constants.SP || chr == Constants.HT) {
-                        pos++;
-                    } else {
-                        headerParsePos = HeaderParsePosition.HEADER_VALUE;
-                        break;
-                    }
-                }
-            }
-            if ( headerParsePos == HeaderParsePosition.HEADER_VALUE ) {
-
-                // Reading bytes until the end of the line
-                boolean eol = false;
-                while (!eol) {
-
-                    // Read new bytes if needed
-                    if (pos >= lastValid) {
-                        if (!fill(false)) {//parse header
-                            //HEADER_VALUE
-                            return HeaderParseStatus.NEED_MORE_DATA;
-                        }
-                    }
-
-                    chr = buf[pos];
-                    if (chr == Constants.CR) {
-                        // Skip
-                    } else if (chr == Constants.LF) {
-                        eol = true;
-                    } else if (chr == Constants.SP || chr == Constants.HT) {
-                        buf[headerData.realPos] = chr;
-                        headerData.realPos++;
-                    } else {
-                        buf[headerData.realPos] = chr;
-                        headerData.realPos++;
-                        headerData.lastSignificantChar = headerData.realPos;
-                    }
-
-                    pos++;
-                }
-
-                // Ignore whitespaces at the end of the line
-                headerData.realPos = headerData.lastSignificantChar;
-
-                // Checking the first character of the new line. If the character
-                // is a LWS, then it's a multiline header
-                headerParsePos = HeaderParsePosition.HEADER_MULTI_LINE;
-            }
-            // Read new bytes if needed
-            if (pos >= lastValid) {
-                if (!fill(false)) {//parse header
-                    //HEADER_MULTI_LINE
-                    return HeaderParseStatus.NEED_MORE_DATA;
-                }
-            }
-
-            chr = buf[pos];
-            if ( headerParsePos == HeaderParsePosition.HEADER_MULTI_LINE ) {
-                if ( (chr != Constants.SP) && (chr != Constants.HT)) {
-                    headerParsePos = HeaderParsePosition.HEADER_START;
-                    break;
-                } else {
-                    // Copying one extra space in the buffer (since there must
-                    // be at least one space inserted between the lines)
-                    buf[headerData.realPos] = chr;
-                    headerData.realPos++;
-                    headerParsePos = HeaderParsePosition.HEADER_VALUE_START;
-                }
-            }
-        }
-        // Set the header value
-        headerData.headerValue.setBytes(buf, headerData.start,
-                headerData.lastSignificantChar - headerData.start);
-        headerData.recycle();
-        return HeaderParseStatus.HAVE_MORE_HEADERS;
-    }
-
-    public int getParsingRequestLinePhase() {
-        return parsingRequestLinePhase;
-    }
-
-    private HeaderParseStatus skipLine() throws IOException {
-        headerParsePos = HeaderParsePosition.HEADER_SKIPLINE;
-        boolean eol = false;
-
-        // Reading bytes until the end of the line
-        while (!eol) {
-
-            // Read new bytes if needed
-            if (pos >= lastValid) {
-                if (!fill(false)) {
-                    return HeaderParseStatus.NEED_MORE_DATA;
-                }
-            }
-
-            if (buf[pos] == Constants.CR) {
-                // Skip
-            } else if (buf[pos] == Constants.LF) {
-                eol = true;
-            } else {
-                headerData.lastSignificantChar = pos;
-            }
-
-            pos++;
-        }
-        if (log.isDebugEnabled()) {
-            log.debug(sm.getString("iib.invalidheader", new String(buf,
-                    headerData.start,
-                    headerData.lastSignificantChar - headerData.start + 1,
-                    StandardCharsets.ISO_8859_1)));
-        }
-
-        headerParsePos = HeaderParsePosition.HEADER_START;
-        return HeaderParseStatus.HAVE_MORE_HEADERS;
-    }
-
-    private final HeaderParseData headerData = new HeaderParseData();
-    public static class HeaderParseData {
-        /**
-         * When parsing header name: first character of the header.<br />
-         * When skipping broken header line: first character of the header.<br />
-         * When parsing header value: first character after ':'.
-         */
-        int start = 0;
-        /**
-         * When parsing header name: not used (stays as 0).<br />
-         * When skipping broken header line: not used (stays as 0).<br />
-         * When parsing header value: starts as the first character after ':'.
-         * Then is increased as far as more bytes of the header are harvested.
-         * Bytes from buf[pos] are copied to buf[realPos]. Thus the string from
-         * [start] to [realPos-1] is the prepared value of the header, with
-         * whitespaces removed as needed.<br />
-         */
-        int realPos = 0;
-        /**
-         * When parsing header name: not used (stays as 0).<br />
-         * When skipping broken header line: last non-CR/non-LF character.<br />
-         * When parsing header value: position after the last not-LWS character.<br />
-         */
-        int lastSignificantChar = 0;
-        /**
-         * MB that will store the value of the header. It is null while parsing
-         * header name and is created after the name has been parsed.
-         */
-        MessageBytes headerValue = null;
-        public void recycle() {
-            start = 0;
-            realPos = 0;
-            lastSignificantChar = 0;
-            headerValue = null;
-        }
-    }
-
-
     // ------------------------------------------------------ Protected Methods
 
     @Override

Modified: tomcat/trunk/java/org/apache/coyote/http11/InternalNioInputBuffer.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/InternalNioInputBuffer.java?rev=1588299&r1=1588298&r2=1588299&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http11/InternalNioInputBuffer.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http11/InternalNioInputBuffer.java Thu Apr 17 16:10:14 2014
@@ -19,14 +19,12 @@ package org.apache.coyote.http11;
 import java.io.EOFException;
 import java.io.IOException;
 import java.nio.channels.Selector;
-import java.nio.charset.StandardCharsets;
 
 import org.apache.coyote.InputBuffer;
 import org.apache.coyote.Request;
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.util.buf.ByteChunk;
-import org.apache.tomcat.util.buf.MessageBytes;
 import org.apache.tomcat.util.net.AbstractEndpoint;
 import org.apache.tomcat.util.net.NioChannel;
 import org.apache.tomcat.util.net.NioEndpoint;
@@ -39,56 +37,11 @@ import org.apache.tomcat.util.net.Socket
  *
  * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
  */
-public class InternalNioInputBuffer extends AbstractInputBuffer<NioChannel> {
+public class InternalNioInputBuffer extends AbstractNioInputBuffer<NioChannel> {
 
     private static final Log log =
             LogFactory.getLog(InternalNioInputBuffer.class);
 
-    // -------------------------------------------------------------- Constants
-
-    enum HeaderParseStatus {
-        DONE, HAVE_MORE_HEADERS, NEED_MORE_DATA
-    }
-
-    enum HeaderParsePosition {
-        /**
-         * Start of a new header. A CRLF here means that there are no more
-         * headers. Any other character starts a header name.
-         */
-        HEADER_START,
-        /**
-         * Reading a header name. All characters of header are HTTP_TOKEN_CHAR.
-         * Header name is followed by ':'. No whitespace is allowed.<br />
-         * Any non-HTTP_TOKEN_CHAR (this includes any whitespace) encountered
-         * before ':' will result in the whole line being ignored.
-         */
-        HEADER_NAME,
-        /**
-         * Skipping whitespace before text of header value starts, either on the
-         * first line of header value (just after ':') or on subsequent lines
-         * when it is known that subsequent line starts with SP or HT.
-         */
-        HEADER_VALUE_START,
-        /**
-         * Reading the header value. We are inside the value. Either on the
-         * first line or on any subsequent line. We come into this state from
-         * HEADER_VALUE_START after the first non-SP/non-HT byte is encountered
-         * on the line.
-         */
-        HEADER_VALUE,
-        /**
-         * Before reading a new line of a header. Once the next byte is peeked,
-         * the state changes without advancing our position. The state becomes
-         * either HEADER_VALUE_START (if that first byte is SP or HT), or
-         * HEADER_START (otherwise).
-         */
-        HEADER_MULTI_LINE,
-        /**
-         * Reading all bytes until the next CRLF. The line is being ignored.
-         */
-        HEADER_SKIPLINE
-    }
-
     // ----------------------------------------------------------- Constructors
 
 
@@ -96,42 +49,11 @@ public class InternalNioInputBuffer exte
      * Alternate constructor.
      */
     public InternalNioInputBuffer(Request request, int headerBufferSize) {
-
-        this.request = request;
-        headers = request.getMimeHeaders();
-
-        this.headerBufferSize = headerBufferSize;
-
+        super(request, headerBufferSize);
         inputStreamInputBuffer = new SocketInputBuffer();
-
-        filterLibrary = new InputFilter[0];
-        activeFilters = new InputFilter[0];
-        lastActiveFilter = -1;
-
-        parsingHeader = true;
-        parsingRequestLine = true;
-        parsingRequestLinePhase = 0;
-        parsingRequestLineEol = false;
-        parsingRequestLineStart = 0;
-        parsingRequestLineQPos = -1;
-        headerParsePos = HeaderParsePosition.HEADER_START;
-        headerData.recycle();
-        swallowInput = true;
-
     }
 
     /**
-     * Parsing state - used for non blocking parsing so that
-     * when more data arrives, we can pick up where we left off.
-     */
-    private boolean parsingRequestLine;
-    private int parsingRequestLinePhase = 0;
-    private boolean parsingRequestLineEol = false;
-    private int parsingRequestLineStart = 0;
-    private int parsingRequestLineQPos = -1;
-    private HeaderParsePosition headerParsePos;
-
-    /**
      * Underlying socket.
      */
     private NioChannel socket;
@@ -142,18 +64,6 @@ public class InternalNioInputBuffer exte
     private NioSelectorPool pool;
 
 
-    /**
-     * Maximum allowed size of the HTTP request line plus headers plus any
-     * leading blank lines.
-     */
-    private final int headerBufferSize;
-
-    /**
-     * Known size of the NioChannel read buffer.
-     */
-    private int socketReadBufferSize;
-
-
     // --------------------------------------------------------- Public Methods
 
     @Override
@@ -170,236 +80,10 @@ public class InternalNioInputBuffer exte
     public void recycle() {
         super.recycle();
         socket = null;
-        headerParsePos = HeaderParsePosition.HEADER_START;
-        parsingRequestLine = true;
-        parsingRequestLinePhase = 0;
-        parsingRequestLineEol = false;
-        parsingRequestLineStart = 0;
-        parsingRequestLineQPos = -1;
-        headerData.recycle();
     }
 
 
     /**
-     * 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.
-     */
-    @Override
-    public void nextRequest() {
-        super.nextRequest();
-        headerParsePos = HeaderParsePosition.HEADER_START;
-        parsingRequestLine = true;
-        parsingRequestLinePhase = 0;
-        parsingRequestLineEol = false;
-        parsingRequestLineStart = 0;
-        parsingRequestLineQPos = -1;
-        headerData.recycle();
-    }
-
-    /**
-     * 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 accommodate
-     * the whole line.
-     * @return true if data is properly fed; false if no data is available
-     * immediately and thread should be freed
-     */
-    @Override
-    public boolean parseRequestLine(boolean useAvailableDataOnly)
-        throws IOException {
-
-        //check state
-        if ( !parsingRequestLine ) return true;
-        //
-        // Skipping blank lines
-        //
-        if ( parsingRequestLinePhase == 0 ) {
-            byte chr = 0;
-            do {
-
-                // Read new bytes if needed
-                if (pos >= lastValid) {
-                    if (useAvailableDataOnly) {
-                        return false;
-                    }
-                    // Do a simple read with a short timeout
-                    if (!fill(true, false)) {
-                        return false;
-                    }
-                }
-                chr = buf[pos++];
-            } while ((chr == Constants.CR) || (chr == Constants.LF));
-            pos--;
-
-            parsingRequestLineStart = pos;
-            parsingRequestLinePhase = 2;
-            if (log.isDebugEnabled()) {
-                log.debug("Received ["
-                        + new String(buf, pos, lastValid - pos,
-                                StandardCharsets.ISO_8859_1)
-                        + "]");
-            }
-        }
-        if ( parsingRequestLinePhase == 2 ) {
-            //
-            // 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(true, false)) //request line parsing
-                        return false;
-                }
-                // Spec says no CR or LF in method name
-                if (buf[pos] == Constants.CR || buf[pos] == Constants.LF) {
-                    throw new IllegalArgumentException(
-                            sm.getString("iib.invalidmethod"));
-                }
-                if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
-                    space = true;
-                    request.method().setBytes(buf, parsingRequestLineStart, pos - parsingRequestLineStart);
-                }
-                pos++;
-            }
-            parsingRequestLinePhase = 3;
-        }
-        if ( parsingRequestLinePhase == 3 ) {
-            // Spec says single SP but also be tolerant of multiple and/or HT
-            boolean space = true;
-            while (space) {
-                // Read new bytes if needed
-                if (pos >= lastValid) {
-                    if (!fill(true, false)) //request line parsing
-                        return false;
-                }
-                if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
-                    pos++;
-                } else {
-                    space = false;
-                }
-            }
-            parsingRequestLineStart = pos;
-            parsingRequestLinePhase = 4;
-        }
-        if (parsingRequestLinePhase == 4) {
-            // Mark the current buffer position
-
-            int end = 0;
-            //
-            // Reading the URI
-            //
-            boolean space = false;
-            while (!space) {
-                // Read new bytes if needed
-                if (pos >= lastValid) {
-                    if (!fill(true,false)) //request line parsing
-                        return false;
-                }
-                if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
-                    space = true;
-                    end = pos;
-                } else if ((buf[pos] == Constants.CR)
-                           || (buf[pos] == Constants.LF)) {
-                    // HTTP/0.9 style request
-                    parsingRequestLineEol = true;
-                    space = true;
-                    end = pos;
-                } else if ((buf[pos] == Constants.QUESTION)
-                           && (parsingRequestLineQPos == -1)) {
-                    parsingRequestLineQPos = pos;
-                }
-                pos++;
-            }
-            request.unparsedURI().setBytes(buf, parsingRequestLineStart, end - parsingRequestLineStart);
-            if (parsingRequestLineQPos >= 0) {
-                request.queryString().setBytes(buf, parsingRequestLineQPos + 1,
-                                               end - parsingRequestLineQPos - 1);
-                request.requestURI().setBytes(buf, parsingRequestLineStart, parsingRequestLineQPos - parsingRequestLineStart);
-            } else {
-                request.requestURI().setBytes(buf, parsingRequestLineStart, end - parsingRequestLineStart);
-            }
-            parsingRequestLinePhase = 5;
-        }
-        if ( parsingRequestLinePhase == 5 ) {
-            // Spec says single SP but also be tolerant of multiple and/or HT
-            boolean space = true;
-            while (space) {
-                // Read new bytes if needed
-                if (pos >= lastValid) {
-                    if (!fill(true, false)) //request line parsing
-                        return false;
-                }
-                if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
-                    pos++;
-                } else {
-                    space = false;
-                }
-            }
-            parsingRequestLineStart = pos;
-            parsingRequestLinePhase = 6;
-
-            // Mark the current buffer position
-            end = 0;
-        }
-        if (parsingRequestLinePhase == 6) {
-            //
-            // Reading the protocol
-            // Protocol is always US-ASCII
-            //
-            while (!parsingRequestLineEol) {
-                // Read new bytes if needed
-                if (pos >= lastValid) {
-                    if (!fill(true, false)) //request line parsing
-                        return false;
-                }
-
-                if (buf[pos] == Constants.CR) {
-                    end = pos;
-                } else if (buf[pos] == Constants.LF) {
-                    if (end == 0)
-                        end = pos;
-                    parsingRequestLineEol = true;
-                }
-                pos++;
-            }
-
-            if ( (end - parsingRequestLineStart) > 0) {
-                request.protocol().setBytes(buf, parsingRequestLineStart, end - parsingRequestLineStart);
-            } else {
-                request.protocol().setString("");
-            }
-            parsingRequestLine = false;
-            parsingRequestLinePhase = 0;
-            parsingRequestLineEol = false;
-            parsingRequestLineStart = 0;
-            return true;
-        }
-        throw new IllegalStateException("Invalid request line parse phase:"+parsingRequestLinePhase);
-    }
-
-    private void expand(int newsize) {
-        if ( newsize > buf.length ) {
-            if (parsingHeader) {
-                throw new IllegalArgumentException(
-                        sm.getString("iib.requestheadertoolarge.error"));
-            }
-            // Should not happen
-            log.warn("Expanding buffer size. Old size: " + buf.length
-                    + ", new size: " + newsize, new Exception());
-            byte[] tmp = new byte[newsize];
-            System.arraycopy(buf,0,tmp,0,buf.length);
-            buf = tmp;
-        }
-    }
-
-    /**
      * Perform blocking read with a timeout if desired
      * @param timeout boolean - if we want to use the timeout data
      * @param block - true if the system should perform a blocking read, false otherwise
@@ -407,7 +91,6 @@ public class InternalNioInputBuffer exte
      * @throws IOException if a socket exception occurs
      * @throws EOFException if end of stream is reached
      */
-
     private int readSocket(boolean timeout, boolean block) throws IOException {
         int nRead = 0;
         socket.getBufHandler().getReadBuffer().clear();
@@ -450,306 +133,6 @@ public class InternalNioInputBuffer exte
         }
     }
 
-    /**
-     * Parse the HTTP headers.
-     */
-    @Override
-    public boolean parseHeaders()
-        throws IOException {
-        if (!parsingHeader) {
-            throw new IllegalStateException(
-                    sm.getString("iib.parseheaders.ise.error"));
-        }
-
-        HeaderParseStatus status = HeaderParseStatus.HAVE_MORE_HEADERS;
-
-        do {
-            status = parseHeader();
-            // Checking that
-            // (1) Headers plus request line size does not exceed its limit
-            // (2) There are enough bytes to avoid expanding the buffer when
-            // reading body
-            // Technically, (2) is technical limitation, (1) is logical
-            // limitation to enforce the meaning of headerBufferSize
-            // From the way how buf is allocated and how blank lines are being
-            // read, it should be enough to check (1) only.
-            if (pos > headerBufferSize
-                    || buf.length - pos < socketReadBufferSize) {
-                throw new IllegalArgumentException(
-                        sm.getString("iib.requestheadertoolarge.error"));
-            }
-        } while ( status == HeaderParseStatus.HAVE_MORE_HEADERS );
-        if (status == HeaderParseStatus.DONE) {
-            parsingHeader = false;
-            end = pos;
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-
-    /**
-     * Parse an HTTP header.
-     *
-     * @return false after reading a blank line (which indicates that the
-     * HTTP header parsing is done
-     */
-    private HeaderParseStatus parseHeader()
-        throws IOException {
-
-        //
-        // Check for blank line
-        //
-
-        byte chr = 0;
-        while (headerParsePos == HeaderParsePosition.HEADER_START) {
-
-            // Read new bytes if needed
-            if (pos >= lastValid) {
-                if (!fill(true,false)) {//parse header
-                    headerParsePos = HeaderParsePosition.HEADER_START;
-                    return HeaderParseStatus.NEED_MORE_DATA;
-                }
-            }
-
-            chr = buf[pos];
-
-            if (chr == Constants.CR) {
-                // Skip
-            } else if (chr == Constants.LF) {
-                pos++;
-                return HeaderParseStatus.DONE;
-            } else {
-                break;
-            }
-
-            pos++;
-
-        }
-
-        if ( headerParsePos == HeaderParsePosition.HEADER_START ) {
-            // Mark the current buffer position
-            headerData.start = pos;
-            headerParsePos = HeaderParsePosition.HEADER_NAME;
-        }
-
-        //
-        // Reading the header name
-        // Header name is always US-ASCII
-        //
-
-        while (headerParsePos == HeaderParsePosition.HEADER_NAME) {
-
-            // Read new bytes if needed
-            if (pos >= lastValid) {
-                if (!fill(true,false)) { //parse header
-                    return HeaderParseStatus.NEED_MORE_DATA;
-                }
-            }
-
-            chr = buf[pos];
-            if (chr == Constants.COLON) {
-                headerParsePos = HeaderParsePosition.HEADER_VALUE_START;
-                headerData.headerValue = headers.addValue(buf, headerData.start, pos - headerData.start);
-                pos++;
-                // Mark the current buffer position
-                headerData.start = pos;
-                headerData.realPos = pos;
-                headerData.lastSignificantChar = pos;
-                break;
-            } else if (!HTTP_TOKEN_CHAR[chr]) {
-                // If a non-token header is detected, skip the line and
-                // ignore the header
-                headerData.lastSignificantChar = pos;
-                return skipLine();
-            }
-
-            // chr is next byte of header name. Convert to lowercase.
-            if ((chr >= Constants.A) && (chr <= Constants.Z)) {
-                buf[pos] = (byte) (chr - Constants.LC_OFFSET);
-            }
-            pos++;
-        }
-
-        // Skip the line and ignore the header
-        if (headerParsePos == HeaderParsePosition.HEADER_SKIPLINE) {
-            return skipLine();
-        }
-
-        //
-        // Reading the header value (which can be spanned over multiple lines)
-        //
-
-        while (headerParsePos == HeaderParsePosition.HEADER_VALUE_START ||
-               headerParsePos == HeaderParsePosition.HEADER_VALUE ||
-               headerParsePos == HeaderParsePosition.HEADER_MULTI_LINE) {
-
-            if ( headerParsePos == HeaderParsePosition.HEADER_VALUE_START ) {
-                // Skipping spaces
-                while (true) {
-                    // Read new bytes if needed
-                    if (pos >= lastValid) {
-                        if (!fill(true,false)) {//parse header
-                            //HEADER_VALUE_START
-                            return HeaderParseStatus.NEED_MORE_DATA;
-                        }
-                    }
-
-                    chr = buf[pos];
-                    if (chr == Constants.SP || chr == Constants.HT) {
-                        pos++;
-                    } else {
-                        headerParsePos = HeaderParsePosition.HEADER_VALUE;
-                        break;
-                    }
-                }
-            }
-            if ( headerParsePos == HeaderParsePosition.HEADER_VALUE ) {
-
-                // Reading bytes until the end of the line
-                boolean eol = false;
-                while (!eol) {
-
-                    // Read new bytes if needed
-                    if (pos >= lastValid) {
-                        if (!fill(true,false)) {//parse header
-                            //HEADER_VALUE
-                            return HeaderParseStatus.NEED_MORE_DATA;
-                        }
-                    }
-
-                    chr = buf[pos];
-                    if (chr == Constants.CR) {
-                        // Skip
-                    } else if (chr == Constants.LF) {
-                        eol = true;
-                    } else if (chr == Constants.SP || chr == Constants.HT) {
-                        buf[headerData.realPos] = chr;
-                        headerData.realPos++;
-                    } else {
-                        buf[headerData.realPos] = chr;
-                        headerData.realPos++;
-                        headerData.lastSignificantChar = headerData.realPos;
-                    }
-
-                    pos++;
-                }
-
-                // Ignore whitespaces at the end of the line
-                headerData.realPos = headerData.lastSignificantChar;
-
-                // Checking the first character of the new line. If the character
-                // is a LWS, then it's a multiline header
-                headerParsePos = HeaderParsePosition.HEADER_MULTI_LINE;
-            }
-            // Read new bytes if needed
-            if (pos >= lastValid) {
-                if (!fill(true,false)) {//parse header
-
-                    //HEADER_MULTI_LINE
-                    return HeaderParseStatus.NEED_MORE_DATA;
-                }
-            }
-
-            chr = buf[pos];
-            if ( headerParsePos == HeaderParsePosition.HEADER_MULTI_LINE ) {
-                if ( (chr != Constants.SP) && (chr != Constants.HT)) {
-                    headerParsePos = HeaderParsePosition.HEADER_START;
-                    break;
-                } else {
-                    // Copying one extra space in the buffer (since there must
-                    // be at least one space inserted between the lines)
-                    buf[headerData.realPos] = chr;
-                    headerData.realPos++;
-                    headerParsePos = HeaderParsePosition.HEADER_VALUE_START;
-                }
-            }
-        }
-        // Set the header value
-        headerData.headerValue.setBytes(buf, headerData.start,
-                headerData.lastSignificantChar - headerData.start);
-        headerData.recycle();
-        return HeaderParseStatus.HAVE_MORE_HEADERS;
-    }
-
-    public int getParsingRequestLinePhase() {
-        return parsingRequestLinePhase;
-    }
-
-    private HeaderParseStatus skipLine() throws IOException {
-        headerParsePos = HeaderParsePosition.HEADER_SKIPLINE;
-        boolean eol = false;
-
-        // Reading bytes until the end of the line
-        while (!eol) {
-
-            // Read new bytes if needed
-            if (pos >= lastValid) {
-                if (!fill(true,false)) {
-                    return HeaderParseStatus.NEED_MORE_DATA;
-                }
-            }
-
-            if (buf[pos] == Constants.CR) {
-                // Skip
-            } else if (buf[pos] == Constants.LF) {
-                eol = true;
-            } else {
-                headerData.lastSignificantChar = pos;
-            }
-
-            pos++;
-        }
-        if (log.isDebugEnabled()) {
-            log.debug(sm.getString("iib.invalidheader", new String(buf,
-                    headerData.start,
-                    headerData.lastSignificantChar - headerData.start + 1,
-                    StandardCharsets.ISO_8859_1)));
-        }
-
-        headerParsePos = HeaderParsePosition.HEADER_START;
-        return HeaderParseStatus.HAVE_MORE_HEADERS;
-    }
-
-    private final HeaderParseData headerData = new HeaderParseData();
-    public static class HeaderParseData {
-        /**
-         * When parsing header name: first character of the header.<br />
-         * When skipping broken header line: first character of the header.<br />
-         * When parsing header value: first character after ':'.
-         */
-        int start = 0;
-        /**
-         * When parsing header name: not used (stays as 0).<br />
-         * When skipping broken header line: not used (stays as 0).<br />
-         * When parsing header value: starts as the first character after ':'.
-         * Then is increased as far as more bytes of the header are harvested.
-         * Bytes from buf[pos] are copied to buf[realPos]. Thus the string from
-         * [start] to [realPos-1] is the prepared value of the header, with
-         * whitespaces removed as needed.<br />
-         */
-        int realPos = 0;
-        /**
-         * When parsing header name: not used (stays as 0).<br />
-         * When skipping broken header line: last non-CR/non-LF character.<br />
-         * When parsing header value: position after the last not-LWS character.<br />
-         */
-        int lastSignificantChar = 0;
-        /**
-         * MB that will store the value of the header. It is null while parsing
-         * header name and is created after the name has been parsed.
-         */
-        MessageBytes headerValue = null;
-        public void recycle() {
-            start = 0;
-            realPos = 0;
-            lastSignificantChar = 0;
-            headerValue = null;
-        }
-    }
-
-
     // ------------------------------------------------------ Protected Methods
 
     @Override

Modified: tomcat/trunk/webapps/docs/changelog.xml
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1588299&r1=1588298&r2=1588299&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Thu Apr 17 16:10:14 2014
@@ -146,6 +146,9 @@
       <scode>
         Upgrade the NIO2 connectors to beta, but still not ready for production. (remm)
       </scode>
+      <scode>
+        Fix code duplication between NIO and NIO2. (remm)
+      </scode>
     </changelog>
   </subsection>
   <subsection name="Jasper">



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org