[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