[tomcat7] 01/01: Import Debian patch 7.0.28-4+deb7u7

Markus Koschany apo at moszumanska.debian.org
Sat Nov 26 15:48:15 UTC 2016


This is an automated email from the git hooks/post-receive script.

apo pushed a commit to branch wheezy
in repository tomcat7.

commit f644856075a39e6d127e76576cd2819601ac17d3
Author: Markus Koschany <apo at debian.org>
Date:   Sat Nov 26 15:39:08 2016 +0100

    Import Debian patch 7.0.28-4+deb7u7
---
 debian/changelog                   |   12 +-
 debian/patches/CVE-2016-6816.patch | 5043 ++++++++++++++++++++++++++++++++++++
 debian/patches/CVE-2016-8735.patch |   24 +
 debian/patches/series              |    2 +
 4 files changed, 5080 insertions(+), 1 deletion(-)

diff --git a/debian/changelog b/debian/changelog
index 10eaff7..22e54ee 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -19,6 +19,16 @@ tomcat7 (7.0.28-4+deb7u7) UNRELEASED; urgency=high
     web application. Therefore, it was possible for a web application to access
     any global JNDI resource whether an explicit ResourceLink had been
     configured or not.
+  * Fixed CVE-2016-6816: The code that parsed the HTTP request line permitted
+    invalid characters. This could be exploited, in conjunction with a proxy
+    that also permitted the invalid characters but with a different
+    interpretation, to inject data into the HTTP response. By manipulating the
+    HTTP response the attacker could poison a web-cache, perform an XSS attack
+    and/or obtain sensitive information from requests other then their own.
+  * Fixed CVE-2016-8735: The JmxRemoteLifecycleListener was not updated to take
+    account of Oracle's fix for CVE-2016-3427. Therefore, Tomcat installations
+    using this listener remained vulnerable to a similar remote code execution
+    vulnerability.
   * CVE-2016-1240 follow-up:
     - The previous init.d fix was vulnerable to a race condition that could
       be exploited to make any existing file writable by the tomcat user.
@@ -28,7 +38,7 @@ tomcat7 (7.0.28-4+deb7u7) UNRELEASED; urgency=high
       Thanks to Paul Szabo for the report.
   * Hardened the init.d script, thanks to Paul Szabo
 
- -- Markus Koschany <apo at debian.org>  Thu, 03 Nov 2016 17:15:18 +0100
+ -- Markus Koschany <apo at debian.org>  Sat, 26 Nov 2016 15:39:08 +0100
 
 tomcat7 (7.0.28-4+deb7u6) wheezy-security; urgency=high
 
diff --git a/debian/patches/CVE-2016-6816.patch b/debian/patches/CVE-2016-6816.patch
new file mode 100644
index 0000000..5bf6a04
--- /dev/null
+++ b/debian/patches/CVE-2016-6816.patch
@@ -0,0 +1,5043 @@
+From: Markus Koschany <apo at debian.org>
+Date: Wed, 23 Nov 2016 00:08:50 +0100
+Subject: CVE-2016-6816
+
+Backport new HttpParser implementation to Wheezy and fix CVE-2016-6816.
+
+Origin: http://svn.apache.org/r1767675
+---
+ java/org/apache/catalina/connector/Response.java   |  19 +-
+ java/org/apache/coyote/Response.java               |  13 +-
+ .../apache/coyote/http11/AbstractInputBuffer.java  |  56 +-
+ .../coyote/http11/InternalAprInputBuffer.java      |  52 +-
+ .../apache/coyote/http11/InternalInputBuffer.java  |  39 +-
+ .../coyote/http11/InternalNioInputBuffer.java      |  68 +-
+ .../apache/coyote/http11/LocalStrings.properties   |   4 +-
+ .../tomcat/util/collections/ConcurrentCache.java   |  59 ++
+ .../tomcat/util/http/parser/AstAttribute.java      |  36 -
+ .../tomcat/util/http/parser/AstMediaType.java      |  82 --
+ .../tomcat/util/http/parser/AstParameter.java      |  44 --
+ .../apache/tomcat/util/http/parser/AstSubType.java |  36 -
+ .../apache/tomcat/util/http/parser/AstType.java    |  36 -
+ .../apache/tomcat/util/http/parser/AstValue.java   |  48 --
+ .../apache/tomcat/util/http/parser/HttpParser.java | 873 +++++++++++++--------
+ .../apache/tomcat/util/http/parser/HttpParser.jjt  | 141 ----
+ .../util/http/parser/HttpParserConstants.java      |  66 --
+ .../util/http/parser/HttpParserTokenManager.java   | 465 -----------
+ .../util/http/parser/HttpParserTreeConstants.java  |  23 -
+ .../util/http/parser/JJTHttpParserState.java       | 124 ---
+ .../apache/tomcat/util/http/parser/MediaType.java  | 125 +++
+ .../tomcat/util/http/parser/MediaTypeCache.java    |  65 ++
+ java/org/apache/tomcat/util/http/parser/Node.java  |  59 --
+ .../tomcat/util/http/parser/ParseException.java    | 187 -----
+ .../tomcat/util/http/parser/SimpleCharStream.java  | 471 -----------
+ .../apache/tomcat/util/http/parser/SimpleNode.java | 118 ---
+ java/org/apache/tomcat/util/http/parser/Token.java | 131 ----
+ .../tomcat/util/http/parser/TokenMgrError.java     | 148 ----
+ .../util/http/parser/TestAuthorizationDigest.java  | 324 ++++++++
+ .../tomcat/util/http/parser/TestMediaType.java     | 190 ++---
+ 30 files changed, 1323 insertions(+), 2779 deletions(-)
+ create mode 100644 java/org/apache/tomcat/util/collections/ConcurrentCache.java
+ delete mode 100644 java/org/apache/tomcat/util/http/parser/AstAttribute.java
+ delete mode 100644 java/org/apache/tomcat/util/http/parser/AstMediaType.java
+ delete mode 100644 java/org/apache/tomcat/util/http/parser/AstParameter.java
+ delete mode 100644 java/org/apache/tomcat/util/http/parser/AstSubType.java
+ delete mode 100644 java/org/apache/tomcat/util/http/parser/AstType.java
+ delete mode 100644 java/org/apache/tomcat/util/http/parser/AstValue.java
+ delete mode 100644 java/org/apache/tomcat/util/http/parser/HttpParser.jjt
+ delete mode 100644 java/org/apache/tomcat/util/http/parser/HttpParserConstants.java
+ delete mode 100644 java/org/apache/tomcat/util/http/parser/HttpParserTokenManager.java
+ delete mode 100644 java/org/apache/tomcat/util/http/parser/HttpParserTreeConstants.java
+ delete mode 100644 java/org/apache/tomcat/util/http/parser/JJTHttpParserState.java
+ create mode 100644 java/org/apache/tomcat/util/http/parser/MediaType.java
+ create mode 100644 java/org/apache/tomcat/util/http/parser/MediaTypeCache.java
+ delete mode 100644 java/org/apache/tomcat/util/http/parser/Node.java
+ delete mode 100644 java/org/apache/tomcat/util/http/parser/ParseException.java
+ delete mode 100644 java/org/apache/tomcat/util/http/parser/SimpleCharStream.java
+ delete mode 100644 java/org/apache/tomcat/util/http/parser/SimpleNode.java
+ delete mode 100644 java/org/apache/tomcat/util/http/parser/Token.java
+ delete mode 100644 java/org/apache/tomcat/util/http/parser/TokenMgrError.java
+ create mode 100644 test/org/apache/tomcat/util/http/parser/TestAuthorizationDigest.java
+
+diff --git a/java/org/apache/catalina/connector/Response.java b/java/org/apache/catalina/connector/Response.java
+index b4b5b95..72d183e 100644
+--- a/java/org/apache/catalina/connector/Response.java
++++ b/java/org/apache/catalina/connector/Response.java
+@@ -53,11 +53,10 @@ import org.apache.tomcat.util.buf.UEncoder;
+ import org.apache.tomcat.util.http.FastHttpDateFormat;
+ import org.apache.tomcat.util.http.MimeHeaders;
+ import org.apache.tomcat.util.http.ServerCookie;
+-import org.apache.tomcat.util.http.parser.AstMediaType;
+ import org.apache.tomcat.util.http.parser.HttpParser;
+-import org.apache.tomcat.util.http.parser.ParseException;
+ import org.apache.tomcat.util.net.URL;
+ import org.apache.tomcat.util.res.StringManager;
++import org.apache.tomcat.util.http.parser.MediaTypeCache;
+ 
+ /**
+  * Wrapper object for the Coyote response.
+@@ -73,6 +72,8 @@ public class Response
+ 
+     // ----------------------------------------------------------- Constructors
+ 
++    private static final MediaTypeCache MEDIA_TYPE_CACHE = new MediaTypeCache(100);
++
+     /**
+      * Compliance with SRV.15.2.22.1. A call to Response.getWriter() if no
+      * character encoding has been specified will result in subsequent calls to
+@@ -800,24 +801,20 @@ public class Response
+             return;
+         }
+ 
+-        AstMediaType m = null;
+-        HttpParser hp = new HttpParser(new StringReader(type));
+-        try {
+-             m = hp.MediaType();
+-        } catch (ParseException e) {
++        String[] m = MEDIA_TYPE_CACHE.parse(type);
++        if (m == null) {
+             // Invalid - Assume no charset and just pass through whatever
+             // the user provided.
+             coyoteResponse.setContentTypeNoCharset(type);
+             return;
+         }
+ 
+-        coyoteResponse.setContentTypeNoCharset(m.toStringNoCharset());
++        coyoteResponse.setContentTypeNoCharset(m[0]);
+ 
+-        String charset = m.getCharset();
+-        if (charset != null) {
++        if (m[1] != null) {
+             // Ignore charset if getWriter() has already been called
+             if (!usingWriter) {
+-                coyoteResponse.setCharacterEncoding(charset);
++                coyoteResponse.setCharacterEncoding(m[1]);
+                 isCharacterEncodingSet = true;
+             }
+         }
+diff --git a/java/org/apache/coyote/Response.java b/java/org/apache/coyote/Response.java
+index df35070..e9f1a61 100644
+--- a/java/org/apache/coyote/Response.java
++++ b/java/org/apache/coyote/Response.java
+@@ -23,9 +23,8 @@ import java.util.Locale;
+ 
+ import org.apache.tomcat.util.buf.ByteChunk;
+ import org.apache.tomcat.util.http.MimeHeaders;
+-import org.apache.tomcat.util.http.parser.AstMediaType;
+ import org.apache.tomcat.util.http.parser.HttpParser;
+-import org.apache.tomcat.util.http.parser.ParseException;
++import org.apache.tomcat.util.http.parser.MediaType;
+ 
+ /**
+  * Response object.
+@@ -435,11 +434,13 @@ public final class Response {
+             return;
+         }
+ 
+-        AstMediaType m = null;
+-        HttpParser hp = new HttpParser(new StringReader(type));
++        MediaType m = null;
+         try {
+-             m = hp.MediaType();
+-        } catch (ParseException e) {
++            m = HttpParser.parseMediaType(new StringReader(type));
++        } catch (IOException e) {
++            // Ignore - null test below handles this
++        }
++        if (m == null) {
+             // Invalid - Assume no charset and just pass through whatever
+             // the user provided.
+             this.contentType = type;
+diff --git a/java/org/apache/coyote/http11/AbstractInputBuffer.java b/java/org/apache/coyote/http11/AbstractInputBuffer.java
+index e013c9f..b534cdf 100644
+--- a/java/org/apache/coyote/http11/AbstractInputBuffer.java
++++ b/java/org/apache/coyote/http11/AbstractInputBuffer.java
+@@ -28,65 +28,11 @@ import org.apache.tomcat.util.res.StringManager;
+ 
+ public abstract class AbstractInputBuffer<S> implements InputBuffer{
+ 
+-    protected static final boolean[] HTTP_TOKEN_CHAR = new boolean[128];
+-
+     /**
+      * The string manager for this package.
+      */
+-    protected static final StringManager sm =
+-        StringManager.getManager(Constants.Package);
+-
+-
+-    static {
+-        for (int i = 0; i < 128; i++) {
+-            if (i < 32) {
+-                HTTP_TOKEN_CHAR[i] = false;
+-            } else if (i == 127) {
+-                HTTP_TOKEN_CHAR[i] = false;
+-            } else if (i == '(') {
+-                HTTP_TOKEN_CHAR[i] = false;
+-            } else if (i == ')') {
+-                HTTP_TOKEN_CHAR[i] = false;
+-            } else if (i == '<') {
+-                HTTP_TOKEN_CHAR[i] = false;
+-            } else if (i == '>') {
+-                HTTP_TOKEN_CHAR[i] = false;
+-            } else if (i == '@') {
+-                HTTP_TOKEN_CHAR[i] = false;
+-            } else if (i == ',') {
+-                HTTP_TOKEN_CHAR[i] = false;
+-            } else if (i == ';') {
+-                HTTP_TOKEN_CHAR[i] = false;
+-            } else if (i == ':') {
+-                HTTP_TOKEN_CHAR[i] = false;
+-            } else if (i == '\\') {
+-                HTTP_TOKEN_CHAR[i] = false;
+-            } else if (i == '\"') {
+-                HTTP_TOKEN_CHAR[i] = false;
+-            } else if (i == '/') {
+-                HTTP_TOKEN_CHAR[i] = false;
+-            } else if (i == '[') {
+-                HTTP_TOKEN_CHAR[i] = false;
+-            } else if (i == ']') {
+-                HTTP_TOKEN_CHAR[i] = false;
+-            } else if (i == '?') {
+-                HTTP_TOKEN_CHAR[i] = false;
+-            } else if (i == '=') {
+-                HTTP_TOKEN_CHAR[i] = false;
+-            } else if (i == '{') {
+-                HTTP_TOKEN_CHAR[i] = false;
+-            } else if (i == '}') {
+-                HTTP_TOKEN_CHAR[i] = false;
+-            } else if (i == ' ') {
+-                HTTP_TOKEN_CHAR[i] = false;
+-            } else if (i == '\t') {
+-                HTTP_TOKEN_CHAR[i] = false;
+-            } else {
+-                HTTP_TOKEN_CHAR[i] = true;
+-            }
+-        }
+-    }
+ 
++    protected static final StringManager sm = StringManager.getManager(Constants.Package);
+ 
+     /**
+      * Associated Coyote request.
+diff --git a/java/org/apache/coyote/http11/InternalAprInputBuffer.java b/java/org/apache/coyote/http11/InternalAprInputBuffer.java
+index eb78d5e..737f09b 100644
+--- a/java/org/apache/coyote/http11/InternalAprInputBuffer.java
++++ b/java/org/apache/coyote/http11/InternalAprInputBuffer.java
+@@ -30,6 +30,7 @@ import org.apache.tomcat.jni.Socket;
+ import org.apache.tomcat.jni.Status;
+ import org.apache.tomcat.util.buf.ByteChunk;
+ import org.apache.tomcat.util.buf.MessageBytes;
++import org.apache.tomcat.util.http.parser.HttpParser;
+ import org.apache.tomcat.util.net.AbstractEndpoint;
+ import org.apache.tomcat.util.net.SocketWrapper;
+ 
+@@ -70,7 +71,7 @@ public class InternalAprInputBuffer extends AbstractInputBuffer<Long> {
+ 
+         parsingHeader = true;
+         swallowInput = true;
+-        
++
+     }
+ 
+ 
+@@ -92,7 +93,7 @@ public class InternalAprInputBuffer extends AbstractInputBuffer<Long> {
+     // --------------------------------------------------------- Public Methods
+ 
+     /**
+-     * Recycle the input buffer. This should be called when closing the 
++     * Recycle the input buffer. This should be called when closing the
+      * connection.
+      */
+     @Override
+@@ -103,14 +104,14 @@ public class InternalAprInputBuffer extends AbstractInputBuffer<Long> {
+ 
+ 
+     /**
+-     * 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 
++     * 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 
++     * @return true if data is properly fed; false if no data is available
+      * immediately and thread should be freed
+      */
+     @Override
+@@ -219,15 +220,16 @@ public class InternalAprInputBuffer extends AbstractInputBuffer<Long> {
+             if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
+                 space = true;
+                 end = pos;
+-            } else if ((buf[pos] == Constants.CR) 
++            } else if ((buf[pos] == Constants.CR)
+                        || (buf[pos] == Constants.LF)) {
+                 // HTTP/0.9 style request
+                 eol = true;
+                 space = true;
+                 end = pos;
+-            } else if ((buf[pos] == Constants.QUESTION) 
+-                       && (questionPos == -1)) {
++            } else if ((buf[pos] == Constants.QUESTION) && (questionPos == -1)) {
+                 questionPos = pos;
++            } else if (HttpParser.isNotRequestTarget(buf[pos])) {
++                throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget"));
+             }
+ 
+             pos++;
+@@ -236,7 +238,7 @@ public class InternalAprInputBuffer extends AbstractInputBuffer<Long> {
+ 
+         request.unparsedURI().setBytes(buf, start, end - start);
+         if (questionPos >= 0) {
+-            request.queryString().setBytes(buf, questionPos + 1, 
++            request.queryString().setBytes(buf, questionPos + 1,
+                                            end - questionPos - 1);
+             request.requestURI().setBytes(buf, start, questionPos - start);
+         } else {
+@@ -264,7 +266,7 @@ public class InternalAprInputBuffer extends AbstractInputBuffer<Long> {
+ 
+         //
+         // Reading the protocol
+-        // Protocol is always US-ASCII
++        // Protocol is always "HTTP/" DIGIT "." DIGIT
+         //
+ 
+         while (!eol) {
+@@ -281,6 +283,8 @@ public class InternalAprInputBuffer extends AbstractInputBuffer<Long> {
+                 if (end == 0)
+                     end = pos;
+                 eol = true;
++            } else if (!HttpParser.isHttpProtocol(buf[pos])) {
++                throw new IllegalArgumentException(sm.getString("iib.invalidHttpProtocol"));
+             }
+ 
+             pos++;
+@@ -292,7 +296,7 @@ public class InternalAprInputBuffer extends AbstractInputBuffer<Long> {
+         } else {
+             request.protocol().setString("");
+         }
+-        
++
+         return true;
+ 
+     }
+@@ -321,7 +325,7 @@ public class InternalAprInputBuffer extends AbstractInputBuffer<Long> {
+ 
+     /**
+      * Parse an HTTP header.
+-     * 
++     *
+      * @return false after reading a blank line (which indicates that the
+      * HTTP header parsing is done
+      */
+@@ -379,7 +383,7 @@ public class InternalAprInputBuffer extends AbstractInputBuffer<Long> {
+             if (buf[pos] == Constants.COLON) {
+                 colon = true;
+                 headerValue = headers.addValue(buf, start, pos - start);
+-            } else if (!HTTP_TOKEN_CHAR[buf[pos]]) {
++            } else if (!HttpParser.isToken(buf[pos])) {
+                 // If a non-token header is detected, skip the line and
+                 // ignore the header
+                 skipLine(start);
+@@ -485,14 +489,14 @@ public class InternalAprInputBuffer extends AbstractInputBuffer<Long> {
+ 
+     }
+ 
+-    
++
+     private void skipLine(int start) throws IOException {
+         boolean eol = false;
+         int lastRealByte = start;
+         if (pos - 1 > start) {
+             lastRealByte = pos - 1;
+         }
+-        
++
+         while (!eol) {
+ 
+             // Read new bytes if needed
+@@ -516,8 +520,8 @@ public class InternalAprInputBuffer extends AbstractInputBuffer<Long> {
+                     lastRealByte - start + 1, Charset.forName("ISO-8859-1"))));
+         }
+     }
+-    
+-    
++
++
+     // ---------------------------------------------------- InputBuffer Methods
+ 
+ 
+@@ -525,7 +529,7 @@ public class InternalAprInputBuffer extends AbstractInputBuffer<Long> {
+      * Read some bytes.
+      */
+     @Override
+-    public int doRead(ByteChunk chunk, Request req) 
++    public int doRead(ByteChunk chunk, Request req)
+         throws IOException {
+ 
+         if (lastActiveFilter == -1)
+@@ -552,11 +556,11 @@ public class InternalAprInputBuffer extends AbstractInputBuffer<Long> {
+         // Ignore the block parameter and just call fill
+         return fill();
+     }
+-    
+-    
++
++
+     /**
+      * Fill the internal buffer using data from the underlying input stream.
+-     * 
++     *
+      * @return false if at end of stream
+      */
+     protected boolean fill()
+@@ -588,7 +592,7 @@ public class InternalAprInputBuffer extends AbstractInputBuffer<Long> {
+         } else {
+ 
+             if (buf.length - end < 4500) {
+-                // In this case, the request header was really large, so we allocate a 
++                // In this case, the request header was really large, so we allocate a
+                 // brand new one; the old one will get GCed when subsequent requests
+                 // clear all references
+                 buf = new byte[buf.length];
+@@ -627,7 +631,7 @@ public class InternalAprInputBuffer extends AbstractInputBuffer<Long> {
+      * This class is an input buffer which will read its data from an input
+      * stream.
+      */
+-    protected class SocketInputBuffer 
++    protected class SocketInputBuffer
+         implements InputBuffer {
+ 
+ 
+@@ -635,7 +639,7 @@ public class InternalAprInputBuffer extends AbstractInputBuffer<Long> {
+          * Read bytes into the specified chunk.
+          */
+         @Override
+-        public int doRead(ByteChunk chunk, Request req ) 
++        public int doRead(ByteChunk chunk, Request req )
+             throws IOException {
+ 
+             if (pos >= lastValid) {
+diff --git a/java/org/apache/coyote/http11/InternalInputBuffer.java b/java/org/apache/coyote/http11/InternalInputBuffer.java
+index a389f4e..1776538 100644
+--- a/java/org/apache/coyote/http11/InternalInputBuffer.java
++++ b/java/org/apache/coyote/http11/InternalInputBuffer.java
+@@ -28,6 +28,7 @@ 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.http.parser.HttpParser;
+ import org.apache.tomcat.util.net.AbstractEndpoint;
+ import org.apache.tomcat.util.net.SocketWrapper;
+ 
+@@ -69,10 +70,10 @@ public class InternalInputBuffer extends AbstractInputBuffer<Socket> {
+ 
+     }
+ 
+-    
++
+     /**
+-     * 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 
++     * 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
+@@ -81,7 +82,7 @@ public class InternalInputBuffer extends AbstractInputBuffer<Socket> {
+      */
+     @Override
+     public boolean parseRequestLine(boolean useAvailableDataOnly)
+-    
++
+         throws IOException {
+ 
+         int start = 0;
+@@ -176,15 +177,16 @@ public class InternalInputBuffer extends AbstractInputBuffer<Socket> {
+             if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
+                 space = true;
+                 end = pos;
+-            } else if ((buf[pos] == Constants.CR) 
++            } else if ((buf[pos] == Constants.CR)
+                        || (buf[pos] == Constants.LF)) {
+                 // HTTP/0.9 style request
+                 eol = true;
+                 space = true;
+                 end = pos;
+-            } else if ((buf[pos] == Constants.QUESTION) 
+-                       && (questionPos == -1)) {
++            } else if ((buf[pos] == Constants.QUESTION) && (questionPos == -1)) {
+                 questionPos = pos;
++            } else if (HttpParser.isNotRequestTarget(buf[pos])) {
++                throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget"));
+             }
+ 
+             pos++;
+@@ -193,7 +195,7 @@ public class InternalInputBuffer extends AbstractInputBuffer<Socket> {
+ 
+         request.unparsedURI().setBytes(buf, start, end - start);
+         if (questionPos >= 0) {
+-            request.queryString().setBytes(buf, questionPos + 1, 
++            request.queryString().setBytes(buf, questionPos + 1,
+                                            end - questionPos - 1);
+             request.requestURI().setBytes(buf, start, questionPos - start);
+         } else {
+@@ -220,9 +222,8 @@ public class InternalInputBuffer extends AbstractInputBuffer<Socket> {
+ 
+         //
+         // Reading the protocol
+-        // Protocol is always US-ASCII
++        // Protocol is always "HTTP/" DIGIT "." DIGIT
+         //
+-
+         while (!eol) {
+ 
+             // Read new bytes if needed
+@@ -237,6 +238,8 @@ public class InternalInputBuffer extends AbstractInputBuffer<Socket> {
+                 if (end == 0)
+                     end = pos;
+                 eol = true;
++            } else if (!HttpParser.isHttpProtocol(buf[pos])) {
++                throw new IllegalArgumentException(sm.getString("iib.invalidHttpProtocol"));
+             }
+ 
+             pos++;
+@@ -248,7 +251,7 @@ public class InternalInputBuffer extends AbstractInputBuffer<Socket> {
+         } else {
+             request.protocol().setString("");
+         }
+-        
++
+         return true;
+ 
+     }
+@@ -277,7 +280,7 @@ public class InternalInputBuffer extends AbstractInputBuffer<Socket> {
+ 
+     /**
+      * Parse an HTTP header.
+-     * 
++     *
+      * @return false after reading a blank line (which indicates that the
+      * HTTP header parsing is done
+      */
+@@ -335,7 +338,7 @@ public class InternalInputBuffer extends AbstractInputBuffer<Socket> {
+             if (buf[pos] == Constants.COLON) {
+                 colon = true;
+                 headerValue = headers.addValue(buf, start, pos - start);
+-            } else if (!HTTP_TOKEN_CHAR[buf[pos]]) {
++            } else if (!HttpParser.isToken(buf[pos])) {
+                 // If a non-token header is detected, skip the line and
+                 // ignore the header
+                 skipLine(start);
+@@ -467,7 +470,7 @@ public class InternalInputBuffer extends AbstractInputBuffer<Socket> {
+         if (pos - 1 > start) {
+             lastRealByte = pos - 1;
+         }
+-        
++
+         while (!eol) {
+ 
+             // Read new bytes if needed
+@@ -494,7 +497,7 @@ public class InternalInputBuffer extends AbstractInputBuffer<Socket> {
+ 
+     /**
+      * Fill the internal buffer using data from the underlying input stream.
+-     * 
++     *
+      * @return false if at end of stream
+      */
+     protected boolean fill() throws IOException {
+@@ -521,7 +524,7 @@ public class InternalInputBuffer extends AbstractInputBuffer<Socket> {
+         } else {
+ 
+             if (buf.length - end < 4500) {
+-                // In this case, the request header was really large, so we allocate a 
++                // In this case, the request header was really large, so we allocate a
+                 // brand new one; the old one will get GCed when subsequent requests
+                 // clear all references
+                 buf = new byte[buf.length];
+@@ -548,7 +551,7 @@ public class InternalInputBuffer extends AbstractInputBuffer<Socket> {
+      * This class is an input buffer which will read its data from an input
+      * stream.
+      */
+-    protected class InputStreamInputBuffer 
++    protected class InputStreamInputBuffer
+         implements InputBuffer {
+ 
+ 
+@@ -556,7 +559,7 @@ public class InternalInputBuffer extends AbstractInputBuffer<Socket> {
+          * Read bytes into the specified chunk.
+          */
+         @Override
+-        public int doRead(ByteChunk chunk, Request req ) 
++        public int doRead(ByteChunk chunk, Request req )
+             throws IOException {
+ 
+             if (pos >= lastValid) {
+diff --git a/java/org/apache/coyote/http11/InternalNioInputBuffer.java b/java/org/apache/coyote/http11/InternalNioInputBuffer.java
+index d6c43bf..98dafea 100644
+--- a/java/org/apache/coyote/http11/InternalNioInputBuffer.java
++++ b/java/org/apache/coyote/http11/InternalNioInputBuffer.java
+@@ -25,6 +25,7 @@ import org.apache.coyote.InputBuffer;
+ import org.apache.coyote.Request;
+ import org.apache.tomcat.util.buf.ByteChunk;
+ import org.apache.tomcat.util.buf.MessageBytes;
++import org.apache.tomcat.util.http.parser.HttpParser;
+ import org.apache.tomcat.util.net.AbstractEndpoint;
+ import org.apache.tomcat.util.net.NioChannel;
+ import org.apache.tomcat.util.net.NioEndpoint;
+@@ -92,7 +93,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer<NioChannel> {
+     }
+ 
+     // ----------------------------------------------------------- Constructors
+-    
++
+ 
+     /**
+      * Alternate constructor.
+@@ -137,7 +138,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer<NioChannel> {
+      * Underlying socket.
+      */
+     private NioChannel socket;
+-    
++
+     /**
+      * Selector pool, for blocking reads and blocking writes
+      */
+@@ -170,7 +171,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer<NioChannel> {
+     // --------------------------------------------------------- Public Methods
+ 
+     /**
+-     * Recycle the input buffer. This should be called when closing the 
++     * Recycle the input buffer. This should be called when closing the
+      * connection.
+      */
+     @Override
+@@ -189,7 +190,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer<NioChannel> {
+ 
+     /**
+      * End processing of current HTTP request.
+-     * Note: All bytes of the current request should have been already 
++     * 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.
+      */
+@@ -206,14 +207,14 @@ public class InternalNioInputBuffer extends AbstractInputBuffer<NioChannel> {
+     }
+ 
+     /**
+-     * 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 
++     * 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 
++     * @return true if data is properly fed; false if no data is available
+      * immediately and thread should be freed
+      */
+     @Override
+@@ -228,7 +229,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer<NioChannel> {
+         if ( parsingRequestLinePhase == 0 ) {
+             byte chr = 0;
+             do {
+-                
++
+                 // Read new bytes if needed
+                 if (pos >= lastValid) {
+                     if (useAvailableDataOnly) {
+@@ -303,7 +304,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer<NioChannel> {
+         }
+         if (parsingRequestLinePhase == 4) {
+             // Mark the current buffer position
+-            
++
+             int end = 0;
+             //
+             // Reading the URI
+@@ -318,21 +319,22 @@ public class InternalNioInputBuffer extends AbstractInputBuffer<NioChannel> {
+                 if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
+                     space = true;
+                     end = pos;
+-                } else if ((buf[pos] == Constants.CR) 
++                } 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)) {
++                } else if ((buf[pos] == Constants.QUESTION) && (parsingRequestLineQPos == -1)) {
+                     parsingRequestLineQPos = pos;
++                } else if (HttpParser.isNotRequestTarget(buf[pos])) {
++                    throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget"));
+                 }
+                 pos++;
+             }
+             request.unparsedURI().setBytes(buf, parsingRequestLineStart, end - parsingRequestLineStart);
+             if (parsingRequestLineQPos >= 0) {
+-                request.queryString().setBytes(buf, parsingRequestLineQPos + 1, 
++                request.queryString().setBytes(buf, parsingRequestLineQPos + 1,
+                                                end - parsingRequestLineQPos - 1);
+                 request.requestURI().setBytes(buf, parsingRequestLineStart, parsingRequestLineQPos - parsingRequestLineStart);
+             } else {
+@@ -372,17 +374,19 @@ public class InternalNioInputBuffer extends AbstractInputBuffer<NioChannel> {
+                     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;
++                } else if (!HttpParser.isHttpProtocol(buf[pos])) {
++                    throw new IllegalArgumentException(sm.getString("iib.invalidHttpProtocol"));
+                 }
+                 pos++;
+             }
+-        
++
+             if ( (end - parsingRequestLineStart) > 0) {
+                 request.protocol().setBytes(buf, parsingRequestLineStart, end - parsingRequestLineStart);
+             } else {
+@@ -396,7 +400,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer<NioChannel> {
+         }
+         throw new IllegalStateException("Invalid request line parse phase:"+parsingRequestLinePhase);
+     }
+-    
++
+     private void expand(int newsize) {
+         if ( newsize > buf.length ) {
+             if (parsingHeader) {
+@@ -411,7 +415,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer<NioChannel> {
+             buf = tmp;
+         }
+     }
+-    
++
+     /**
+      * Perform blocking read with a timeout if desired
+      * @param timeout boolean - if we want to use the timeout data
+@@ -420,7 +424,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer<NioChannel> {
+      * @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();
+@@ -437,7 +441,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer<NioChannel> {
+                 nRead = pool.read(socket.getBufHandler().getReadBuffer(),socket,selector,att.getTimeout());
+             } catch ( EOFException eof ) {
+                 nRead = -1;
+-            } finally { 
++            } finally {
+                 if ( selector != null ) pool.put(selector);
+             }
+         } else {
+@@ -470,7 +474,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer<NioChannel> {
+         }
+ 
+         HeaderParseStatus status = HeaderParseStatus.HAVE_MORE_HEADERS;
+-        
++
+         do {
+             status = parseHeader();
+             // Checking that
+@@ -499,7 +503,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer<NioChannel> {
+ 
+     /**
+      * Parse an HTTP header.
+-     * 
++     *
+      * @return false after reading a blank line (which indicates that the
+      * HTTP header parsing is done
+      */
+@@ -515,7 +519,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer<NioChannel> {
+ 
+             // Read new bytes if needed
+             if (pos >= lastValid) {
+-                if (!fill(true,false)) {//parse header 
++                if (!fill(true,false)) {//parse header
+                     headerParsePos = HeaderParsePosition.HEADER_START;
+                     return HeaderParseStatus.NEED_MORE_DATA;
+                 }
+@@ -551,7 +555,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer<NioChannel> {
+ 
+             // Read new bytes if needed
+             if (pos >= lastValid) {
+-                if (!fill(true,false)) { //parse header 
++                if (!fill(true,false)) { //parse header
+                     return HeaderParseStatus.NEED_MORE_DATA;
+                 }
+             }
+@@ -566,7 +570,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer<NioChannel> {
+                 headerData.realPos = pos;
+                 headerData.lastSignificantChar = pos;
+                 break;
+-            } else if (!HTTP_TOKEN_CHAR[chr]) {
++            } else if (!HttpParser.isToken(chr)) {
+                 // If a non-token header is detected, skip the line and
+                 // ignore the header
+                 headerData.lastSignificantChar = pos;
+@@ -598,7 +602,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer<NioChannel> {
+                 while (true) {
+                     // Read new bytes if needed
+                     if (pos >= lastValid) {
+-                        if (!fill(true,false)) {//parse header 
++                        if (!fill(true,false)) {//parse header
+                             //HEADER_VALUE_START
+                             return HeaderParseStatus.NEED_MORE_DATA;
+                         }
+@@ -621,7 +625,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer<NioChannel> {
+ 
+                     // Read new bytes if needed
+                     if (pos >= lastValid) {
+-                        if (!fill(true,false)) {//parse header 
++                        if (!fill(true,false)) {//parse header
+                             //HEADER_VALUE
+                             return HeaderParseStatus.NEED_MORE_DATA;
+                         }
+@@ -654,7 +658,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer<NioChannel> {
+             // Read new bytes if needed
+             if (pos >= lastValid) {
+                 if (!fill(true,false)) {//parse header
+-                    
++
+                     //HEADER_MULTI_LINE
+                     return HeaderParseStatus.NEED_MORE_DATA;
+                 }
+@@ -680,7 +684,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer<NioChannel> {
+         headerData.recycle();
+         return HeaderParseStatus.HAVE_MORE_HEADERS;
+     }
+-    
++
+     public int getParsingRequestLinePhase() {
+         return parsingRequestLinePhase;
+     }
+@@ -780,7 +784,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer<NioChannel> {
+ 
+     /**
+      * Fill the internal buffer using data from the underlying input stream.
+-     * 
++     *
+      * @return false if at end of stream
+      */
+     @Override
+@@ -789,7 +793,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer<NioChannel> {
+     }
+ 
+     protected boolean fill(boolean timeout, boolean block) throws IOException, EOFException {
+-        
++
+ 
+         boolean read = false;
+ 
+@@ -818,7 +822,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer<NioChannel> {
+      * This class is an input buffer which will read its data from an input
+      * stream.
+      */
+-    protected class SocketInputBuffer 
++    protected class SocketInputBuffer
+         implements InputBuffer {
+ 
+ 
+@@ -826,7 +830,7 @@ public class InternalNioInputBuffer extends AbstractInputBuffer<NioChannel> {
+          * Read bytes into the specified chunk.
+          */
+         @Override
+-        public int doRead(ByteChunk chunk, Request req ) 
++        public int doRead(ByteChunk chunk, Request req )
+             throws IOException {
+ 
+             if (pos >= lastValid) {
+diff --git a/java/org/apache/coyote/http11/LocalStrings.properties b/java/org/apache/coyote/http11/LocalStrings.properties
+index 94df96c..42c7aa7 100644
+--- a/java/org/apache/coyote/http11/LocalStrings.properties
++++ b/java/org/apache/coyote/http11/LocalStrings.properties
+@@ -38,8 +38,10 @@ http11processor.comet.notsupported=The Comet protocol is not supported by this c
+ http11processor.sendfile.error=Error sending data using sendfile. May be caused by invalid request attributes for start/end points
+ 
+ iib.eof.error=Unexpected EOF read on the socket
+-iib.invalidheader=The HTTP header line [{0}] does not conform to RFC 2616 and has been ignored.
++iib.invalidheader=The HTTP header line [{0}] does not conform to RFC 7230 and has been ignored.
+ iib.invalidmethod=Invalid character (CR or LF) found in method name
++iib.invalidRequestTarget=Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986
++iib.invalidHttpProtocol=Invalid character found in the HTTP protocol
+ iib.parseheaders.ise.error=Unexpected state: headers already parsed. Buffer not recycled?
+ iib.requestheadertoolarge.error=Request header is too large
+ 
+diff --git a/java/org/apache/tomcat/util/collections/ConcurrentCache.java b/java/org/apache/tomcat/util/collections/ConcurrentCache.java
+new file mode 100644
+index 0000000..3d882b6
+--- /dev/null
++++ b/java/org/apache/tomcat/util/collections/ConcurrentCache.java
+@@ -0,0 +1,59 @@
++/*
++ * 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.tomcat.util.collections;
++
++import java.util.Map;
++import java.util.WeakHashMap;
++import java.util.concurrent.ConcurrentHashMap;
++
++public final class ConcurrentCache<K,V> {
++
++    private final int size;
++
++    private final Map<K,V> eden;
++
++    private final Map<K,V> longterm;
++
++    public ConcurrentCache(int size) {
++        this.size = size;
++        this.eden = new ConcurrentHashMap<K,V>(size);
++        this.longterm = new WeakHashMap<K,V>(size);
++    }
++
++    public V get(K k) {
++        V v = this.eden.get(k);
++        if (v == null) {
++            synchronized (longterm) {
++                v = this.longterm.get(k);
++            }
++            if (v != null) {
++                this.eden.put(k, v);
++            }
++        }
++        return v;
++    }
++
++    public void put(K k, V v) {
++        if (this.eden.size() >= size) {
++            synchronized (longterm) {
++                this.longterm.putAll(this.eden);
++            }
++            this.eden.clear();
++        }
++        this.eden.put(k, v);
++    }
++}
+diff --git a/java/org/apache/tomcat/util/http/parser/AstAttribute.java b/java/org/apache/tomcat/util/http/parser/AstAttribute.java
+deleted file mode 100644
+index 105c09f..0000000
+--- a/java/org/apache/tomcat/util/http/parser/AstAttribute.java
++++ /dev/null
+@@ -1,36 +0,0 @@
+-/*
+- *  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.tomcat.util.http.parser;
+-
+-/**
+- * Represents an attribute as per section 3.6 of RFC 2616. Originally generated
+- * by <a href="http://javacc.java.net/doc/JJTree.html"> JJTree</a>.
+- */
+-public class AstAttribute extends SimpleNode {
+-    public AstAttribute(int id) {
+-        super(id);
+-    }
+-
+-    public AstAttribute(HttpParser p, int id) {
+-        super(p, id);
+-    }
+-
+-    @Override
+-    public String toString() {
+-        return value.toString();
+-    }
+-}
+diff --git a/java/org/apache/tomcat/util/http/parser/AstMediaType.java b/java/org/apache/tomcat/util/http/parser/AstMediaType.java
+deleted file mode 100644
+index 758d10e..0000000
+--- a/java/org/apache/tomcat/util/http/parser/AstMediaType.java
++++ /dev/null
+@@ -1,82 +0,0 @@
+-/*
+- *  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.tomcat.util.http.parser;
+-
+-/**
+- * Represents a media-type as per section 3.7 of RFC 2616. Originally generated
+- * by <a href="http://javacc.java.net/doc/JJTree.html"> JJTree</a>.
+- */
+-public class AstMediaType extends SimpleNode {
+-
+-    private static final String CHARSET = "charset";
+-
+-    public AstMediaType(int id) {
+-        super(id);
+-    }
+-
+-    public AstMediaType(HttpParser p, int id) {
+-        super(p, id);
+-    }
+-
+-    @Override
+-    public String toString() {
+-        StringBuilder sb = new StringBuilder();
+-        sb.append(children[0].toString());
+-        sb.append('/');
+-        sb.append(children[1].toString());
+-        for (int i = 2; i < children.length; i++) {
+-            String s = children[i].toString();
+-            // Invalid parameters will have zero length - skip them
+-            if (s.length() > 0) {
+-                sb.append(';');
+-                sb.append(s);
+-            }
+-        }
+-        return sb.toString();
+-    }
+-
+-    public String toStringNoCharset() {
+-        StringBuilder sb = new StringBuilder();
+-        sb.append(children[0].toString());
+-        sb.append('/');
+-        sb.append(children[1].toString());
+-        for (int i = 2; i < children.length; i++) {
+-            AstParameter p = (AstParameter) children[i];
+-            if (!CHARSET.equalsIgnoreCase(
+-                    p.children[0].jjtGetValue().toString())) {
+-                String s = p.toString();
+-                // Invalid parameters will have zero length - skip them
+-                if (s.length() > 0) {
+-                    sb.append(';');
+-                    sb.append(p.toString());
+-                }
+-            }
+-        }
+-        return sb.toString();
+-    }
+-
+-    public String getCharset() {
+-        for (int i = 2; i < children.length; i++) {
+-            AstParameter p = (AstParameter) children[i];
+-            if (CHARSET.equalsIgnoreCase(
+-                    p.children[0].jjtGetValue().toString())) {
+-                return p.children[1].jjtGetValue().toString();
+-            }
+-        }
+-        return null;
+-    }
+-}
+diff --git a/java/org/apache/tomcat/util/http/parser/AstParameter.java b/java/org/apache/tomcat/util/http/parser/AstParameter.java
+deleted file mode 100644
+index 52d2c6e..0000000
+--- a/java/org/apache/tomcat/util/http/parser/AstParameter.java
++++ /dev/null
+@@ -1,44 +0,0 @@
+-/*
+- *  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.tomcat.util.http.parser;
+-
+-/**
+- * Represents a parameter as per section 3.6 of RFC 2616. Originally generated
+- * by <a href="http://javacc.java.net/doc/JJTree.html"> JJTree</a>.
+- */
+-public class AstParameter extends SimpleNode {
+-    public AstParameter(int id) {
+-        super(id);
+-    }
+-
+-    public AstParameter(HttpParser p, int id) {
+-        super(p, id);
+-    }
+-
+-    @Override
+-    public String toString() {
+-        if (children.length != 2) {
+-            // Invalid input - swallow it.
+-            return "";
+-        }
+-        StringBuilder sb = new StringBuilder();
+-        sb.append(children[0].toString());
+-        sb.append("=");
+-        sb.append(children[1].toString());
+-        return sb.toString();
+-    }
+-}
+diff --git a/java/org/apache/tomcat/util/http/parser/AstSubType.java b/java/org/apache/tomcat/util/http/parser/AstSubType.java
+deleted file mode 100644
+index b1b6dec..0000000
+--- a/java/org/apache/tomcat/util/http/parser/AstSubType.java
++++ /dev/null
+@@ -1,36 +0,0 @@
+-/*
+- *  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.tomcat.util.http.parser;
+-
+-/**
+- * Represents a sub-type as per section 3.7 of RFC 2616. Originally generated by
+- * <a href="http://javacc.java.net/doc/JJTree.html"> JJTree</a>.
+- */
+-public class AstSubType extends SimpleNode {
+-    public AstSubType(int id) {
+-        super(id);
+-    }
+-
+-    public AstSubType(HttpParser p, int id) {
+-        super(p, id);
+-    }
+-
+-    @Override
+-    public String toString() {
+-        return value.toString();
+-    }
+-}
+diff --git a/java/org/apache/tomcat/util/http/parser/AstType.java b/java/org/apache/tomcat/util/http/parser/AstType.java
+deleted file mode 100644
+index 97d36d8..0000000
+--- a/java/org/apache/tomcat/util/http/parser/AstType.java
++++ /dev/null
+@@ -1,36 +0,0 @@
+-/*
+- *  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.tomcat.util.http.parser;
+-
+-/**
+- * Represents a type as per section 3.7 of RFC 2616. Originally generated by <a
+- * href="http://javacc.java.net/doc/JJTree.html"> JJTree</a>.
+- */
+-public class AstType extends SimpleNode {
+-    public AstType(int id) {
+-        super(id);
+-    }
+-
+-    public AstType(HttpParser p, int id) {
+-        super(p, id);
+-    }
+-
+-    @Override
+-    public String toString() {
+-        return value.toString();
+-    }
+-}
+diff --git a/java/org/apache/tomcat/util/http/parser/AstValue.java b/java/org/apache/tomcat/util/http/parser/AstValue.java
+deleted file mode 100644
+index 7dd91e1..0000000
+--- a/java/org/apache/tomcat/util/http/parser/AstValue.java
++++ /dev/null
+@@ -1,48 +0,0 @@
+-/*
+- *  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.tomcat.util.http.parser;
+-
+-/**
+- * Represents a value as per section 3.6 of RFC 2616. Originally generated by <a
+- * href="http://javacc.java.net/doc/JJTree.html"> JJTree</a>.
+- */
+-public class AstValue extends SimpleNode {
+-    @Override
+-    public Object jjtGetValue() {
+-        String s = value.toString();
+-        if (s.charAt(0) == '\"') {
+-            // Quoted
+-            return s.substring(1, s.length() - 1).replaceAll("\\\"", "\"");
+-        } else {
+-            // Unquoted
+-            return s;
+-        }
+-    }
+-
+-    public AstValue(int id) {
+-        super(id);
+-    }
+-
+-    public AstValue(HttpParser p, int id) {
+-        super(p, id);
+-    }
+-
+-    @Override
+-    public String toString() {
+-        return value.toString();
+-    }
+-}
+diff --git a/java/org/apache/tomcat/util/http/parser/HttpParser.java b/java/org/apache/tomcat/util/http/parser/HttpParser.java
+index 15d28f2..948694e 100644
+--- a/java/org/apache/tomcat/util/http/parser/HttpParser.java
++++ b/java/org/apache/tomcat/util/http/parser/HttpParser.java
+@@ -1,348 +1,563 @@
+-/* Generated By:JJTree&JavaCC: Do not edit this line. HttpParser.java */
++/*
++ * 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.tomcat.util.http.parser;
+- at SuppressWarnings("all") // Ignore warnings in generated code
+-public class HttpParser/*@bgen(jjtree)*/implements HttpParserTreeConstants, HttpParserConstants {/*@bgen(jjtree)*/
+-  protected JJTHttpParserState jjtree = new JJTHttpParserState();
+ 
+-/*
+- * Content-Type
++import java.io.IOException;
++import java.io.StringReader;
++import java.util.HashMap;
++import java.util.LinkedHashMap;
++import java.util.Locale;
++import java.util.Map;
++
++/**
++ * HTTP header value parser implementation. Parsing HTTP headers as per RFC2616
++ * is not always as simple as it first appears. For headers that only use tokens
++ * the simple approach will normally be sufficient. However, for the other
++ * headers, while simple code meets 99.9% of cases, there are often some edge
++ * cases that make things far more complicated.
++ *
++ * The purpose of this parser is to let the parser worry about the edge cases.
++ * It provides tolerant (where safe to do so) parsing of HTTP header values
++ * assuming that wrapped header lines have already been unwrapped. (The Tomcat
++ * header processing code does the unwrapping.)
++ *
++ * Provides parsing of the following HTTP header values as per RFC 2616:
++ * - Authorization for DIGEST authentication
++ * - MediaType (used for Content-Type header)
++ *
++ * Support for additional headers will be provided as required.
+  */
+-  final public AstMediaType MediaType() throws ParseException {
+-                                       /*@bgen(jjtree) MediaType */
+-  AstMediaType jjtn000 = new AstMediaType(JJTMEDIATYPE);
+-  boolean jjtc000 = true;
+-  jjtree.openNodeScope(jjtn000);
+-    try {
+-      Type();
+-      jj_consume_token(FORWARD_SLASH);
+-      SubType();
+-      label_1:
+-      while (true) {
+-        switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+-        case SEMI_COLON:
+-          ;
+-          break;
+-        default:
+-          jj_la1[0] = jj_gen;
+-          break label_1;
+-        }
+-        jj_consume_token(SEMI_COLON);
+-        Parameter();
+-      }
+-      jj_consume_token(0);
+-                                                                          jjtree.closeNodeScope(jjtn000, true);
+-                                                                          jjtc000 = false;
+-        {if (true) return jjtn000;}
+-    } catch (Throwable jjte000) {
+-      if (jjtc000) {
+-        jjtree.clearNodeScope(jjtn000);
+-        jjtc000 = false;
+-      } else {
+-        jjtree.popNode();
+-      }
+-      if (jjte000 instanceof RuntimeException) {
+-        {if (true) throw (RuntimeException)jjte000;}
+-      }
+-      if (jjte000 instanceof ParseException) {
+-        {if (true) throw (ParseException)jjte000;}
+-      }
+-      {if (true) throw (Error)jjte000;}
+-    } finally {
+-      if (jjtc000) {
+-        jjtree.closeNodeScope(jjtn000, true);
+-      }
++public class HttpParser {
++
++    @SuppressWarnings("unused")  // Unused due to buggy client implementations
++    private static final Integer FIELD_TYPE_TOKEN = Integer.valueOf(0);
++    private static final Integer FIELD_TYPE_QUOTED_STRING = Integer.valueOf(1);
++    private static final Integer FIELD_TYPE_TOKEN_OR_QUOTED_STRING = Integer.valueOf(2);
++    private static final Integer FIELD_TYPE_LHEX = Integer.valueOf(3);
++    private static final Integer FIELD_TYPE_QUOTED_TOKEN = Integer.valueOf(4);
++
++    private static final Map<String,Integer> fieldTypes =
++            new HashMap<String,Integer>();
++
++    private static final int ARRAY_SIZE = 128;
++
++    private static final boolean[] IS_CONTROL = new boolean[ARRAY_SIZE];
++    private static final boolean[] IS_SEPARATOR = new boolean[ARRAY_SIZE];
++    private static final boolean[] IS_TOKEN = new boolean[ARRAY_SIZE];
++    private static final boolean[] IS_HEX = new boolean[ARRAY_SIZE];
++    private static final boolean[] IS_NOT_REQUEST_TARGET = new boolean[ARRAY_SIZE];
++    private static final boolean[] IS_HTTP_PROTOCOL = new boolean[ARRAY_SIZE];
++
++    static {
++        // Digest field types.
++        // Note: These are more relaxed than RFC2617. This adheres to the
++        //       recommendation of RFC2616 that servers are tolerant of buggy
++        //       clients when they can be so without ambiguity.
++        fieldTypes.put("username", FIELD_TYPE_QUOTED_STRING);
++        fieldTypes.put("realm", FIELD_TYPE_QUOTED_STRING);
++        fieldTypes.put("nonce", FIELD_TYPE_QUOTED_STRING);
++        fieldTypes.put("digest-uri", FIELD_TYPE_QUOTED_STRING);
++        // RFC2617 says response is <">32LHEX<">. 32LHEX will also be accepted
++        fieldTypes.put("response", FIELD_TYPE_LHEX);
++        // RFC2617 says algorithm is token. <">token<"> will also be accepted
++        fieldTypes.put("algorithm", FIELD_TYPE_QUOTED_TOKEN);
++        fieldTypes.put("cnonce", FIELD_TYPE_QUOTED_STRING);
++        fieldTypes.put("opaque", FIELD_TYPE_QUOTED_STRING);
++        // RFC2617 says qop is token. <">token<"> will also be accepted
++        fieldTypes.put("qop", FIELD_TYPE_QUOTED_TOKEN);
++        // RFC2617 says nc is 8LHEX. <">8LHEX<"> will also be accepted
++        fieldTypes.put("nc", FIELD_TYPE_LHEX);
++
++        for (int i = 0; i < ARRAY_SIZE; i++) {
++            // Control> 0-31, 127
++            if (i < 32 || i == 127) {
++                IS_CONTROL[i] = true;
++            }
++
++            // Separator
++            if (    i == '(' || i == ')' || i == '<' || i == '>'  || i == '@'  ||
++                    i == ',' || i == ';' || i == ':' || i == '\\' || i == '\"' ||
++                    i == '/' || i == '[' || i == ']' || i == '?'  || i == '='  ||
++                    i == '{' || i == '}' || i == ' ' || i == '\t') {
++                IS_SEPARATOR[i] = true;
++            }
++
++            // Token: Anything 0-127 that is not a control and not a separator
++            if (!IS_CONTROL[i] && !IS_SEPARATOR[i] && i < 128) {
++                IS_TOKEN[i] = true;
++            }
++
++            // Hex: 0-9, a-f, A-F
++            if ((i >= '0' && i <='9') || (i >= 'a' && i <= 'f') || (i >= 'A' && i <= 'F')) {
++                IS_HEX[i] = true;
++            }
++
++            // Not valid for request target.
++            // Combination of multiple rules from RFC7230 and RFC 3986. Must be
++            // ASCII, no controls plus a few additional characters excluded
++            if (IS_CONTROL[i] || i > 127 ||
++                    i == ' ' || i == '\"' || i == '#' || i == '<' || i == '>' || i == '\\' ||
++                    i == '^' || i == '`'  || i == '{' || i == '|' || i == '}') {
++                IS_NOT_REQUEST_TARGET[i] = true;
++            }
++
++            // Not valid for HTTP protocol
++            // "HTTP/" DIGIT "." DIGIT
++            if (i == 'H' || i == 'T' || i == 'P' || i == '/' || i == '.' || (i >= '0' && i <= '9')) {
++                IS_HTTP_PROTOCOL[i] = true;
++            }
++        }
++    }
++
++    /**
++     * Parses an HTTP Authorization header for DIGEST authentication as per RFC
++     * 2617 section 3.2.2.
++     *
++     * @param input The header value to parse
++     *
++     * @return  A map of directives and values as {@link String}s or
++     *          <code>null</code> if a parsing error occurs. Although the
++     *          values returned are {@link String}s they will have been
++     *          validated to ensure that they conform to RFC 2617.
++     *
++     * @throws IllegalArgumentException If the header does not conform to RFC
++     *                                  2617
++     * @throws IOException If an error occurs while reading the input
++     */
++    public static Map<String,String> parseAuthorizationDigest (
++            StringReader input) throws IllegalArgumentException, IOException {
++
++        Map<String,String> result = new HashMap<String,String>();
++
++        if (skipConstant(input, "Digest") != SkipConstantResult.FOUND) {
++            return null;
++        }
++        // All field names are valid tokens
++        String field = readToken(input);
++        if (field == null) {
++            return null;
++        }
++        while (!field.equals("")) {
++            if (skipConstant(input, "=") != SkipConstantResult.FOUND) {
++                return null;
++            }
++            String value = null;
++            Integer type = fieldTypes.get(field.toLowerCase(Locale.ENGLISH));
++            if (type == null) {
++                // auth-param = token "=" ( token | quoted-string )
++                type = FIELD_TYPE_TOKEN_OR_QUOTED_STRING;
++            }
++            switch (type.intValue()) {
++                case 0:
++                    // FIELD_TYPE_TOKEN
++                    value = readToken(input);
++                    break;
++                case 1:
++                    // FIELD_TYPE_QUOTED_STRING
++                    value = readQuotedString(input, false);
++                    break;
++                case 2:
++                    // FIELD_TYPE_TOKEN_OR_QUOTED_STRING
++                    value = readTokenOrQuotedString(input, false);
++                    break;
++                case 3:
++                    // FIELD_TYPE_LHEX
++                    value = readLhex(input);
++                    break;
++                case 4:
++                    // FIELD_TYPE_QUOTED_TOKEN
++                    value = readQuotedToken(input);
++                    break;
++                default:
++                    // Error
++                    throw new IllegalArgumentException(
++                            "TODO i18n: Unsupported type");
++            }
++
++            if (value == null) {
++                return null;
++            }
++            result.put(field, value);
++
++            if (skipConstant(input, ",") == SkipConstantResult.NOT_FOUND) {
++                return null;
++            }
++            field = readToken(input);
++            if (field == null) {
++                return null;
++            }
++        }
++
++        return result;
++    }
++
++    public static MediaType parseMediaType(StringReader input)
++            throws IOException {
++
++        // Type (required)
++        String type = readToken(input);
++        if (type == null || type.length() == 0) {
++            return null;
++        }
++
++        if (skipConstant(input, "/") == SkipConstantResult.NOT_FOUND) {
++            return null;
++        }
++
++        // Subtype (required)
++        String subtype = readToken(input);
++        if (subtype == null || subtype.length() == 0) {
++            return null;
++        }
++
++        LinkedHashMap<String,String> parameters =
++                new LinkedHashMap<String,String>();
++
++        SkipConstantResult lookForSemiColon = skipConstant(input, ";");
++        if (lookForSemiColon == SkipConstantResult.NOT_FOUND) {
++            return null;
++        }
++        while (lookForSemiColon == SkipConstantResult.FOUND) {
++            String attribute = readToken(input);
++
++            String value = "";
++            if (skipConstant(input, "=") == SkipConstantResult.FOUND) {
++                value = readTokenOrQuotedString(input, true);
++            }
++
++            if (attribute != null) {
++                parameters.put(attribute.toLowerCase(Locale.ENGLISH), value);
++            }
++
++            lookForSemiColon = skipConstant(input, ";");
++            if (lookForSemiColon == SkipConstantResult.NOT_FOUND) {
++                return null;
++            }
++        }
++
++        return new MediaType(type, subtype, parameters);
+     }
+-    throw new Error("Missing return statement in function");
+-  }
+-
+-  final public void Type() throws ParseException {
+-                     /*@bgen(jjtree) Type */
+-                      AstType jjtn000 = new AstType(JJTTYPE);
+-                      boolean jjtc000 = true;
+-                      jjtree.openNodeScope(jjtn000);Token t = null;
+-    try {
+-      t = jj_consume_token(HTTP_TOKEN);
+-                     jjtree.closeNodeScope(jjtn000, true);
+-                     jjtc000 = false;
+-                     jjtn000.jjtSetValue(t.image.trim());
+-    } finally {
+-      if (jjtc000) {
+-        jjtree.closeNodeScope(jjtn000, true);
+-      }
++
++
++    public static String unquote(String input) {
++        if (input == null || input.length() < 2) {
++            return input;
++        }
++
++        int start;
++        int end;
++
++        // Skip surrounding quotes if there are any
++        if (input.charAt(0) == '"') {
++            start = 1;
++            end = input.length() - 1;
++        } else {
++            start = 0;
++            end = input.length();
++        }
++
++        StringBuilder result = new StringBuilder();
++        for (int i = start ; i < end; i++) {
++            char c = input.charAt(i);
++            if (input.charAt(i) == '\\') {
++                i++;
++                result.append(input.charAt(i));
++            } else {
++                result.append(c);
++            }
++        }
++        return result.toString();
+     }
+-  }
+-
+-  final public void SubType() throws ParseException {
+-                          /*@bgen(jjtree) SubType */
+-                           AstSubType jjtn000 = new AstSubType(JJTSUBTYPE);
+-                           boolean jjtc000 = true;
+-                           jjtree.openNodeScope(jjtn000);Token t = null;
+-    try {
+-      t = jj_consume_token(HTTP_TOKEN);
+-                     jjtree.closeNodeScope(jjtn000, true);
+-                     jjtc000 = false;
+-                     jjtn000.jjtSetValue(t.image.trim());
+-    } finally {
+-      if (jjtc000) {
+-        jjtree.closeNodeScope(jjtn000, true);
+-      }
++
++
++    public static boolean isToken(int c) {
++        // Fast for correct values, slower for incorrect ones
++        try {
++            return IS_TOKEN[c];
++        } catch (ArrayIndexOutOfBoundsException ex) {
++            return false;
++        }
+     }
+-  }
+-
+-  final public void Parameter() throws ParseException {
+-                               /*@bgen(jjtree) Parameter */
+-  AstParameter jjtn000 = new AstParameter(JJTPARAMETER);
+-  boolean jjtc000 = true;
+-  jjtree.openNodeScope(jjtn000);
+-    try {
+-      Attribute();
+-      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+-      case EQUALS:
+-        jj_consume_token(EQUALS);
+-        Value();
+-        break;
+-      default:
+-        jj_la1[1] = jj_gen;
+-        ;
+-      }
+-    } catch (Throwable jjte000) {
+-      if (jjtc000) {
+-        jjtree.clearNodeScope(jjtn000);
+-        jjtc000 = false;
+-      } else {
+-        jjtree.popNode();
+-      }
+-      if (jjte000 instanceof RuntimeException) {
+-        {if (true) throw (RuntimeException)jjte000;}
+-      }
+-      if (jjte000 instanceof ParseException) {
+-        {if (true) throw (ParseException)jjte000;}
+-      }
+-      {if (true) throw (Error)jjte000;}
+-    } finally {
+-      if (jjtc000) {
+-        jjtree.closeNodeScope(jjtn000, true);
+-      }
++
++
++    public static boolean isHex(int c) {
++        // Fast for correct values, slower for some incorrect ones
++        try {
++            return IS_HEX[c];
++        } catch (ArrayIndexOutOfBoundsException ex) {
++            return false;
++        }
+     }
+-  }
+-
+-  final public void Attribute() throws ParseException {
+-                                /*@bgen(jjtree) Attribute */
+-                                 AstAttribute jjtn000 = new AstAttribute(JJTATTRIBUTE);
+-                                 boolean jjtc000 = true;
+-                                 jjtree.openNodeScope(jjtn000);Token t = null;
+-    try {
+-      t = jj_consume_token(HTTP_TOKEN);
+-                     jjtree.closeNodeScope(jjtn000, true);
+-                     jjtc000 = false;
+-                     jjtn000.jjtSetValue(t.image.trim());
+-    } finally {
+-      if (jjtc000) {
+-        jjtree.closeNodeScope(jjtn000, true);
+-      }
++
++
++    public static boolean isNotRequestTarget(int c) {
++        // Fast for valid request target characters, slower for some incorrect
++        // ones
++        try {
++            return IS_NOT_REQUEST_TARGET[c];
++        } catch (ArrayIndexOutOfBoundsException ex) {
++            return true;
++        }
+     }
+-  }
+-
+-  final public void Value() throws ParseException {
+-                       /*@bgen(jjtree) Value */
+-                        AstValue jjtn000 = new AstValue(JJTVALUE);
+-                        boolean jjtc000 = true;
+-                        jjtree.openNodeScope(jjtn000);Token t = null;
+-    try {
+-      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+-      case HTTP_TOKEN:
+-        t = jj_consume_token(HTTP_TOKEN);
+-                     jjtree.closeNodeScope(jjtn000, true);
+-                     jjtc000 = false;
+-                     jjtn000.jjtSetValue(t.image.trim());
+-        break;
+-      case QUOTED_STRING:
+-        t = jj_consume_token(QUOTED_STRING);
+-                                                                                  jjtree.closeNodeScope(jjtn000, true);
+-                                                                                  jjtc000 = false;
+-                                                                                  jjtn000.jjtSetValue(t.image.trim());
+-        break;
+-      default:
+-        jj_la1[2] = jj_gen;
+-        jj_consume_token(-1);
+-        throw new ParseException();
+-      }
+-    } finally {
+-      if (jjtc000) {
+-        jjtree.closeNodeScope(jjtn000, true);
+-      }
++
++
++    public static boolean isHttpProtocol(int c) {
++        // Fast for valid HTTP protocol characters, slower for some incorrect
++        // ones
++        try {
++            return IS_HTTP_PROTOCOL[c];
++        } catch (ArrayIndexOutOfBoundsException ex) {
++            return false;
++        }
+     }
+-  }
+-
+-  /** Generated Token Manager. */
+-  public HttpParserTokenManager token_source;
+-  SimpleCharStream jj_input_stream;
+-  /** Current token. */
+-  public Token token;
+-  /** Next token. */
+-  public Token jj_nt;
+-  private int jj_ntk;
+-  private int jj_gen;
+-  final private int[] jj_la1 = new int[3];
+-  static private int[] jj_la1_0;
+-  static {
+-      jj_la1_init_0();
+-   }
+-   private static void jj_la1_init_0() {
+-      jj_la1_0 = new int[] {0x2,0x4,0x180,};
+-   }
+-
+-  /** Constructor with InputStream. */
+-  public HttpParser(java.io.InputStream stream) {
+-     this(stream, null);
+-  }
+-  /** Constructor with InputStream and supplied encoding */
+-  public HttpParser(java.io.InputStream stream, String encoding) {
+-    try { jj_input_stream = new SimpleCharStream(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
+-    token_source = new HttpParserTokenManager(jj_input_stream);
+-    token = new Token();
+-    jj_ntk = -1;
+-    jj_gen = 0;
+-    for (int i = 0; i < 3; i++) jj_la1[i] = -1;
+-  }
+-
+-  /** Reinitialise. */
+-  public void ReInit(java.io.InputStream stream) {
+-     ReInit(stream, null);
+-  }
+-  /** Reinitialise. */
+-  public void ReInit(java.io.InputStream stream, String encoding) {
+-    try { jj_input_stream.ReInit(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
+-    token_source.ReInit(jj_input_stream);
+-    token = new Token();
+-    jj_ntk = -1;
+-    jjtree.reset();
+-    jj_gen = 0;
+-    for (int i = 0; i < 3; i++) jj_la1[i] = -1;
+-  }
+-
+-  /** Constructor. */
+-  public HttpParser(java.io.Reader stream) {
+-    jj_input_stream = new SimpleCharStream(stream, 1, 1);
+-    token_source = new HttpParserTokenManager(jj_input_stream);
+-    token = new Token();
+-    jj_ntk = -1;
+-    jj_gen = 0;
+-    for (int i = 0; i < 3; i++) jj_la1[i] = -1;
+-  }
+-
+-  /** Reinitialise. */
+-  public void ReInit(java.io.Reader stream) {
+-    jj_input_stream.ReInit(stream, 1, 1);
+-    token_source.ReInit(jj_input_stream);
+-    token = new Token();
+-    jj_ntk = -1;
+-    jjtree.reset();
+-    jj_gen = 0;
+-    for (int i = 0; i < 3; i++) jj_la1[i] = -1;
+-  }
+-
+-  /** Constructor with generated Token Manager. */
+-  public HttpParser(HttpParserTokenManager tm) {
+-    token_source = tm;
+-    token = new Token();
+-    jj_ntk = -1;
+-    jj_gen = 0;
+-    for (int i = 0; i < 3; i++) jj_la1[i] = -1;
+-  }
+-
+-  /** Reinitialise. */
+-  public void ReInit(HttpParserTokenManager tm) {
+-    token_source = tm;
+-    token = new Token();
+-    jj_ntk = -1;
+-    jjtree.reset();
+-    jj_gen = 0;
+-    for (int i = 0; i < 3; i++) jj_la1[i] = -1;
+-  }
+-
+-  private Token jj_consume_token(int kind) throws ParseException {
+-    Token oldToken;
+-    if ((oldToken = token).next != null) token = token.next;
+-    else token = token.next = token_source.getNextToken();
+-    jj_ntk = -1;
+-    if (token.kind == kind) {
+-      jj_gen++;
+-      return token;
++
++
++    // Skip any LWS and return the next char
++    private static int skipLws(StringReader input, boolean withReset)
++            throws IOException {
++
++        if (withReset) {
++            input.mark(1);
++        }
++        int c = input.read();
++
++        while (c == 32 || c == 9 || c == 10 || c == 13) {
++            if (withReset) {
++                input.mark(1);
++            }
++            c = input.read();
++        }
++
++        if (withReset) {
++            input.reset();
++        }
++        return c;
+     }
+-    token = oldToken;
+-    jj_kind = kind;
+-    throw generateParseException();
+-  }
+-
+-
+-/** Get the next Token. */
+-  final public Token getNextToken() {
+-    if (token.next != null) token = token.next;
+-    else token = token.next = token_source.getNextToken();
+-    jj_ntk = -1;
+-    jj_gen++;
+-    return token;
+-  }
+-
+-/** Get the specific Token. */
+-  final public Token getToken(int index) {
+-    Token t = token;
+-    for (int i = 0; i < index; i++) {
+-      if (t.next != null) t = t.next;
+-      else t = t.next = token_source.getNextToken();
++
++    private static SkipConstantResult skipConstant(StringReader input,
++            String constant) throws IOException {
++        int len = constant.length();
++
++        int c = skipLws(input, false);
++
++        for (int i = 0; i < len; i++) {
++            if (i == 0 && c == -1) {
++                return SkipConstantResult.EOF;
++            }
++            if (c != constant.charAt(i)) {
++                input.skip(-(i + 1));
++                return SkipConstantResult.NOT_FOUND;
++            }
++            if (i != (len - 1)) {
++                c = input.read();
++            }
++        }
++        return SkipConstantResult.FOUND;
+     }
+-    return t;
+-  }
+-
+-  private int jj_ntk() {
+-    if ((jj_nt=token.next) == null)
+-      return (jj_ntk = (token.next=token_source.getNextToken()).kind);
+-    else
+-      return (jj_ntk = jj_nt.kind);
+-  }
+-
+-  private java.util.List<int[]> jj_expentries = new java.util.ArrayList<int[]>();
+-  private int[] jj_expentry;
+-  private int jj_kind = -1;
+-
+-  /** Generate ParseException. */
+-  public ParseException generateParseException() {
+-    jj_expentries.clear();
+-    boolean[] la1tokens = new boolean[15];
+-    if (jj_kind >= 0) {
+-      la1tokens[jj_kind] = true;
+-      jj_kind = -1;
++
++    /**
++     * @return  the token if one was found, the empty string if no data was
++     *          available to read or <code>null</code> if data other than a
++     *          token was found
++     */
++    private static String readToken(StringReader input) throws IOException {
++        StringBuilder result = new StringBuilder();
++
++        int c = skipLws(input, false);
++
++        while (c != -1 && isToken(c)) {
++            result.append((char) c);
++            c = input.read();
++        }
++        // Skip back so non-token character is available for next read
++        input.skip(-1);
++
++        if (c != -1 && result.length() == 0) {
++            return null;
++        } else {
++            return result.toString();
++        }
+     }
+-    for (int i = 0; i < 3; i++) {
+-      if (jj_la1[i] == jj_gen) {
+-        for (int j = 0; j < 32; j++) {
+-          if ((jj_la1_0[i] & (1<<j)) != 0) {
+-            la1tokens[j] = true;
+-          }
+-        }
+-      }
++
++    /**
++     * @return the quoted string if one was found, null if data other than a
++     *         quoted string was found or null if the end of data was reached
++     *         before the quoted string was terminated
++     */
++    private static String readQuotedString(StringReader input,
++            boolean returnQuoted) throws IOException {
++
++        int c = skipLws(input, false);
++
++        if (c != '"') {
++            return null;
++        }
++
++        StringBuilder result = new StringBuilder();
++        if (returnQuoted) {
++            result.append('\"');
++        }
++        c = input.read();
++
++        while (c != '"') {
++            if (c == -1) {
++                return null;
++            } else if (c == '\\') {
++                c = input.read();
++                if (returnQuoted) {
++                    result.append('\\');
++                }
++                result.append(c);
++            } else {
++                result.append((char) c);
++            }
++            c = input.read();
++        }
++        if (returnQuoted) {
++            result.append('\"');
++        }
++
++        return result.toString();
+     }
+-    for (int i = 0; i < 15; i++) {
+-      if (la1tokens[i]) {
+-        jj_expentry = new int[1];
+-        jj_expentry[0] = i;
+-        jj_expentries.add(jj_expentry);
+-      }
++
++    private static String readTokenOrQuotedString(StringReader input,
++            boolean returnQuoted) throws IOException {
++
++        // Go back so first non-LWS character is available to be read again
++        int c = skipLws(input, true);
++
++        if (c == '"') {
++            return readQuotedString(input, returnQuoted);
++        } else {
++            return readToken(input);
++        }
+     }
+-    int[][] exptokseq = new int[jj_expentries.size()][];
+-    for (int i = 0; i < jj_expentries.size(); i++) {
+-      exptokseq[i] = jj_expentries.get(i);
++
++    /**
++     * Token can be read unambiguously with or without surrounding quotes so
++     * this parsing method for token permits optional surrounding double quotes.
++     * This is not defined in any RFC. It is a special case to handle data from
++     * buggy clients (known buggy clients for DIGEST auth include Microsoft IE 8
++     * & 9, Apple Safari for OSX and iOS) that add quotes to values that
++     * should be tokens.
++     *
++     * @return the token if one was found, null if data other than a token or
++     *         quoted token was found or null if the end of data was reached
++     *         before a quoted token was terminated
++     */
++    private static String readQuotedToken(StringReader input)
++            throws IOException {
++
++        StringBuilder result = new StringBuilder();
++        boolean quoted = false;
++
++        int c = skipLws(input, false);
++
++        if (c == '"') {
++            quoted = true;
++        } else if (c == -1 || !isToken(c)) {
++            return null;
++        } else {
++            result.append((char) c);
++        }
++        c = input.read();
++
++        while (c != -1 && isToken(c)) {
++            result.append((char) c);
++            c = input.read();
++        }
++
++        if (quoted) {
++            if (c != '"') {
++                return null;
++            }
++        } else {
++            // Skip back so non-token character is available for next read
++            input.skip(-1);
++        }
++
++        if (c != -1 && result.length() == 0) {
++            return null;
++        } else {
++            return result.toString();
++        }
+     }
+-    return new ParseException(token, exptokseq, tokenImage);
+-  }
+ 
+-  /** Enable tracing. */
+-  final public void enable_tracing() {
+-  }
++    /**
++     * LHEX can be read unambiguously with or without surrounding quotes so this
++     * parsing method for LHEX permits optional surrounding double quotes. Some
++     * buggy clients (libwww-perl for DIGEST auth) are known to send quoted LHEX
++     * when the specification requires just LHEX.
++     *
++     * <p>
++     * LHEX are, literally, lower-case hexadecimal digits. This implementation
++     * allows for upper-case digits as well, converting the returned value to
++     * lower-case.
++     *
++     * @return  the sequence of LHEX (minus any surrounding quotes) if any was
++     *          found, or <code>null</code> if data other LHEX was found
++     */
++    private static String readLhex(StringReader input)
++            throws IOException {
++
++        StringBuilder result = new StringBuilder();
++        boolean quoted = false;
++
++        int c = skipLws(input, false);
++
++        if (c == '"') {
++            quoted = true;
++        } else if (c == -1 || !isHex(c)) {
++            return null;
++        } else {
++            if ('A' <= c && c <= 'F') {
++                c -= ('A' - 'a');
++            }
++            result.append((char) c);
++        }
++        c = input.read();
++
++        while (c != -1 && isHex(c)) {
++            if ('A' <= c && c <= 'F') {
++                c -= ('A' - 'a');
++            }
++            result.append((char) c);
++            c = input.read();
++        }
+ 
+-  /** Disable tracing. */
+-  final public void disable_tracing() {
+-  }
++        if (quoted) {
++            if (c != '"') {
++                return null;
++            }
++        } else {
++            // Skip back so non-hex character is available for next read
++            input.skip(-1);
++        }
+ 
++        if (c != -1 && result.length() == 0) {
++            return null;
++        } else {
++            return result.toString();
++        }
++    }
++
++    private static enum SkipConstantResult {
++        FOUND,
++        NOT_FOUND,
++        EOF
++    }
+ }
+diff --git a/java/org/apache/tomcat/util/http/parser/HttpParser.jjt b/java/org/apache/tomcat/util/http/parser/HttpParser.jjt
+deleted file mode 100644
+index 957155d..0000000
+--- a/java/org/apache/tomcat/util/http/parser/HttpParser.jjt
++++ /dev/null
+@@ -1,141 +0,0 @@
+-/*
+- * 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.
+- */
+-
+-/*
+- * Parsing HTTP headers as per RFC2616 is not always as simple as it first
+- * appears. For headers that only use tokens the simple approach will normally
+- * be sufficient. However, for the other headers, while simple code meets 99.9%
+- * of cases, there are often some edge cases that make things far more
+- * complicated.
+- *
+- * The purpose of this parser is to let the parser worry about the edge cases.
+- * It provides strict parsing of HTTP header values assuming that wrapped header
+- * lines have already been unwrapped. (The Tomcat header processing code does
+- * the unwrapping.)
+- *
+- * Provides parsing of the following HTTP header values as per RFC 2616:
+- * - Content-Type
+- *     Note: The parser tolerates invalid parameters that do not include a
+- *           value. These parameters are available in the Parser node collection
+- *           but will be skipped by the various toString() methods. See BZ 53353
+- *           for an example.
+- *
+- * Support for additional headers will be provided as required.
+- */
+-
+-/* Option Declaration */
+-options
+-{
+-    STATIC=false;
+-    NODE_PREFIX="Ast";
+-    MULTI=true;
+-    NODE_DEFAULT_VOID=true;
+-    JAVA_UNICODE_ESCAPE=false;
+-    UNICODE_INPUT=true;
+-    BUILD_NODE_FILES=true;
+-}
+-
+-
+-/* Parser Declaration */
+-PARSER_BEGIN( HttpParser )
+-package org.apache.tomcat.util.http.parser;
+-public class HttpParser
+-{
+-}
+-PARSER_END( HttpParser )
+-
+-
+-/*
+- * Content-Type
+- */
+-AstMediaType MediaType() #MediaType : {}
+-{
+-    Type() <FORWARD_SLASH> SubType() ( <SEMI_COLON> Parameter())* <EOF> {
+-        return jjtThis;
+-    }
+-}
+-
+-void Type() #Type : { Token t = null; }
+-{
+-    t=<HTTP_TOKEN> { jjtThis.jjtSetValue(t.image.trim()); }
+-}
+-
+-void SubType() #SubType :{ Token t = null; }
+-{
+-    t=<HTTP_TOKEN> { jjtThis.jjtSetValue(t.image.trim()); }
+-}
+-
+-void Parameter() #Parameter : {}
+-{
+-    Attribute() (<EQUALS> Value())?
+-}
+-
+-void Attribute() # Attribute : { Token t = null; }
+-{
+-    t=<HTTP_TOKEN> { jjtThis.jjtSetValue(t.image.trim()); }
+-}
+-
+-void Value() #Value : { Token t = null; }
+-{
+-    t=<HTTP_TOKEN> { jjtThis.jjtSetValue(t.image.trim()); } | t=<QUOTED_STRING> { jjtThis.jjtSetValue(t.image.trim()); }
+-}
+-
+-
+-<DEFAULT> TOKEN:
+-{
+-  < SEMI_COLON :    ";" >
+-| < EQUALS :        "=" >
+-| < FORWARD_SLASH : "/" >
+-| < SP :            " " >
+-| < HT :            "\t" >
+-| < LWS :           (<HT> | <SP>)+ >
+-| < HTTP_TOKEN :    (<LWS>)? (<HTTP_TOKEN_CHAR>)+ (<LWS>)? >
+-| < QUOTED_STRING : (<LWS>)? <START_QUOTE> <QUOTED_TEXT> <END_QUOTE> (<LWS>)? >
+-| < START_QUOTE :   "\"" > : IN_QUOTED_TEXT
+-| < #HTTP_TOKEN_CHAR:
+-     [
+-     "\u0021",
+-     "\u0023"-"\u0027",
+-     "\u002a"-"\u002b",
+-     "\u002d"-"\u002e",
+-     "\u0030"-"\u0039",
+-     "\u0041"-"\u005a",
+-     "\u005e"-"\u007a",
+-     "\u007c",
+-     "\u007e"
+-     ]
+-  >
+-}
+-
+-<IN_QUOTED_TEXT> TOKEN :
+-{
+-  < #QUOTED_TEXT :   (<QUOTED_TEXT_CHAR> | ("\\" <CHAR> ))* >
+-| < #END_QUOTE :     "\"" >
+-| < #QUOTED_TEXT_CHAR:
+-     [
+-     "\u0009",
+-     "\u0020"-"\u0021",
+-     "\u0023"-"\u005b",
+-     "\u005d"-"\u00ff"
+-     ]
+-  >
+-| < #CHAR:
+-     [
+-     "\u0000"-"\u007f"
+-     ]
+-  >
+-}
+\ No newline at end of file
+diff --git a/java/org/apache/tomcat/util/http/parser/HttpParserConstants.java b/java/org/apache/tomcat/util/http/parser/HttpParserConstants.java
+deleted file mode 100644
+index 74379e1..0000000
+--- a/java/org/apache/tomcat/util/http/parser/HttpParserConstants.java
++++ /dev/null
+@@ -1,66 +0,0 @@
+-/* Generated By:JJTree&JavaCC: Do not edit this line. HttpParserConstants.java */
+-package org.apache.tomcat.util.http.parser;
+-
+-
+-/**
+- * Token literal values and constants.
+- * Generated by org.javacc.parser.OtherFilesGen#start()
+- */
+-public interface HttpParserConstants {
+-
+-  /** End of File. */
+-  int EOF = 0;
+-  /** RegularExpression Id. */
+-  int SEMI_COLON = 1;
+-  /** RegularExpression Id. */
+-  int EQUALS = 2;
+-  /** RegularExpression Id. */
+-  int FORWARD_SLASH = 3;
+-  /** RegularExpression Id. */
+-  int SP = 4;
+-  /** RegularExpression Id. */
+-  int HT = 5;
+-  /** RegularExpression Id. */
+-  int LWS = 6;
+-  /** RegularExpression Id. */
+-  int HTTP_TOKEN = 7;
+-  /** RegularExpression Id. */
+-  int QUOTED_STRING = 8;
+-  /** RegularExpression Id. */
+-  int START_QUOTE = 9;
+-  /** RegularExpression Id. */
+-  int HTTP_TOKEN_CHAR = 10;
+-  /** RegularExpression Id. */
+-  int QUOTED_TEXT = 11;
+-  /** RegularExpression Id. */
+-  int END_QUOTE = 12;
+-  /** RegularExpression Id. */
+-  int QUOTED_TEXT_CHAR = 13;
+-  /** RegularExpression Id. */
+-  int CHAR = 14;
+-
+-  /** Lexical state. */
+-  int DEFAULT = 0;
+-  /** Lexical state. */
+-  int IN_QUOTED_TEXT = 1;
+-
+-  /** Literal token values. */
+-  String[] tokenImage = {
+-    "<EOF>",
+-    "\";\"",
+-    "\"=\"",
+-    "\"/\"",
+-    "\" \"",
+-    "\"\\t\"",
+-    "<LWS>",
+-    "<HTTP_TOKEN>",
+-    "<QUOTED_STRING>",
+-    "\"\\\"\"",
+-    "<HTTP_TOKEN_CHAR>",
+-    "<QUOTED_TEXT>",
+-    "\"\\\"\"",
+-    "<QUOTED_TEXT_CHAR>",
+-    "<CHAR>",
+-  };
+-
+-}
+diff --git a/java/org/apache/tomcat/util/http/parser/HttpParserTokenManager.java b/java/org/apache/tomcat/util/http/parser/HttpParserTokenManager.java
+deleted file mode 100644
+index c155a46..0000000
+--- a/java/org/apache/tomcat/util/http/parser/HttpParserTokenManager.java
++++ /dev/null
+@@ -1,465 +0,0 @@
+-/* Generated By:JJTree&JavaCC: Do not edit this line. HttpParserTokenManager.java */
+-package org.apache.tomcat.util.http.parser;
+-
+-/** Token Manager. */
+- at SuppressWarnings("all") // Ignore warnings in generated code
+-public class HttpParserTokenManager implements HttpParserConstants
+-{
+-
+-  /** Debug output. */
+-  public  java.io.PrintStream debugStream = System.out;
+-  /** Set debug output. */
+-  public  void setDebugStream(java.io.PrintStream ds) { debugStream = ds; }
+-private final int jjStopStringLiteralDfa_0(int pos, long active0)
+-{
+-   switch (pos)
+-   {
+-      default :
+-         return -1;
+-   }
+-}
+-private final int jjStartNfa_0(int pos, long active0)
+-{
+-   return jjMoveNfa_0(jjStopStringLiteralDfa_0(pos, active0), pos + 1);
+-}
+-private int jjStopAtPos(int pos, int kind)
+-{
+-   jjmatchedKind = kind;
+-   jjmatchedPos = pos;
+-   return pos + 1;
+-}
+-private int jjMoveStringLiteralDfa0_0()
+-{
+-   switch(curChar)
+-   {
+-      case 9:
+-         return jjStartNfaWithStates_0(0, 5, 12);
+-      case 32:
+-         return jjStartNfaWithStates_0(0, 4, 12);
+-      case 34:
+-         return jjStartNfaWithStates_0(0, 9, 13);
+-      case 47:
+-         return jjStopAtPos(0, 3);
+-      case 59:
+-         return jjStopAtPos(0, 1);
+-      case 61:
+-         return jjStopAtPos(0, 2);
+-      default :
+-         return jjMoveNfa_0(8, 0);
+-   }
+-}
+-private int jjStartNfaWithStates_0(int pos, int kind, int state)
+-{
+-   jjmatchedKind = kind;
+-   jjmatchedPos = pos;
+-   try { curChar = input_stream.readChar(); }
+-   catch(java.io.IOException e) { return pos + 1; }
+-   return jjMoveNfa_0(state, pos + 1);
+-}
+-static final long[] jjbitVec0 = {
+-   0x0L, 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL
+-};
+-private int jjMoveNfa_0(int startState, int curPos)
+-{
+-   int startsAt = 0;
+-   jjnewStateCnt = 12;
+-   int i = 1;
+-   jjstateSet[0] = startState;
+-   int kind = 0x7fffffff;
+-   for (;;)
+-   {
+-      if (++jjround == 0x7fffffff)
+-         ReInitRounds();
+-      if (curChar < 64)
+-      {
+-         long l = 1L << curChar;
+-         do
+-         {
+-            switch(jjstateSet[--i])
+-            {
+-               case 13:
+-                  if ((0xfffffffb00000200L & l) != 0L)
+-                     jjCheckNAddStates(0, 2);
+-                  else if (curChar == 34)
+-                  {
+-                     if (kind > 8)
+-                        kind = 8;
+-                     jjCheckNAdd(7);
+-                  }
+-                  break;
+-               case 12:
+-                  if ((0x3ff6cfa00000000L & l) != 0L)
+-                  {
+-                     if (kind > 7)
+-                        kind = 7;
+-                     jjCheckNAddTwoStates(0, 1);
+-                  }
+-                  else if ((0x100000200L & l) != 0L)
+-                     jjCheckNAddTwoStates(11, 2);
+-                  else if (curChar == 34)
+-                     jjCheckNAddStates(0, 2);
+-                  if ((0x100000200L & l) != 0L)
+-                     jjCheckNAddTwoStates(10, 0);
+-                  if ((0x100000200L & l) != 0L)
+-                  {
+-                     if (kind > 6)
+-                        kind = 6;
+-                     jjCheckNAdd(9);
+-                  }
+-                  break;
+-               case 8:
+-                  if ((0x3ff6cfa00000000L & l) != 0L)
+-                  {
+-                     if (kind > 7)
+-                        kind = 7;
+-                     jjCheckNAddTwoStates(0, 1);
+-                  }
+-                  else if ((0x100000200L & l) != 0L)
+-                  {
+-                     if (kind > 6)
+-                        kind = 6;
+-                     jjCheckNAddStates(3, 7);
+-                  }
+-                  else if (curChar == 34)
+-                     jjCheckNAddStates(0, 2);
+-                  break;
+-               case 0:
+-                  if ((0x3ff6cfa00000000L & l) == 0L)
+-                     break;
+-                  if (kind > 7)
+-                     kind = 7;
+-                  jjCheckNAddTwoStates(0, 1);
+-                  break;
+-               case 1:
+-                  if ((0x100000200L & l) == 0L)
+-                     break;
+-                  if (kind > 7)
+-                     kind = 7;
+-                  jjCheckNAdd(1);
+-                  break;
+-               case 2:
+-                  if (curChar == 34)
+-                     jjCheckNAddStates(0, 2);
+-                  break;
+-               case 3:
+-                  if ((0xfffffffb00000200L & l) != 0L)
+-                     jjCheckNAddStates(0, 2);
+-                  break;
+-               case 5:
+-                  jjCheckNAddStates(0, 2);
+-                  break;
+-               case 6:
+-                  if (curChar != 34)
+-                     break;
+-                  if (kind > 8)
+-                     kind = 8;
+-                  jjCheckNAdd(7);
+-                  break;
+-               case 7:
+-                  if ((0x100000200L & l) == 0L)
+-                     break;
+-                  if (kind > 8)
+-                     kind = 8;
+-                  jjCheckNAdd(7);
+-                  break;
+-               case 9:
+-                  if ((0x100000200L & l) == 0L)
+-                     break;
+-                  if (kind > 6)
+-                     kind = 6;
+-                  jjCheckNAdd(9);
+-                  break;
+-               case 10:
+-                  if ((0x100000200L & l) != 0L)
+-                     jjCheckNAddTwoStates(10, 0);
+-                  break;
+-               case 11:
+-                  if ((0x100000200L & l) != 0L)
+-                     jjCheckNAddTwoStates(11, 2);
+-                  break;
+-               default : break;
+-            }
+-         } while(i != startsAt);
+-      }
+-      else if (curChar < 128)
+-      {
+-         long l = 1L << (curChar & 077);
+-         do
+-         {
+-            switch(jjstateSet[--i])
+-            {
+-               case 13:
+-                  if ((0xffffffffefffffffL & l) != 0L)
+-                     jjCheckNAddStates(0, 2);
+-                  else if (curChar == 92)
+-                     jjstateSet[jjnewStateCnt++] = 5;
+-                  break;
+-               case 12:
+-               case 0:
+-                  if ((0x57ffffffc7fffffeL & l) == 0L)
+-                     break;
+-                  if (kind > 7)
+-                     kind = 7;
+-                  jjCheckNAddTwoStates(0, 1);
+-                  break;
+-               case 8:
+-                  if ((0x57ffffffc7fffffeL & l) == 0L)
+-                     break;
+-                  if (kind > 7)
+-                     kind = 7;
+-                  jjCheckNAddTwoStates(0, 1);
+-                  break;
+-               case 3:
+-                  if ((0xffffffffefffffffL & l) != 0L)
+-                     jjCheckNAddStates(0, 2);
+-                  break;
+-               case 4:
+-                  if (curChar == 92)
+-                     jjstateSet[jjnewStateCnt++] = 5;
+-                  break;
+-               case 5:
+-                  jjCheckNAddStates(0, 2);
+-                  break;
+-               default : break;
+-            }
+-         } while(i != startsAt);
+-      }
+-      else
+-      {
+-         int hiByte = (int)(curChar >> 8);
+-         int i1 = hiByte >> 6;
+-         long l1 = 1L << (hiByte & 077);
+-         int i2 = (curChar & 0xff) >> 6;
+-         long l2 = 1L << (curChar & 077);
+-         do
+-         {
+-            switch(jjstateSet[--i])
+-            {
+-               case 13:
+-               case 3:
+-                  if (jjCanMove_0(hiByte, i1, i2, l1, l2))
+-                     jjCheckNAddStates(0, 2);
+-                  break;
+-               default : break;
+-            }
+-         } while(i != startsAt);
+-      }
+-      if (kind != 0x7fffffff)
+-      {
+-         jjmatchedKind = kind;
+-         jjmatchedPos = curPos;
+-         kind = 0x7fffffff;
+-      }
+-      ++curPos;
+-      if ((i = jjnewStateCnt) == (startsAt = 12 - (jjnewStateCnt = startsAt)))
+-         return curPos;
+-      try { curChar = input_stream.readChar(); }
+-      catch(java.io.IOException e) { return curPos; }
+-   }
+-}
+-private int jjMoveStringLiteralDfa0_1()
+-{
+-   return 1;
+-}
+-static final int[] jjnextStates = {
+-   3, 4, 6, 9, 10, 0, 11, 2,
+-};
+-private static final boolean jjCanMove_0(int hiByte, int i1, int i2, long l1, long l2)
+-{
+-   switch(hiByte)
+-   {
+-      case 0:
+-         return ((jjbitVec0[i2] & l2) != 0L);
+-      default :
+-         return false;
+-   }
+-}
+-
+-/** Token literal values. */
+-public static final String[] jjstrLiteralImages = {
+-"", "\73", "\75", "\57", "\40", "\11", null, null, null, "\42", null, null,
+-null, null, null, };
+-
+-/** Lexer state names. */
+-public static final String[] lexStateNames = {
+-   "DEFAULT",
+-   "IN_QUOTED_TEXT",
+-};
+-
+-/** Lex State array. */
+-public static final int[] jjnewLexState = {
+-   -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1,
+-};
+-protected SimpleCharStream input_stream;
+-private final int[] jjrounds = new int[12];
+-private final int[] jjstateSet = new int[24];
+-protected char curChar;
+-/** Constructor. */
+-public HttpParserTokenManager(SimpleCharStream stream){
+-   if (SimpleCharStream.staticFlag)
+-      throw new Error("ERROR: Cannot use a static CharStream class with a non-static lexical analyzer.");
+-   input_stream = stream;
+-}
+-
+-/** Constructor. */
+-public HttpParserTokenManager(SimpleCharStream stream, int lexState){
+-   this(stream);
+-   SwitchTo(lexState);
+-}
+-
+-/** Reinitialise parser. */
+-public void ReInit(SimpleCharStream stream)
+-{
+-   jjmatchedPos = jjnewStateCnt = 0;
+-   curLexState = defaultLexState;
+-   input_stream = stream;
+-   ReInitRounds();
+-}
+-private void ReInitRounds()
+-{
+-   int i;
+-   jjround = 0x80000001;
+-   for (i = 12; i-- > 0;)
+-      jjrounds[i] = 0x80000000;
+-}
+-
+-/** Reinitialise parser. */
+-public void ReInit(SimpleCharStream stream, int lexState)
+-{
+-   ReInit(stream);
+-   SwitchTo(lexState);
+-}
+-
+-/** Switch to specified lex state. */
+-public void SwitchTo(int lexState)
+-{
+-   if (lexState >= 2 || lexState < 0)
+-      throw new TokenMgrError("Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.", TokenMgrError.INVALID_LEXICAL_STATE);
+-   else
+-      curLexState = lexState;
+-}
+-
+-protected Token jjFillToken()
+-{
+-   final Token t;
+-   final String curTokenImage;
+-   final int beginLine;
+-   final int endLine;
+-   final int beginColumn;
+-   final int endColumn;
+-   String im = jjstrLiteralImages[jjmatchedKind];
+-   curTokenImage = (im == null) ? input_stream.GetImage() : im;
+-   beginLine = input_stream.getBeginLine();
+-   beginColumn = input_stream.getBeginColumn();
+-   endLine = input_stream.getEndLine();
+-   endColumn = input_stream.getEndColumn();
+-   t = Token.newToken(jjmatchedKind, curTokenImage);
+-
+-   t.beginLine = beginLine;
+-   t.endLine = endLine;
+-   t.beginColumn = beginColumn;
+-   t.endColumn = endColumn;
+-
+-   return t;
+-}
+-
+-int curLexState = 0;
+-int defaultLexState = 0;
+-int jjnewStateCnt;
+-int jjround;
+-int jjmatchedPos;
+-int jjmatchedKind;
+-
+-/** Get the next Token. */
+-public Token getNextToken()
+-{
+-  Token matchedToken;
+-  int curPos = 0;
+-
+-  EOFLoop :
+-  for (;;)
+-  {
+-   try
+-   {
+-      curChar = input_stream.BeginToken();
+-   }
+-   catch(java.io.IOException e)
+-   {
+-      jjmatchedKind = 0;
+-      matchedToken = jjFillToken();
+-      return matchedToken;
+-   }
+-
+-   switch(curLexState)
+-   {
+-     case 0:
+-       jjmatchedKind = 0x7fffffff;
+-       jjmatchedPos = 0;
+-       curPos = jjMoveStringLiteralDfa0_0();
+-       break;
+-     case 1:
+-       jjmatchedKind = 0x7fffffff;
+-       jjmatchedPos = 0;
+-       curPos = jjMoveStringLiteralDfa0_1();
+-       break;
+-   }
+-     if (jjmatchedKind != 0x7fffffff)
+-     {
+-        if (jjmatchedPos + 1 < curPos)
+-           input_stream.backup(curPos - jjmatchedPos - 1);
+-           matchedToken = jjFillToken();
+-       if (jjnewLexState[jjmatchedKind] != -1)
+-         curLexState = jjnewLexState[jjmatchedKind];
+-           return matchedToken;
+-     }
+-     int error_line = input_stream.getEndLine();
+-     int error_column = input_stream.getEndColumn();
+-     String error_after = null;
+-     boolean EOFSeen = false;
+-     try { input_stream.readChar(); input_stream.backup(1); }
+-     catch (java.io.IOException e1) {
+-        EOFSeen = true;
+-        error_after = curPos <= 1 ? "" : input_stream.GetImage();
+-        if (curChar == '\n' || curChar == '\r') {
+-           error_line++;
+-           error_column = 0;
+-        }
+-        else
+-           error_column++;
+-     }
+-     if (!EOFSeen) {
+-        input_stream.backup(1);
+-        error_after = curPos <= 1 ? "" : input_stream.GetImage();
+-     }
+-     throw new TokenMgrError(EOFSeen, curLexState, error_line, error_column, error_after, curChar, TokenMgrError.LEXICAL_ERROR);
+-  }
+-}
+-
+-private void jjCheckNAdd(int state)
+-{
+-   if (jjrounds[state] != jjround)
+-   {
+-      jjstateSet[jjnewStateCnt++] = state;
+-      jjrounds[state] = jjround;
+-   }
+-}
+-private void jjAddStates(int start, int end)
+-{
+-   do {
+-      jjstateSet[jjnewStateCnt++] = jjnextStates[start];
+-   } while (start++ != end);
+-}
+-private void jjCheckNAddTwoStates(int state1, int state2)
+-{
+-   jjCheckNAdd(state1);
+-   jjCheckNAdd(state2);
+-}
+-
+-private void jjCheckNAddStates(int start, int end)
+-{
+-   do {
+-      jjCheckNAdd(jjnextStates[start]);
+-   } while (start++ != end);
+-}
+-
+-}
+diff --git a/java/org/apache/tomcat/util/http/parser/HttpParserTreeConstants.java b/java/org/apache/tomcat/util/http/parser/HttpParserTreeConstants.java
+deleted file mode 100644
+index 615c56c..0000000
+--- a/java/org/apache/tomcat/util/http/parser/HttpParserTreeConstants.java
++++ /dev/null
+@@ -1,23 +0,0 @@
+-/* Generated By:JavaCC: Do not edit this line. HttpParserTreeConstants.java Version 5.0 */
+-package org.apache.tomcat.util.http.parser;
+-
+-public interface HttpParserTreeConstants
+-{
+-  public int JJTMEDIATYPE = 0;
+-  public int JJTTYPE = 1;
+-  public int JJTSUBTYPE = 2;
+-  public int JJTPARAMETER = 3;
+-  public int JJTATTRIBUTE = 4;
+-  public int JJTVALUE = 5;
+-
+-
+-  public String[] jjtNodeName = {
+-    "MediaType",
+-    "Type",
+-    "SubType",
+-    "Parameter",
+-    "Attribute",
+-    "Value",
+-  };
+-}
+-/* JavaCC - OriginalChecksum=f4745df783771bdbdddd3be661f1c089 (do not edit this line) */
+diff --git a/java/org/apache/tomcat/util/http/parser/JJTHttpParserState.java b/java/org/apache/tomcat/util/http/parser/JJTHttpParserState.java
+deleted file mode 100644
+index 5b20ba3..0000000
+--- a/java/org/apache/tomcat/util/http/parser/JJTHttpParserState.java
++++ /dev/null
+@@ -1,124 +0,0 @@
+-/* Generated By:JavaCC: Do not edit this line. JJTHttpParserState.java Version 5.0 */
+-package org.apache.tomcat.util.http.parser;
+-
+- at SuppressWarnings("all") // Ignore warnings in generated code
+-public class JJTHttpParserState {
+-  private java.util.List<Node> nodes;
+-  private java.util.List<Integer> marks;
+-
+-  private int sp;        // number of nodes on stack
+-  private int mk;        // current mark
+-  private boolean node_created;
+-
+-  public JJTHttpParserState() {
+-    nodes = new java.util.ArrayList<Node>();
+-    marks = new java.util.ArrayList<Integer>();
+-    sp = 0;
+-    mk = 0;
+-  }
+-
+-  /* Determines whether the current node was actually closed and
+-     pushed.  This should only be called in the final user action of a
+-     node scope.  */
+-  public boolean nodeCreated() {
+-    return node_created;
+-  }
+-
+-  /* Call this to reinitialize the node stack.  It is called
+-     automatically by the parser's ReInit() method. */
+-  public void reset() {
+-    nodes.clear();
+-    marks.clear();
+-    sp = 0;
+-    mk = 0;
+-  }
+-
+-  /* Returns the root node of the AST.  It only makes sense to call
+-     this after a successful parse. */
+-  public Node rootNode() {
+-    return nodes.get(0);
+-  }
+-
+-  /* Pushes a node on to the stack. */
+-  public void pushNode(Node n) {
+-    nodes.add(n);
+-    ++sp;
+-  }
+-
+-  /* Returns the node on the top of the stack, and remove it from the
+-     stack.  */
+-  public Node popNode() {
+-    if (--sp < mk) {
+-      mk = marks.remove(marks.size()-1);
+-    }
+-    return nodes.remove(nodes.size()-1);
+-  }
+-
+-  /* Returns the node currently on the top of the stack. */
+-  public Node peekNode() {
+-    return nodes.get(nodes.size()-1);
+-  }
+-
+-  /* Returns the number of children on the stack in the current node
+-     scope. */
+-  public int nodeArity() {
+-    return sp - mk;
+-  }
+-
+-
+-  public void clearNodeScope(Node n) {
+-    while (sp > mk) {
+-      popNode();
+-    }
+-    mk = marks.remove(marks.size()-1);
+-  }
+-
+-
+-  public void openNodeScope(Node n) {
+-    marks.add(mk);
+-    mk = sp;
+-    n.jjtOpen();
+-  }
+-
+-
+-  /* A definite node is constructed from a specified number of
+-     children.  That number of nodes are popped from the stack and
+-     made the children of the definite node.  Then the definite node
+-     is pushed on to the stack. */
+-  public void closeNodeScope(Node n, int num) {
+-    mk = marks.remove(marks.size()-1);
+-    while (num-- > 0) {
+-      Node c = popNode();
+-      c.jjtSetParent(n);
+-      n.jjtAddChild(c, num);
+-    }
+-    n.jjtClose();
+-    pushNode(n);
+-    node_created = true;
+-  }
+-
+-
+-  /* A conditional node is constructed if its condition is true.  All
+-     the nodes that have been pushed since the node was opened are
+-     made children of the conditional node, which is then pushed
+-     on to the stack.  If the condition is false the node is not
+-     constructed and they are left on the stack. */
+-  public void closeNodeScope(Node n, boolean condition) {
+-    if (condition) {
+-      int a = nodeArity();
+-      mk = marks.remove(marks.size()-1);
+-      while (a-- > 0) {
+-        Node c = popNode();
+-        c.jjtSetParent(n);
+-        n.jjtAddChild(c, a);
+-      }
+-      n.jjtClose();
+-      pushNode(n);
+-      node_created = true;
+-    } else {
+-      mk = marks.remove(marks.size()-1);
+-      node_created = false;
+-    }
+-  }
+-}
+-/* JavaCC - OriginalChecksum=ddd6baff0afccf0891566804931f534d (do not edit this line) */
+diff --git a/java/org/apache/tomcat/util/http/parser/MediaType.java b/java/org/apache/tomcat/util/http/parser/MediaType.java
+new file mode 100644
+index 0000000..ce60a14
+--- /dev/null
++++ b/java/org/apache/tomcat/util/http/parser/MediaType.java
+@@ -0,0 +1,125 @@
++/*
++ *  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.tomcat.util.http.parser;
++
++import java.util.LinkedHashMap;
++import java.util.Locale;
++import java.util.Map;
++
++public class MediaType {
++
++    private final String type;
++    private final String subtype;
++    private final LinkedHashMap<String,String> parameters;
++    private final String charset;
++    private volatile String noCharset;
++    private volatile String withCharset;
++
++    protected MediaType(String type, String subtype,
++            LinkedHashMap<String,String> parameters) {
++        this.type = type;
++        this.subtype = subtype;
++        this.parameters = parameters;
++
++        String cs = parameters.get("charset");
++        if (cs != null && cs.length() > 0 &&
++                cs.charAt(0) == '"') {
++            cs = HttpParser.unquote(cs);
++        }
++        this.charset = cs;
++    }
++
++    public String getType() {
++        return type;
++    }
++
++    public String getSubtype() {
++        return subtype;
++    }
++
++    public String getCharset() {
++        return charset;
++    }
++
++    public int getParameterCount() {
++        return parameters.size();
++    }
++
++    public String getParameterValue(String parameter) {
++        return parameters.get(parameter.toLowerCase(Locale.ENGLISH));
++    }
++
++    @Override
++    public String toString() {
++        if (withCharset == null) {
++            synchronized (this) {
++                if (withCharset == null) {
++                    StringBuilder result = new StringBuilder();
++                    result.append(type);
++                    result.append('/');
++                    result.append(subtype);
++                    for (Map.Entry<String, String> entry : parameters.entrySet()) {
++                        String value = entry.getValue();
++                        if (value == null || value.length() == 0) {
++                            continue;
++                        }
++                        result.append(';');
++                        // Workaround for Adobe Read 9 plug-in on IE bug
++                        // Can be removed after 26 June 2013 (EOL of Reader 9)
++                        // See BZ 53814
++                        result.append(' ');
++                        result.append(entry.getKey());
++                        result.append('=');
++                        result.append(value);
++                    }
++
++                    withCharset = result.toString();
++                }
++            }
++        }
++        return withCharset;
++    }
++
++    public String toStringNoCharset() {
++        if (noCharset == null) {
++            synchronized (this) {
++                if (noCharset == null) {
++                    StringBuilder result = new StringBuilder();
++                    result.append(type);
++                    result.append('/');
++                    result.append(subtype);
++                    for (Map.Entry<String, String> entry : parameters.entrySet()) {
++                        if (entry.getKey().equalsIgnoreCase("charset")) {
++                            continue;
++                        }
++                        result.append(';');
++                        // Workaround for Adobe Read 9 plug-in on IE bug
++                        // Can be removed after 26 June 2013 (EOL of Reader 9)
++                        // See BZ 53814
++                        result.append(' ');
++                        result.append(entry.getKey());
++                        result.append('=');
++                        result.append(entry.getValue());
++                    }
++
++                    noCharset = result.toString();
++                }
++            }
++        }
++        return noCharset;
++    }
++}
+diff --git a/java/org/apache/tomcat/util/http/parser/MediaTypeCache.java b/java/org/apache/tomcat/util/http/parser/MediaTypeCache.java
+new file mode 100644
+index 0000000..efed85c
+--- /dev/null
++++ b/java/org/apache/tomcat/util/http/parser/MediaTypeCache.java
+@@ -0,0 +1,65 @@
++/*
++ *  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.tomcat.util.http.parser;
++
++import java.io.IOException;
++import java.io.StringReader;
++
++import org.apache.tomcat.util.collections.ConcurrentCache;
++
++/**
++ * Caches the results of parsing content-type headers.
++ */
++public class MediaTypeCache {
++
++    private final ConcurrentCache<String,String[]> cache;
++
++    public MediaTypeCache(int size) {
++        cache = new ConcurrentCache<String,String[]>(size);
++    }
++
++    /**
++     * Looks in the cache and returns the cached value if one is present. If no
++     * match exists in the cache, a new parser is created, the input parsed and
++     * the results placed in the cache and returned to the user.
++     *
++     * @param input The content-type header value to parse
++     * @return      The results are provided as a two element String array. The
++     *                  first element is the media type less the charset and
++     *                  the second element is the charset
++     */
++    public String[] parse(String input) {
++        String[] result = cache.get(input);
++
++        if (result != null) {
++            return result;
++        }
++
++        MediaType m = null;
++        try {
++            m = HttpParser.parseMediaType(new StringReader(input));
++        } catch (IOException e) {
++            // Ignore - return null
++        }
++        if (m != null) {
++            result = new String[] {m.toStringNoCharset(), m.getCharset()};
++            cache.put(input, result);
++        }
++
++        return result;
++    }
++}
+diff --git a/java/org/apache/tomcat/util/http/parser/Node.java b/java/org/apache/tomcat/util/http/parser/Node.java
+deleted file mode 100644
+index 6aaaba4..0000000
+--- a/java/org/apache/tomcat/util/http/parser/Node.java
++++ /dev/null
+@@ -1,59 +0,0 @@
+-/*
+- *  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.tomcat.util.http.parser;
+-
+-/* All AST nodes must implement this interface.  It provides basic
+- machinery for constructing the parent and child relationships
+- between nodes. */
+-
+-public interface Node {
+-
+-    /**
+-     * This method is called after the node has been made the current node. It
+-     * indicates that child nodes can now be added to it.
+-     */
+-    public void jjtOpen();
+-
+-    /**
+-     * This method is called after all the child nodes have been added.
+-     */
+-    public void jjtClose();
+-
+-    /**
+-     * This pair of methods are used to inform the node of its parent.
+-     */
+-    public void jjtSetParent(Node n);
+-
+-    public Node jjtGetParent();
+-
+-    /**
+-     * This method tells the node to add its argument to the node's list of
+-     * children.
+-     */
+-    public void jjtAddChild(Node n, int i);
+-
+-    /**
+-     * This method returns a child node. The children are numbered from zero,
+-     * left to right.
+-     */
+-    public Node jjtGetChild(int i);
+-
+-    /** Return the number of children the node has. */
+-    public int jjtGetNumChildren();
+-
+-    public Object jjtGetValue();
+-}
+diff --git a/java/org/apache/tomcat/util/http/parser/ParseException.java b/java/org/apache/tomcat/util/http/parser/ParseException.java
+deleted file mode 100644
+index 4e0e2df..0000000
+--- a/java/org/apache/tomcat/util/http/parser/ParseException.java
++++ /dev/null
+@@ -1,187 +0,0 @@
+-/* Generated By:JavaCC: Do not edit this line. ParseException.java Version 5.0 */
+-/* JavaCCOptions:KEEP_LINE_COL=null */
+-package org.apache.tomcat.util.http.parser;
+-
+-/**
+- * This exception is thrown when parse errors are encountered.
+- * You can explicitly create objects of this exception type by
+- * calling the method generateParseException in the generated
+- * parser.
+- *
+- * You can modify this class to customize your error reporting
+- * mechanisms so long as you retain the public fields.
+- */
+-public class ParseException extends Exception {
+-
+-  /**
+-   * The version identifier for this Serializable class.
+-   * Increment only if the <i>serialized</i> form of the
+-   * class changes.
+-   */
+-  private static final long serialVersionUID = 1L;
+-
+-  /**
+-   * This constructor is used by the method "generateParseException"
+-   * in the generated parser.  Calling this constructor generates
+-   * a new object of this type with the fields "currentToken",
+-   * "expectedTokenSequences", and "tokenImage" set.
+-   */
+-  public ParseException(Token currentTokenVal,
+-                        int[][] expectedTokenSequencesVal,
+-                        String[] tokenImageVal
+-                       )
+-  {
+-    super(initialise(currentTokenVal, expectedTokenSequencesVal, tokenImageVal));
+-    currentToken = currentTokenVal;
+-    expectedTokenSequences = expectedTokenSequencesVal;
+-    tokenImage = tokenImageVal;
+-  }
+-
+-  /**
+-   * The following constructors are for use by you for whatever
+-   * purpose you can think of.  Constructing the exception in this
+-   * manner makes the exception behave in the normal way - i.e., as
+-   * documented in the class "Throwable".  The fields "errorToken",
+-   * "expectedTokenSequences", and "tokenImage" do not contain
+-   * relevant information.  The JavaCC generated code does not use
+-   * these constructors.
+-   */
+-
+-  public ParseException() {
+-    super();
+-  }
+-
+-  /** Constructor with message. */
+-  public ParseException(String message) {
+-    super(message);
+-  }
+-
+-
+-  /**
+-   * This is the last token that has been consumed successfully.  If
+-   * this object has been created due to a parse error, the token
+-   * followng this token will (therefore) be the first error token.
+-   */
+-  public Token currentToken;
+-
+-  /**
+-   * Each entry in this array is an array of integers.  Each array
+-   * of integers represents a sequence of tokens (by their ordinal
+-   * values) that is expected at this point of the parse.
+-   */
+-  public int[][] expectedTokenSequences;
+-
+-  /**
+-   * This is a reference to the "tokenImage" array of the generated
+-   * parser within which the parse error occurred.  This array is
+-   * defined in the generated ...Constants interface.
+-   */
+-  public String[] tokenImage;
+-
+-  /**
+-   * It uses "currentToken" and "expectedTokenSequences" to generate a parse
+-   * error message and returns it.  If this object has been created
+-   * due to a parse error, and you do not catch it (it gets thrown
+-   * from the parser) the correct error message
+-   * gets displayed.
+-   */
+-  private static String initialise(Token currentToken,
+-                           int[][] expectedTokenSequences,
+-                           String[] tokenImage) {
+-    String eol = System.getProperty("line.separator", "\n");
+-    StringBuffer expected = new StringBuffer();
+-    int maxSize = 0;
+-    for (int i = 0; i < expectedTokenSequences.length; i++) {
+-      if (maxSize < expectedTokenSequences[i].length) {
+-        maxSize = expectedTokenSequences[i].length;
+-      }
+-      for (int j = 0; j < expectedTokenSequences[i].length; j++) {
+-        expected.append(tokenImage[expectedTokenSequences[i][j]]).append(' ');
+-      }
+-      if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0) {
+-        expected.append("...");
+-      }
+-      expected.append(eol).append("    ");
+-    }
+-    String retval = "Encountered \"";
+-    Token tok = currentToken.next;
+-    for (int i = 0; i < maxSize; i++) {
+-      if (i != 0) retval += " ";
+-      if (tok.kind == 0) {
+-        retval += tokenImage[0];
+-        break;
+-      }
+-      retval += " " + tokenImage[tok.kind];
+-      retval += " \"";
+-      retval += add_escapes(tok.image);
+-      retval += " \"";
+-      tok = tok.next;
+-    }
+-    retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn;
+-    retval += "." + eol;
+-    if (expectedTokenSequences.length == 1) {
+-      retval += "Was expecting:" + eol + "    ";
+-    } else {
+-      retval += "Was expecting one of:" + eol + "    ";
+-    }
+-    retval += expected.toString();
+-    return retval;
+-  }
+-
+-  /**
+-   * The end of line string for this machine.
+-   */
+-  protected String eol = System.getProperty("line.separator", "\n");
+-
+-  /**
+-   * Used to convert raw characters to their escaped version
+-   * when these raw version cannot be used as part of an ASCII
+-   * string literal.
+-   */
+-  static String add_escapes(String str) {
+-      StringBuffer retval = new StringBuffer();
+-      char ch;
+-      for (int i = 0; i < str.length(); i++) {
+-        switch (str.charAt(i))
+-        {
+-           case 0 :
+-              continue;
+-           case '\b':
+-              retval.append("\\b");
+-              continue;
+-           case '\t':
+-              retval.append("\\t");
+-              continue;
+-           case '\n':
+-              retval.append("\\n");
+-              continue;
+-           case '\f':
+-              retval.append("\\f");
+-              continue;
+-           case '\r':
+-              retval.append("\\r");
+-              continue;
+-           case '\"':
+-              retval.append("\\\"");
+-              continue;
+-           case '\'':
+-              retval.append("\\\'");
+-              continue;
+-           case '\\':
+-              retval.append("\\\\");
+-              continue;
+-           default:
+-              if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
+-                 String s = "0000" + Integer.toString(ch, 16);
+-                 retval.append("\\u" + s.substring(s.length() - 4, s.length()));
+-              } else {
+-                 retval.append(ch);
+-              }
+-              continue;
+-        }
+-      }
+-      return retval.toString();
+-   }
+-
+-}
+-/* JavaCC - OriginalChecksum=a5bfbb99e4df5e108e0f9a6351af5364 (do not edit this line) */
+diff --git a/java/org/apache/tomcat/util/http/parser/SimpleCharStream.java b/java/org/apache/tomcat/util/http/parser/SimpleCharStream.java
+deleted file mode 100644
+index 2c7b69e..0000000
+--- a/java/org/apache/tomcat/util/http/parser/SimpleCharStream.java
++++ /dev/null
+@@ -1,471 +0,0 @@
+-/* Generated By:JavaCC: Do not edit this line. SimpleCharStream.java Version 5.0 */
+-/* JavaCCOptions:STATIC=false,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
+-package org.apache.tomcat.util.http.parser;
+-
+-/**
+- * An implementation of interface CharStream, where the stream is assumed to
+- * contain only ASCII characters (without unicode processing).
+- */
+- at SuppressWarnings("all") // Ignore warnings in generated code
+-public class SimpleCharStream
+-{
+-/** Whether parser is static. */
+-  public static final boolean staticFlag = false;
+-  int bufsize;
+-  int available;
+-  int tokenBegin;
+-/** Position in buffer. */
+-  public int bufpos = -1;
+-  protected int bufline[];
+-  protected int bufcolumn[];
+-
+-  protected int column = 0;
+-  protected int line = 1;
+-
+-  protected boolean prevCharIsCR = false;
+-  protected boolean prevCharIsLF = false;
+-
+-  protected java.io.Reader inputStream;
+-
+-  protected char[] buffer;
+-  protected int maxNextCharInd = 0;
+-  protected int inBuf = 0;
+-  protected int tabSize = 8;
+-
+-  protected void setTabSize(int i) { tabSize = i; }
+-  protected int getTabSize(int i) { return tabSize; }
+-
+-
+-  protected void ExpandBuff(boolean wrapAround)
+-  {
+-    char[] newbuffer = new char[bufsize + 2048];
+-    int newbufline[] = new int[bufsize + 2048];
+-    int newbufcolumn[] = new int[bufsize + 2048];
+-
+-    try
+-    {
+-      if (wrapAround)
+-      {
+-        System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
+-        System.arraycopy(buffer, 0, newbuffer, bufsize - tokenBegin, bufpos);
+-        buffer = newbuffer;
+-
+-        System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
+-        System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos);
+-        bufline = newbufline;
+-
+-        System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
+-        System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos);
+-        bufcolumn = newbufcolumn;
+-
+-        maxNextCharInd = (bufpos += (bufsize - tokenBegin));
+-      }
+-      else
+-      {
+-        System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
+-        buffer = newbuffer;
+-
+-        System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
+-        bufline = newbufline;
+-
+-        System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
+-        bufcolumn = newbufcolumn;
+-
+-        maxNextCharInd = (bufpos -= tokenBegin);
+-      }
+-    }
+-    catch (Throwable t)
+-    {
+-      throw new Error(t.getMessage());
+-    }
+-
+-
+-    bufsize += 2048;
+-    available = bufsize;
+-    tokenBegin = 0;
+-  }
+-
+-  protected void FillBuff() throws java.io.IOException
+-  {
+-    if (maxNextCharInd == available)
+-    {
+-      if (available == bufsize)
+-      {
+-        if (tokenBegin > 2048)
+-        {
+-          bufpos = maxNextCharInd = 0;
+-          available = tokenBegin;
+-        }
+-        else if (tokenBegin < 0)
+-          bufpos = maxNextCharInd = 0;
+-        else
+-          ExpandBuff(false);
+-      }
+-      else if (available > tokenBegin)
+-        available = bufsize;
+-      else if ((tokenBegin - available) < 2048)
+-        ExpandBuff(true);
+-      else
+-        available = tokenBegin;
+-    }
+-
+-    int i;
+-    try {
+-      if ((i = inputStream.read(buffer, maxNextCharInd, available - maxNextCharInd)) == -1)
+-      {
+-        inputStream.close();
+-        throw new java.io.IOException();
+-      }
+-      else
+-        maxNextCharInd += i;
+-      return;
+-    }
+-    catch(java.io.IOException e) {
+-      --bufpos;
+-      backup(0);
+-      if (tokenBegin == -1)
+-        tokenBegin = bufpos;
+-      throw e;
+-    }
+-  }
+-
+-/** Start. */
+-  public char BeginToken() throws java.io.IOException
+-  {
+-    tokenBegin = -1;
+-    char c = readChar();
+-    tokenBegin = bufpos;
+-
+-    return c;
+-  }
+-
+-  protected void UpdateLineColumn(char c)
+-  {
+-    column++;
+-
+-    if (prevCharIsLF)
+-    {
+-      prevCharIsLF = false;
+-      line += (column = 1);
+-    }
+-    else if (prevCharIsCR)
+-    {
+-      prevCharIsCR = false;
+-      if (c == '\n')
+-      {
+-        prevCharIsLF = true;
+-      }
+-      else
+-        line += (column = 1);
+-    }
+-
+-    switch (c)
+-    {
+-      case '\r' :
+-        prevCharIsCR = true;
+-        break;
+-      case '\n' :
+-        prevCharIsLF = true;
+-        break;
+-      case '\t' :
+-        column--;
+-        column += (tabSize - (column % tabSize));
+-        break;
+-      default :
+-        break;
+-    }
+-
+-    bufline[bufpos] = line;
+-    bufcolumn[bufpos] = column;
+-  }
+-
+-/** Read a character. */
+-  public char readChar() throws java.io.IOException
+-  {
+-    if (inBuf > 0)
+-    {
+-      --inBuf;
+-
+-      if (++bufpos == bufsize)
+-        bufpos = 0;
+-
+-      return buffer[bufpos];
+-    }
+-
+-    if (++bufpos >= maxNextCharInd)
+-      FillBuff();
+-
+-    char c = buffer[bufpos];
+-
+-    UpdateLineColumn(c);
+-    return c;
+-  }
+-
+-  @Deprecated
+-  /**
+-   * @deprecated
+-   * @see #getEndColumn
+-   */
+-
+-  public int getColumn() {
+-    return bufcolumn[bufpos];
+-  }
+-
+-  @Deprecated
+-  /**
+-   * @deprecated
+-   * @see #getEndLine
+-   */
+-
+-  public int getLine() {
+-    return bufline[bufpos];
+-  }
+-
+-  /** Get token end column number. */
+-  public int getEndColumn() {
+-    return bufcolumn[bufpos];
+-  }
+-
+-  /** Get token end line number. */
+-  public int getEndLine() {
+-     return bufline[bufpos];
+-  }
+-
+-  /** Get token beginning column number. */
+-  public int getBeginColumn() {
+-    return bufcolumn[tokenBegin];
+-  }
+-
+-  /** Get token beginning line number. */
+-  public int getBeginLine() {
+-    return bufline[tokenBegin];
+-  }
+-
+-/** Backup a number of characters. */
+-  public void backup(int amount) {
+-
+-    inBuf += amount;
+-    if ((bufpos -= amount) < 0)
+-      bufpos += bufsize;
+-  }
+-
+-  /** Constructor. */
+-  public SimpleCharStream(java.io.Reader dstream, int startline,
+-  int startcolumn, int buffersize)
+-  {
+-    inputStream = dstream;
+-    line = startline;
+-    column = startcolumn - 1;
+-
+-    available = bufsize = buffersize;
+-    buffer = new char[buffersize];
+-    bufline = new int[buffersize];
+-    bufcolumn = new int[buffersize];
+-  }
+-
+-  /** Constructor. */
+-  public SimpleCharStream(java.io.Reader dstream, int startline,
+-                          int startcolumn)
+-  {
+-    this(dstream, startline, startcolumn, 4096);
+-  }
+-
+-  /** Constructor. */
+-  public SimpleCharStream(java.io.Reader dstream)
+-  {
+-    this(dstream, 1, 1, 4096);
+-  }
+-
+-  /** Reinitialise. */
+-  public void ReInit(java.io.Reader dstream, int startline,
+-  int startcolumn, int buffersize)
+-  {
+-    inputStream = dstream;
+-    line = startline;
+-    column = startcolumn - 1;
+-
+-    if (buffer == null || buffersize != buffer.length)
+-    {
+-      available = bufsize = buffersize;
+-      buffer = new char[buffersize];
+-      bufline = new int[buffersize];
+-      bufcolumn = new int[buffersize];
+-    }
+-    prevCharIsLF = prevCharIsCR = false;
+-    tokenBegin = inBuf = maxNextCharInd = 0;
+-    bufpos = -1;
+-  }
+-
+-  /** Reinitialise. */
+-  public void ReInit(java.io.Reader dstream, int startline,
+-                     int startcolumn)
+-  {
+-    ReInit(dstream, startline, startcolumn, 4096);
+-  }
+-
+-  /** Reinitialise. */
+-  public void ReInit(java.io.Reader dstream)
+-  {
+-    ReInit(dstream, 1, 1, 4096);
+-  }
+-  /** Constructor. */
+-  public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline,
+-  int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException
+-  {
+-    this(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize);
+-  }
+-
+-  /** Constructor. */
+-  public SimpleCharStream(java.io.InputStream dstream, int startline,
+-  int startcolumn, int buffersize)
+-  {
+-    this(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize);
+-  }
+-
+-  /** Constructor. */
+-  public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline,
+-                          int startcolumn) throws java.io.UnsupportedEncodingException
+-  {
+-    this(dstream, encoding, startline, startcolumn, 4096);
+-  }
+-
+-  /** Constructor. */
+-  public SimpleCharStream(java.io.InputStream dstream, int startline,
+-                          int startcolumn)
+-  {
+-    this(dstream, startline, startcolumn, 4096);
+-  }
+-
+-  /** Constructor. */
+-  public SimpleCharStream(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException
+-  {
+-    this(dstream, encoding, 1, 1, 4096);
+-  }
+-
+-  /** Constructor. */
+-  public SimpleCharStream(java.io.InputStream dstream)
+-  {
+-    this(dstream, 1, 1, 4096);
+-  }
+-
+-  /** Reinitialise. */
+-  public void ReInit(java.io.InputStream dstream, String encoding, int startline,
+-                          int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException
+-  {
+-    ReInit(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize);
+-  }
+-
+-  /** Reinitialise. */
+-  public void ReInit(java.io.InputStream dstream, int startline,
+-                          int startcolumn, int buffersize)
+-  {
+-    ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize);
+-  }
+-
+-  /** Reinitialise. */
+-  public void ReInit(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException
+-  {
+-    ReInit(dstream, encoding, 1, 1, 4096);
+-  }
+-
+-  /** Reinitialise. */
+-  public void ReInit(java.io.InputStream dstream)
+-  {
+-    ReInit(dstream, 1, 1, 4096);
+-  }
+-  /** Reinitialise. */
+-  public void ReInit(java.io.InputStream dstream, String encoding, int startline,
+-                     int startcolumn) throws java.io.UnsupportedEncodingException
+-  {
+-    ReInit(dstream, encoding, startline, startcolumn, 4096);
+-  }
+-  /** Reinitialise. */
+-  public void ReInit(java.io.InputStream dstream, int startline,
+-                     int startcolumn)
+-  {
+-    ReInit(dstream, startline, startcolumn, 4096);
+-  }
+-  /** Get token literal value. */
+-  public String GetImage()
+-  {
+-    if (bufpos >= tokenBegin)
+-      return new String(buffer, tokenBegin, bufpos - tokenBegin + 1);
+-    else
+-      return new String(buffer, tokenBegin, bufsize - tokenBegin) +
+-                            new String(buffer, 0, bufpos + 1);
+-  }
+-
+-  /** Get the suffix. */
+-  public char[] GetSuffix(int len)
+-  {
+-    char[] ret = new char[len];
+-
+-    if ((bufpos + 1) >= len)
+-      System.arraycopy(buffer, bufpos - len + 1, ret, 0, len);
+-    else
+-    {
+-      System.arraycopy(buffer, bufsize - (len - bufpos - 1), ret, 0,
+-                                                        len - bufpos - 1);
+-      System.arraycopy(buffer, 0, ret, len - bufpos - 1, bufpos + 1);
+-    }
+-
+-    return ret;
+-  }
+-
+-  /** Reset buffer when finished. */
+-  public void Done()
+-  {
+-    buffer = null;
+-    bufline = null;
+-    bufcolumn = null;
+-  }
+-
+-  /**
+-   * Method to adjust line and column numbers for the start of a token.
+-   */
+-  public void adjustBeginLineColumn(int newLine, int newCol)
+-  {
+-    int start = tokenBegin;
+-    int len;
+-
+-    if (bufpos >= tokenBegin)
+-    {
+-      len = bufpos - tokenBegin + inBuf + 1;
+-    }
+-    else
+-    {
+-      len = bufsize - tokenBegin + bufpos + 1 + inBuf;
+-    }
+-
+-    int i = 0, j = 0, k = 0;
+-    int nextColDiff = 0, columnDiff = 0;
+-
+-    while (i < len && bufline[j = start % bufsize] == bufline[k = ++start % bufsize])
+-    {
+-      bufline[j] = newLine;
+-      nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j];
+-      bufcolumn[j] = newCol + columnDiff;
+-      columnDiff = nextColDiff;
+-      i++;
+-    }
+-
+-    if (i < len)
+-    {
+-      bufline[j] = newLine++;
+-      bufcolumn[j] = newCol + columnDiff;
+-
+-      while (i++ < len)
+-      {
+-        if (bufline[j = start % bufsize] != bufline[++start % bufsize])
+-          bufline[j] = newLine++;
+-        else
+-          bufline[j] = newLine;
+-      }
+-    }
+-
+-    line = bufline[j];
+-    column = bufcolumn[j];
+-  }
+-
+-}
+-/* JavaCC - OriginalChecksum=d7e5626d3f8464d1815f1157b0223728 (do not edit this line) */
+diff --git a/java/org/apache/tomcat/util/http/parser/SimpleNode.java b/java/org/apache/tomcat/util/http/parser/SimpleNode.java
+deleted file mode 100644
+index c401bb0..0000000
+--- a/java/org/apache/tomcat/util/http/parser/SimpleNode.java
++++ /dev/null
+@@ -1,118 +0,0 @@
+-/*
+- *  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.tomcat.util.http.parser;
+-
+-public class SimpleNode implements Node {
+-
+-    protected Node parent;
+-    protected Node[] children;
+-    protected int id;
+-    protected Object value;
+-    protected HttpParser parser;
+-
+-    public SimpleNode(int i) {
+-        id = i;
+-    }
+-
+-    public SimpleNode(HttpParser p, int i) {
+-        this(i);
+-        parser = p;
+-    }
+-
+-    @Override
+-    public void jjtOpen() {
+-        // NOOP
+-    }
+-
+-    @Override
+-    public void jjtClose() {
+-        // NOOP
+-    }
+-
+-    @Override
+-    public void jjtSetParent(Node n) {
+-        parent = n;
+-    }
+-
+-    @Override
+-    public Node jjtGetParent() {
+-        return parent;
+-    }
+-
+-    @Override
+-    public void jjtAddChild(Node n, int i) {
+-        if (children == null) {
+-            children = new Node[i + 1];
+-        } else if (i >= children.length) {
+-            Node c[] = new Node[i + 1];
+-            System.arraycopy(children, 0, c, 0, children.length);
+-            children = c;
+-        }
+-        children[i] = n;
+-    }
+-
+-    @Override
+-    public Node jjtGetChild(int i) {
+-        return children[i];
+-    }
+-
+-    @Override
+-    public int jjtGetNumChildren() {
+-        return (children == null) ? 0 : children.length;
+-    }
+-
+-    public void jjtSetValue(Object value) {
+-        this.value = value;
+-    }
+-
+-    @Override
+-    public Object jjtGetValue() {
+-        return value;
+-    }
+-
+-    /*
+-     * You can override these two methods in subclasses of SimpleNode to
+-     * customize the way the node appears when the tree is dumped. If your
+-     * output uses more than one line you should override toString(String),
+-     * otherwise overriding toString() is probably all you need to do.
+-     */
+-    @Override
+-    public String toString() {
+-        return HttpParserTreeConstants.jjtNodeName[id];
+-    }
+-
+-    public String toString(String prefix) {
+-        return prefix + toString();
+-    }
+-
+-    /*
+-     * Override this method if you want to customize how the node dumps out its
+-     * children.
+-     */
+-    public void dump(String prefix) {
+-        System.out.println(toString(prefix));
+-        if (children != null) {
+-            for (int i = 0; i < children.length; ++i) {
+-                SimpleNode n = (SimpleNode) children[i];
+-                if (n != null) {
+-                    n.dump(prefix + " ");
+-                }
+-            }
+-        }
+-    }
+-}
+-
+diff --git a/java/org/apache/tomcat/util/http/parser/Token.java b/java/org/apache/tomcat/util/http/parser/Token.java
+deleted file mode 100644
+index 43687b0..0000000
+--- a/java/org/apache/tomcat/util/http/parser/Token.java
++++ /dev/null
+@@ -1,131 +0,0 @@
+-/* Generated By:JavaCC: Do not edit this line. Token.java Version 5.0 */
+-/* JavaCCOptions:TOKEN_EXTENDS=,KEEP_LINE_COL=null,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
+-package org.apache.tomcat.util.http.parser;
+-
+-/**
+- * Describes the input token stream.
+- */
+- at SuppressWarnings("all") // Ignore warnings in generated code
+-public class Token implements java.io.Serializable {
+-
+-  /**
+-   * The version identifier for this Serializable class.
+-   * Increment only if the <i>serialized</i> form of the
+-   * class changes.
+-   */
+-  private static final long serialVersionUID = 1L;
+-
+-  /**
+-   * An integer that describes the kind of this token.  This numbering
+-   * system is determined by JavaCCParser, and a table of these numbers is
+-   * stored in the file ...Constants.java.
+-   */
+-  public int kind;
+-
+-  /** The line number of the first character of this Token. */
+-  public int beginLine;
+-  /** The column number of the first character of this Token. */
+-  public int beginColumn;
+-  /** The line number of the last character of this Token. */
+-  public int endLine;
+-  /** The column number of the last character of this Token. */
+-  public int endColumn;
+-
+-  /**
+-   * The string image of the token.
+-   */
+-  public String image;
+-
+-  /**
+-   * A reference to the next regular (non-special) token from the input
+-   * stream.  If this is the last token from the input stream, or if the
+-   * token manager has not read tokens beyond this one, this field is
+-   * set to null.  This is true only if this token is also a regular
+-   * token.  Otherwise, see below for a description of the contents of
+-   * this field.
+-   */
+-  public Token next;
+-
+-  /**
+-   * This field is used to access special tokens that occur prior to this
+-   * token, but after the immediately preceding regular (non-special) token.
+-   * If there are no such special tokens, this field is set to null.
+-   * When there are more than one such special token, this field refers
+-   * to the last of these special tokens, which in turn refers to the next
+-   * previous special token through its specialToken field, and so on
+-   * until the first special token (whose specialToken field is null).
+-   * The next fields of special tokens refer to other special tokens that
+-   * immediately follow it (without an intervening regular token).  If there
+-   * is no such token, this field is null.
+-   */
+-  public Token specialToken;
+-
+-  /**
+-   * An optional attribute value of the Token.
+-   * Tokens which are not used as syntactic sugar will often contain
+-   * meaningful values that will be used later on by the compiler or
+-   * interpreter. This attribute value is often different from the image.
+-   * Any subclass of Token that actually wants to return a non-null value can
+-   * override this method as appropriate.
+-   */
+-  public Object getValue() {
+-    return null;
+-  }
+-
+-  /**
+-   * No-argument constructor
+-   */
+-  public Token() {}
+-
+-  /**
+-   * Constructs a new token for the specified Image.
+-   */
+-  public Token(int kind)
+-  {
+-    this(kind, null);
+-  }
+-
+-  /**
+-   * Constructs a new token for the specified Image and Kind.
+-   */
+-  public Token(int kind, String image)
+-  {
+-    this.kind = kind;
+-    this.image = image;
+-  }
+-
+-  /**
+-   * Returns the image.
+-   */
+-  public String toString()
+-  {
+-    return image;
+-  }
+-
+-  /**
+-   * Returns a new Token object, by default. However, if you want, you
+-   * can create and return subclass objects based on the value of ofKind.
+-   * Simply add the cases to the switch for all those special cases.
+-   * For example, if you have a subclass of Token called IDToken that
+-   * you want to create if ofKind is ID, simply add something like :
+-   *
+-   *    case MyParserConstants.ID : return new IDToken(ofKind, image);
+-   *
+-   * to the following switch statement. Then you can cast matchedToken
+-   * variable to the appropriate type and use sit in your lexical actions.
+-   */
+-  public static Token newToken(int ofKind, String image)
+-  {
+-    switch(ofKind)
+-    {
+-      default : return new Token(ofKind, image);
+-    }
+-  }
+-
+-  public static Token newToken(int ofKind)
+-  {
+-    return newToken(ofKind, null);
+-  }
+-
+-}
+-/* JavaCC - OriginalChecksum=2104130aa3f9189e35a4571dc4c8f0c9 (do not edit this line) */
+diff --git a/java/org/apache/tomcat/util/http/parser/TokenMgrError.java b/java/org/apache/tomcat/util/http/parser/TokenMgrError.java
+deleted file mode 100644
+index b724438..0000000
+--- a/java/org/apache/tomcat/util/http/parser/TokenMgrError.java
++++ /dev/null
+@@ -1,148 +0,0 @@
+-/* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 5.0 */
+-/* JavaCCOptions: */
+-package org.apache.tomcat.util.http.parser;
+-
+-/** Token Manager Error. */
+- at SuppressWarnings("all") // Ignore warnings in generated code
+-public class TokenMgrError extends Error
+-{
+-
+-  /**
+-   * The version identifier for this Serializable class.
+-   * Increment only if the <i>serialized</i> form of the
+-   * class changes.
+-   */
+-  private static final long serialVersionUID = 1L;
+-
+-  /*
+-   * Ordinals for various reasons why an Error of this type can be thrown.
+-   */
+-
+-  /**
+-   * Lexical error occurred.
+-   */
+-  static final int LEXICAL_ERROR = 0;
+-
+-  /**
+-   * An attempt was made to create a second instance of a static token manager.
+-   */
+-  static final int STATIC_LEXER_ERROR = 1;
+-
+-  /**
+-   * Tried to change to an invalid lexical state.
+-   */
+-  static final int INVALID_LEXICAL_STATE = 2;
+-
+-  /**
+-   * Detected (and bailed out of) an infinite loop in the token manager.
+-   */
+-  static final int LOOP_DETECTED = 3;
+-
+-  /**
+-   * Indicates the reason why the exception is thrown. It will have
+-   * one of the above 4 values.
+-   */
+-  int errorCode;
+-
+-  /**
+-   * Replaces unprintable characters by their escaped (or unicode escaped)
+-   * equivalents in the given string
+-   */
+-  protected static final String addEscapes(String str) {
+-    StringBuffer retval = new StringBuffer();
+-    char ch;
+-    for (int i = 0; i < str.length(); i++) {
+-      switch (str.charAt(i))
+-      {
+-        case 0 :
+-          continue;
+-        case '\b':
+-          retval.append("\\b");
+-          continue;
+-        case '\t':
+-          retval.append("\\t");
+-          continue;
+-        case '\n':
+-          retval.append("\\n");
+-          continue;
+-        case '\f':
+-          retval.append("\\f");
+-          continue;
+-        case '\r':
+-          retval.append("\\r");
+-          continue;
+-        case '\"':
+-          retval.append("\\\"");
+-          continue;
+-        case '\'':
+-          retval.append("\\\'");
+-          continue;
+-        case '\\':
+-          retval.append("\\\\");
+-          continue;
+-        default:
+-          if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
+-            String s = "0000" + Integer.toString(ch, 16);
+-            retval.append("\\u" + s.substring(s.length() - 4, s.length()));
+-          } else {
+-            retval.append(ch);
+-          }
+-          continue;
+-      }
+-    }
+-    return retval.toString();
+-  }
+-
+-  /**
+-   * Returns a detailed message for the Error when it is thrown by the
+-   * token manager to indicate a lexical error.
+-   * Parameters :
+-   *    EOFSeen     : indicates if EOF caused the lexical error
+-   *    curLexState : lexical state in which this error occurred
+-   *    errorLine   : line number when the error occurred
+-   *    errorColumn : column number when the error occurred
+-   *    errorAfter  : prefix that was seen before this error occurred
+-   *    curchar     : the offending character
+-   * Note: You can customize the lexical error message by modifying this method.
+-   */
+-  protected static String LexicalError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar) {
+-    return("Lexical error at line " +
+-          errorLine + ", column " +
+-          errorColumn + ".  Encountered: " +
+-          (EOFSeen ? "<EOF> " : ("\"" + addEscapes(String.valueOf(curChar)) + "\"") + " (" + (int)curChar + "), ") +
+-          "after : \"" + addEscapes(errorAfter) + "\"");
+-  }
+-
+-  /**
+-   * You can also modify the body of this method to customize your error messages.
+-   * For example, cases like LOOP_DETECTED and INVALID_LEXICAL_STATE are not
+-   * of end-users concern, so you can return something like :
+-   *
+-   *     "Internal Error : Please file a bug report .... "
+-   *
+-   * from this method for such cases in the release version of your parser.
+-   */
+-  public String getMessage() {
+-    return super.getMessage();
+-  }
+-
+-  /*
+-   * Constructors of various flavors follow.
+-   */
+-
+-  /** No arg constructor. */
+-  public TokenMgrError() {
+-  }
+-
+-  /** Constructor with message and reason. */
+-  public TokenMgrError(String message, int reason) {
+-    super(message);
+-    errorCode = reason;
+-  }
+-
+-  /** Full Constructor. */
+-  public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar, int reason) {
+-    this(LexicalError(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason);
+-  }
+-}
+-/* JavaCC - OriginalChecksum=c0e71cb84849413e4aa36c7471643b93 (do not edit this line) */
+diff --git a/test/org/apache/tomcat/util/http/parser/TestAuthorizationDigest.java b/test/org/apache/tomcat/util/http/parser/TestAuthorizationDigest.java
+new file mode 100644
+index 0000000..ee22469
+--- /dev/null
++++ b/test/org/apache/tomcat/util/http/parser/TestAuthorizationDigest.java
+@@ -0,0 +1,324 @@
++/*
++ *  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.tomcat.util.http.parser;
++
++import java.io.StringReader;
++import java.util.Map;
++
++import org.junit.Assert;
++import org.junit.Test;
++
++public class TestAuthorizationDigest {
++
++    @Test
++    public void testBug54060a() throws Exception {
++        String header = "Digest username=\"mthornton\", " +
++                "realm=\"optrak.com\", " +
++                "nonce=\"1351427243671:c1d6360150712149bae931a3ed7cb498\", " +
++                "uri=\"/files/junk.txt\", " +
++                "response=\"c5c2410bfc46753e83a8f007888b0d2e\", " +
++                "opaque=\"DB85C1A73933A7EB586D10E4BF2924EF\", " +
++                "qop=auth, " +
++                "nc=00000001, " +
++                "cnonce=\"9926cb3c334ede11\"";
++
++        StringReader input = new StringReader(header);
++
++        Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
++
++        Assert.assertEquals("mthornton", result.get("username"));
++        Assert.assertEquals("optrak.com", result.get("realm"));
++        Assert.assertEquals("1351427243671:c1d6360150712149bae931a3ed7cb498",
++                result.get("nonce"));
++        Assert.assertEquals("/files/junk.txt", result.get("uri"));
++        Assert.assertEquals("c5c2410bfc46753e83a8f007888b0d2e",
++                result.get("response"));
++        Assert.assertEquals("DB85C1A73933A7EB586D10E4BF2924EF",
++                result.get("opaque"));
++        Assert.assertEquals("auth", result.get("qop"));
++        Assert.assertEquals("00000001", result.get("nc"));
++        Assert.assertEquals("9926cb3c334ede11", result.get("cnonce"));
++    }
++
++    @Test
++    public void testBug54060b() throws Exception {
++        String header = "Digest username=\"mthornton\", " +
++                "realm=\"optrak.com\", " +
++                "nonce=\"1351427480964:a01c16fed5168d72a2b5267395a2022e\", " +
++                "uri=\"/files\", " +
++                "algorithm=MD5, " +
++                "response=\"f310c44b87efc0bc0a7aab7096fd36b6\", " +
++                "opaque=\"DB85C1A73933A7EB586D10E4BF2924EF\", " +
++                "cnonce=\"MHg3ZjA3ZGMwMTUwMTA6NzI2OToxMzUxNDI3NDgw\", " +
++                "nc=00000001, " +
++                "qop=auth";
++
++        StringReader input = new StringReader(header);
++
++        Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
++
++        Assert.assertEquals("mthornton", result.get("username"));
++        Assert.assertEquals("optrak.com", result.get("realm"));
++        Assert.assertEquals("1351427480964:a01c16fed5168d72a2b5267395a2022e",
++                result.get("nonce"));
++        Assert.assertEquals("/files", result.get("uri"));
++        Assert.assertEquals("MD5", result.get("algorithm"));
++        Assert.assertEquals("f310c44b87efc0bc0a7aab7096fd36b6",
++                result.get("response"));
++        Assert.assertEquals("DB85C1A73933A7EB586D10E4BF2924EF",
++                result.get("opaque"));
++        Assert.assertEquals("MHg3ZjA3ZGMwMTUwMTA6NzI2OToxMzUxNDI3NDgw",
++                result.get("cnonce"));
++        Assert.assertEquals("00000001", result.get("nc"));
++        Assert.assertEquals("auth", result.get("qop"));
++    }
++
++    @Test
++    public void testBug54060c() throws Exception {
++        String header = "Digest username=\"mthornton\", qop=auth";
++
++        StringReader input = new StringReader(header);
++
++        Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
++
++        Assert.assertEquals("mthornton", result.get("username"));
++        Assert.assertEquals("auth", result.get("qop"));
++    }
++
++    @Test
++    public void testBug54060d() throws Exception {
++        String header = "Digest username=\"mthornton\"," +
++                "qop=auth," +
++                "cnonce=\"9926cb3c334ede11\"";
++
++        StringReader input = new StringReader(header);
++
++        Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
++
++        Assert.assertEquals("mthornton", result.get("username"));
++        Assert.assertEquals("auth", result.get("qop"));
++        Assert.assertEquals("9926cb3c334ede11", result.get("cnonce"));
++    }
++
++    @Test
++    public void testEndWithLhex() throws Exception {
++        String header = "Digest nc=00000001";
++
++        StringReader input = new StringReader(header);
++
++        Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
++
++        Assert.assertEquals("00000001", result.get("nc"));
++    }
++
++    @Test
++    public void testQuotedLhex() throws Exception {
++        String header = "Digest nc=\"09abcdef\"";
++
++        StringReader input = new StringReader(header);
++
++        Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
++
++        Assert.assertEquals("09abcdef", result.get("nc"));
++    }
++
++    @Test
++    public void testQuotedLhexUppercase() throws Exception {
++        String header = "Digest nc=\"00ABCDEF\"";
++
++        StringReader input = new StringReader(header);
++
++        Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
++
++        Assert.assertEquals("00abcdef", result.get("nc"));
++    }
++
++    @Test
++    public void testUnclosedQuotedLhex() throws Exception {
++        String header = "Digest nc=\"00000001";
++
++        StringReader input = new StringReader(header);
++
++        Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
++
++        Assert.assertNull(result);
++    }
++
++    @Test
++    public void testUnclosedQuotedString1() throws Exception {
++        String header = "Digest username=\"test";
++
++        StringReader input = new StringReader(header);
++
++        Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
++        Assert.assertNull(result);
++    }
++
++    @Test
++    public void testUnclosedQuotedString2() throws Exception {
++        String header = "Digest username=\"test\\";
++
++        StringReader input = new StringReader(header);
++
++        Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
++        Assert.assertNull(result);
++    }
++
++    @Test
++    public void testNonTokenDirective() throws Exception {
++        String header = "Digest user{name=\"test\"";
++
++        StringReader input = new StringReader(header);
++
++        Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
++        Assert.assertNull(result);
++    }
++
++    @Test
++    public void testTokenQop() throws Exception {
++        String header = "Digest qop=auth";
++
++        StringReader input = new StringReader(header);
++
++        Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
++        Assert.assertEquals("auth", result.get("qop"));
++    }
++
++    @Test
++    public void testQuotedTokenQop() throws Exception {
++        String header = "Digest qop=\"auth\"";
++
++        StringReader input = new StringReader(header);
++
++        Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
++        Assert.assertEquals("auth", result.get("qop"));
++    }
++
++    @Test
++    public void testNonTokenQop() throws Exception {
++        String header = "Digest qop=au{th";
++
++        StringReader input = new StringReader(header);
++
++        Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
++        Assert.assertNull(result);
++    }
++
++    @Test
++    public void testQuotedNonTokenQop() throws Exception {
++        String header = "Digest qop=\"au{th\"";
++
++        StringReader input = new StringReader(header);
++
++        Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
++        Assert.assertNull(result);
++    }
++
++    @Test
++    public void testQuotedNonTokenQop2() throws Exception {
++        String header = "Digest qop=\"{auth\"";
++
++        StringReader input = new StringReader(header);
++
++        Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
++        Assert.assertNull(result);
++    }
++
++    @Test
++    public void testUnclosedQuotedTokenQop() throws Exception {
++        String header = "Digest qop=\"auth";
++
++        StringReader input = new StringReader(header);
++
++        Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
++        Assert.assertNull(result);
++    }
++
++    @Test
++    public void testWrongCharacterInToken() throws Exception {
++        String header = "Digest \u044f";
++
++        StringReader input = new StringReader(header);
++
++        Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
++        Assert.assertNull(result);
++    }
++
++    @Test
++    public void testWrongCharacterInToken2() throws Exception {
++        String header = "Digest qop=\u044f";
++
++        StringReader input = new StringReader(header);
++
++        Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
++        Assert.assertNull(result);
++    }
++
++    @Test
++    public void testWrongCharacterInQuotedToken() throws Exception {
++        String header = "Digest qop=\"\u044f\"";
++
++        StringReader input = new StringReader(header);
++
++        Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
++        Assert.assertNull(result);
++    }
++
++    @Test
++    public void testWrongCharacterInHex() throws Exception {
++        String header = "Digest nc=\u044f";
++
++        StringReader input = new StringReader(header);
++
++        Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
++        Assert.assertNull(result);
++    }
++
++    @Test
++    public void testWrongCharacterInQuotedHex() throws Exception {
++        String header = "Digest nc=\"\u044f\"";
++
++        StringReader input = new StringReader(header);
++
++        Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
++        Assert.assertNull(result);
++    }
++
++    @Test
++    public void testParseAuthParamA() throws Exception {
++        // Test for HttpParser.readTokenOrQuotedString()
++        // auth-param = token "=" ( token | quoted-string )
++        String header = "Digest a=b";
++
++        StringReader input = new StringReader(header);
++
++        Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
++        Assert.assertEquals("b", result.get("a"));
++    }
++
++    @Test
++    public void testParseAuthParamB() throws Exception {
++        // Test for HttpParser.readTokenOrQuotedString()
++        // auth-param = token "=" ( token | quoted-string )
++        String header = "Digest a=\"b\"";
++
++        StringReader input = new StringReader(header);
++
++        Map<String,String> result = HttpParser.parseAuthorizationDigest(input);
++        Assert.assertEquals("b", result.get("a"));
++    }
++}
+diff --git a/test/org/apache/tomcat/util/http/parser/TestMediaType.java b/test/org/apache/tomcat/util/http/parser/TestMediaType.java
+index d80cee6..b564ed8 100644
+--- a/test/org/apache/tomcat/util/http/parser/TestMediaType.java
++++ b/test/org/apache/tomcat/util/http/parser/TestMediaType.java
+@@ -16,12 +16,14 @@
+  */
+ package org.apache.tomcat.util.http.parser;
+ 
++import java.io.IOException;
+ import java.io.StringReader;
+ 
+ import static org.junit.Assert.assertEquals;
+ import static org.junit.Assert.assertNull;
+ import static org.junit.Assert.assertTrue;
+ 
++import org.junit.Assert;
+ import org.junit.Test;
+ 
+ /**
+@@ -44,6 +46,7 @@ public class TestMediaType {
+             new Parameter("z", "\"\"");
+     private static final Parameter PARAM_COMPLEX_QUOTED =
+             new Parameter("w", "\"foo'bar,a=b;x=y\"");
++
+     private static final String CHARSET = "UTF-8";
+     private static final String WS_CHARSET = " \tUTF-8";
+     private static final String CHARSET_WS = "UTF-8 \t";
+@@ -59,94 +62,96 @@ public class TestMediaType {
+             new Parameter("charset", CHARSET_QUOTED);
+ 
+ 
++    private static final String[] LWS_VALUES = new String[] {
++            "", " ", "\t", "\r", "\n", "\r\n", " \r", " \n", " \r\n",
++            "\r ", "\n ", "\r\n ", " \r ", " \n ", " \r\n " };
++
++
+     @Test
+-    public void testSimple() throws ParseException {
++    public void testSimple() throws IOException {
+         doTest();
+     }
+ 
+ 
+     @Test
+-    public void testSimpleWithToken() throws ParseException {
++    public void testSimpleWithToken() throws IOException {
+         doTest(PARAM_TOKEN);
+     }
+ 
+ 
+     @Test
+-    public void testSimpleWithQuotedString() throws ParseException {
++    public void testSimpleWithQuotedString() throws IOException {
+         doTest(PARAM_QUOTED);
+     }
+ 
+ 
+     @Test
+-    public void testSimpleWithEmptyQuotedString() throws ParseException {
++    public void testSimpleWithEmptyQuotedString() throws IOException {
+         doTest(PARAM_EMPTY_QUOTED);
+     }
+ 
+ 
+     @Test
+-    public void testSimpleWithComplesQuotedString() throws ParseException {
++    public void testSimpleWithComplesQuotedString() throws IOException {
+         doTest(PARAM_COMPLEX_QUOTED);
+     }
+ 
+ 
+     @Test
+-    public void testSimpleWithCharset() throws ParseException {
++    public void testSimpleWithCharset() throws IOException {
+         doTest(PARAM_CHARSET);
+     }
+ 
+ 
+     @Test
+-    public void testSimpleWithCharsetWhitespaceBefore() throws ParseException {
++    public void testSimpleWithCharsetWhitespaceBefore() throws IOException {
+         doTest(PARAM_WS_CHARSET);
+     }
+ 
+ 
+     @Test
+-    public void testSimpleWithCharsetWhitespaceAfter() throws ParseException {
++    public void testSimpleWithCharsetWhitespaceAfter() throws IOException {
+         doTest(PARAM_CHARSET_WS);
+     }
+ 
+ 
+     @Test
+-    public void testSimpleWithCharsetQuoted() throws ParseException {
++    public void testSimpleWithCharsetQuoted() throws IOException {
+         doTest(PARAM_CHARSET_QUOTED);
+     }
+ 
+ 
+     @Test
+-    public void testSimpleWithAll() throws ParseException {
++    public void testSimpleWithAll() throws IOException {
+         doTest(PARAM_COMPLEX_QUOTED, PARAM_EMPTY_QUOTED, PARAM_QUOTED,
+                 PARAM_TOKEN, PARAM_CHARSET);
+     }
+ 
+ 
+     @Test
+-    public void testCharset() throws ParseException {
++    public void testCharset() throws IOException {
+         StringBuilder sb = new StringBuilder();
+         sb.append(TYPES);
+         sb.append(PARAM_CHARSET);
+         sb.append(PARAM_TOKEN);
+ 
+         StringReader sr = new StringReader(sb.toString());
+-        HttpParser hp = new HttpParser(sr);
+-        AstMediaType m = hp.MediaType();
++        MediaType m = HttpParser.parseMediaType(sr);
+ 
+-        assertEquals(sb.toString().replaceAll(" ", ""), m.toString());
++        assertEquals("foo/bar; charset=UTF-8; a=b", m.toString());
+         assertEquals(CHARSET, m.getCharset());
+-        assertEquals(TYPES.replaceAll(" ", "") + PARAM_TOKEN,
+-                m.toStringNoCharset());
++        assertEquals("foo/bar; a=b", m.toStringNoCharset());
+     }
+ 
+ 
+     @Test
+-    public void testCharsetQuoted() throws ParseException {
++    public void testCharsetQuoted() throws IOException {
+         StringBuilder sb = new StringBuilder();
+         sb.append(TYPES);
+         sb.append(PARAM_CHARSET_QUOTED);
+ 
+         StringReader sr = new StringReader(sb.toString());
+-        HttpParser hp = new HttpParser(sr);
+-        AstMediaType m = hp.MediaType();
++        MediaType m = HttpParser.parseMediaType(sr);
+ 
+         assertEquals(CHARSET_WS, m.getCharset());
+         assertEquals(TYPES.replaceAll(" ", ""),
+@@ -155,116 +160,105 @@ public class TestMediaType {
+ 
+ 
+     @Test
+-    public void testBug52811() throws ParseException {
++    public void testBug52811() throws IOException {
+         String input = "multipart/related;boundary=1_4F50BD36_CDF8C28;" +
+                 "Start=\"<31671603.smil>\";" +
+                 "Type=\"application/smil;charset=UTF-8\"";
+ 
+         StringReader sr = new StringReader(input);
+-        HttpParser hp = new HttpParser(sr);
+-        AstMediaType m = hp.MediaType();
+-
+-        assertTrue(m.children.length == 5);
++        MediaType m = HttpParser.parseMediaType(sr);
+ 
+         // Check the types
+-        assertTrue(m.children[0] instanceof AstType);
+-        assertTrue(m.children[1] instanceof AstSubType);
+-        assertEquals("multipart", m.children[0].toString());
+-        assertEquals("related", m.children[1].toString());
++        assertEquals("multipart", m.getType());
++        assertEquals("related", m.getSubtype());
+ 
+         // Check the parameters
+-        AstParameter p = (AstParameter) m.children[2];
+-        assertTrue(p.children.length == 2);
+-        assertTrue(p.children[0] instanceof AstAttribute);
+-        assertTrue(p.children[1] instanceof AstValue);
+-        assertEquals("boundary", p.children[0].toString());
+-        assertEquals("1_4F50BD36_CDF8C28", p.children[1].toString());
+-
+-        p = (AstParameter) m.children[3];
+-        assertTrue(p.children.length == 2);
+-        assertTrue(p.children[0] instanceof AstAttribute);
+-        assertTrue(p.children[1] instanceof AstValue);
+-        assertEquals("Start", p.children[0].toString());
+-        assertEquals("\"<31671603.smil>\"", p.children[1].toString());
+-
+-        p = (AstParameter) m.children[4];
+-        assertTrue(p.children.length == 2);
+-        assertTrue(p.children[0] instanceof AstAttribute);
+-        assertTrue(p.children[1] instanceof AstValue);
+-        assertEquals("Type", p.children[0].toString());
++        assertTrue(m.getParameterCount() == 3);
++
++        assertEquals("1_4F50BD36_CDF8C28", m.getParameterValue("boundary"));
++        assertEquals("\"<31671603.smil>\"", m.getParameterValue("Start"));
+         assertEquals("\"application/smil;charset=UTF-8\"",
+-                p.children[1].toString());
++                m.getParameterValue("Type"));
+ 
+-        assertEquals(input, m.toString());
+-        assertEquals(input, m.toStringNoCharset());
++        String expected = "multipart/related; boundary=1_4F50BD36_CDF8C28; " +
++                "start=\"<31671603.smil>\"; " +
++                "type=\"application/smil;charset=UTF-8\"";
++        assertEquals(expected, m.toString());
++        assertEquals(expected, m.toStringNoCharset());
+         assertNull(m.getCharset());
+     }
+ 
+ 
+     @Test
+-    public void testBug53353() throws ParseException {
++    public void testBug53353() throws IOException {
+         String input = "text/html; UTF-8;charset=UTF-8";
+ 
+         StringReader sr = new StringReader(input);
+-        HttpParser hp = new HttpParser(sr);
+-        AstMediaType m = hp.MediaType();
+-
+-        assertTrue(m.children.length == 4);
++        MediaType m = HttpParser.parseMediaType(sr);
+ 
+         // Check the types
+-        assertTrue(m.children[0] instanceof AstType);
+-        assertTrue(m.children[1] instanceof AstSubType);
+-        assertEquals("text", m.children[0].toString());
+-        assertEquals("html", m.children[1].toString());
++        assertEquals("text", m.getType());
++        assertEquals("html", m.getSubtype());
+ 
+         // Check the parameters
+-        AstParameter p = (AstParameter) m.children[2];
+-        assertTrue(p.children.length == 1);
+-        assertTrue(p.children[0] instanceof AstAttribute);
+-        assertEquals("UTF-8", p.children[0].toString());
+-
+-        p = (AstParameter) m.children[3];
+-        assertTrue(p.children.length == 2);
+-        assertTrue(p.children[0] instanceof AstAttribute);
+-        assertTrue(p.children[1] instanceof AstValue);
+-        assertEquals("charset", p.children[0].toString());
+-        assertEquals("UTF-8", p.children[1].toString());
++        assertTrue(m.getParameterCount() == 2);
++
++        assertEquals("", m.getParameterValue("UTF-8"));
++        assertEquals("UTF-8", m.getCharset());
+ 
+         // Note: Invalid input is filtered out
+-        assertEquals("text/html;charset=UTF-8", m.toString());
++        assertEquals("text/html; charset=UTF-8", m.toString());
++        assertEquals("UTF-8", m.getCharset());
++    }
++
++
++    @Test
++    public void testBug55454() throws IOException {
++        String input = "text/html;;charset=UTF-8";
++
++        StringReader sr = new StringReader(input);
++        MediaType m = HttpParser.parseMediaType(sr);
++
++        assertEquals("text", m.getType());
++        assertEquals("html", m.getSubtype());
++
++        assertTrue(m.getParameterCount() == 1);
++
++        assertEquals("UTF-8", m.getParameterValue("charset"));
+         assertEquals("UTF-8", m.getCharset());
++
++        assertEquals("text/html; charset=UTF-8", m.toString());
+     }
+ 
+ 
+-    private void doTest(Parameter... parameters) throws ParseException {
++    private void doTest(Parameter... parameters) throws IOException {
++        for (String lws : LWS_VALUES) {
++            doTest(lws, parameters);
++        }
++    }
++
++    private void doTest(String lws, Parameter... parameters)
++            throws IOException {
+         StringBuilder sb = new StringBuilder();
+         sb.append(TYPES);
+         for (Parameter p : parameters) {
+-            sb.append(p.toString());
++            sb.append(p.toString(lws));
+         }
+ 
+         StringReader sr = new StringReader(sb.toString());
+-        HttpParser hp = new HttpParser(sr);
+-        AstMediaType m = hp.MediaType();
++        MediaType m = HttpParser.parseMediaType(sr);
+ 
+-        // Check all expected children are present
+-        assertTrue(m.children.length == 2 + parameters.length);
++        // Check all expected parameters are present
++        assertTrue(m.getParameterCount() == parameters.length);
+ 
+         // Check the types
+-        assertTrue(m.children[0] instanceof AstType);
+-        assertTrue(m.children[1] instanceof AstSubType);
+-        assertEquals(TYPE.trim(), m.children[0].toString());
+-        assertEquals(SUBTYPE.trim(), m.children[1].toString());
++        assertEquals(TYPE.trim(), m.getType());
++        assertEquals(SUBTYPE.trim(), m.getSubtype());
+ 
+         // Check the parameters
+         for (int i = 0; i <  parameters.length; i++) {
+-            assertTrue(m.children[i + 2] instanceof AstParameter);
+-            AstParameter p = (AstParameter) m.children[i + 2];
+-            assertTrue(p.children.length == 2);
+-            assertTrue(p.children[0] instanceof AstAttribute);
+-            assertTrue(p.children[1] instanceof AstValue);
+-            assertEquals(parameters[i].getName().trim(), p.children[0].toString());
+-            assertEquals(parameters[i].getValue().trim(), p.children[1].toString());
++            assertEquals(parameters[i].getValue().trim(),
++                    m.getParameterValue(parameters[i].getName().trim()));
+         }
+     }
+ 
+@@ -288,12 +282,32 @@ public class TestMediaType {
+ 
+         @Override
+         public String toString() {
++            return toString("");
++        }
++
++        public String toString(String lws) {
+             StringBuilder sb = new StringBuilder();
++            sb.append(lws);
+             sb.append(";");
++            sb.append(lws);
+             sb.append(name);
++            sb.append(lws);
+             sb.append("=");
++            sb.append(lws);
+             sb.append(value);
++            sb.append(lws);
+             return sb.toString();
+         }
++}
++
++    @Test
++    public void testCase() throws Exception {
++        StringReader sr = new StringReader("type/sub-type;a=1;B=2");
++        MediaType m = HttpParser.parseMediaType(sr);
++
++        Assert.assertEquals("1", m.getParameterValue("A"));
++        Assert.assertEquals("1", m.getParameterValue("a"));
++        Assert.assertEquals("2", m.getParameterValue("B"));
++        Assert.assertEquals("2", m.getParameterValue("b"));
+     }
+ }
diff --git a/debian/patches/CVE-2016-8735.patch b/debian/patches/CVE-2016-8735.patch
new file mode 100644
index 0000000..c24fcdf
--- /dev/null
+++ b/debian/patches/CVE-2016-8735.patch
@@ -0,0 +1,24 @@
+From: Markus Koschany <apo at debian.org>
+Date: Thu, 24 Nov 2016 18:22:01 +0100
+Subject: CVE-2016-8735
+
+Origin: http://svn.apache.org/r1767676
+---
+ java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java b/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java
+index 2abcb56..b83bc14 100644
+--- a/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java
++++ b/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java
+@@ -199,6 +199,10 @@ public class JmxRemoteLifecycleListener implements LifecycleListener {
+                 csf = new RmiClientLocalhostSocketFactory(csf);
+             }
+ 
++            env.put("jmx.remote.rmi.server.credential.types", new String[] {
++                    String[].class.getName(),
++                    String.class.getName() });
++
+             // Populate the env properties used to create the server
+             if (csf != null) {
+                 env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE,
diff --git a/debian/patches/series b/debian/patches/series
index 72b5561..b49c674 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -42,3 +42,5 @@ CVE-2016-6794.patch
 CVE-2016-6796.patch
 CVE-2016-6797.patch
 CVE-2016-0762.patch
+CVE-2016-6816.patch
+CVE-2016-8735.patch

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/tomcat7.git



More information about the pkg-java-commits mailing list