Author: markt Date: Sun Mar 19 20:50:36 2017 New Revision: 1787662 URL: http://svn.apache.org/viewvc?rev=1787662&view=rev Log: Expand the HttpParser to include Host header validation / port location extraction. Note: This is not yet integrated into the request handling
Added: tomcat/trunk/java/org/apache/tomcat/util/http/parser/Host.java (with props) tomcat/trunk/test/org/apache/tomcat/util/http/parser/TestHttpParserHost.java (with props) tomcat/trunk/test/org/apache/tomcat/util/http/parser/TesterHostPerformance.java (with props) Modified: tomcat/trunk/java/org/apache/tomcat/util/http/parser/HttpParser.java Added: tomcat/trunk/java/org/apache/tomcat/util/http/parser/Host.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/http/parser/Host.java?rev=1787662&view=auto ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/http/parser/Host.java (added) +++ tomcat/trunk/java/org/apache/tomcat/util/http/parser/Host.java Sun Mar 19 20:50:36 2017 @@ -0,0 +1,136 @@ +/* + * 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.Reader; +import java.io.StringReader; + +import org.apache.tomcat.util.buf.ByteChunk; +import org.apache.tomcat.util.buf.MessageBytes; + +public class Host { + + /** + * Parse the given input as a HTTP Host header value. + * + * @param mb The host header value + * + * @return The position of ':' that separates the host from the port or -1 + * if it is not present + * + * @throws IllegalArgumentException If the host header value is not + * specification compliant + * + * @throws IOException If a problem occurs reading the data from the input + */ + public static int parse(MessageBytes mb) throws IOException { + return parse(new MessageBytesReader(mb)); + } + + + /** + * Parse the given input as a HTTP Host header value. + * + * @param string The host header value + * + * @return The position of ':' that separates the host from the port or -1 + * if it is not present + * + * @throws IllegalArgumentException If the host header value is not + * specification compliant + * + * @throws IOException If a problem occurs reading the data from the input + */ + public static int parse(String string) throws IOException { + return parse(new StringReader(string)); + } + + + private static int parse(Reader reader) throws IOException { + reader.mark(1); + int first = reader.read(); + reader.reset(); + if (HttpParser.isAlpha(first)) { + return HttpParser.readHostDomainName(reader); + } else if (HttpParser.isNumeric(first)) { + return HttpParser.readHostIPv4(reader, false); + } else if ('[' == first) { + return HttpParser.readHostIPv6(reader); + } else { + // Invalid + throw new IllegalArgumentException(); + } + } + + + private static class MessageBytesReader extends Reader { + + private final byte[] bytes; + private final int end; + private int pos; + private int mark; + + public MessageBytesReader(MessageBytes mb) { + ByteChunk bc = mb.getByteChunk(); + bytes = bc.getBytes(); + pos = bc.getOffset(); + end = bc.getEnd(); + } + + @Override + public int read(char[] cbuf, int off, int len) throws IOException { + for (int i = off; i < off + len; i++) { + cbuf[i] = (char) bytes[pos++]; + } + return len; + } + + @Override + public void close() throws IOException { + // NO-OP + } + + // Over-ridden methods to improve performance + + @Override + public int read() throws IOException { + if (pos < end) { + return bytes[pos++]; + } else { + return -1; + } + } + + // Methods to support mark/reset + + @Override + public boolean markSupported() { + return true; + } + + @Override + public void mark(int readAheadLimit) throws IOException { + mark = pos; + } + + @Override + public void reset() throws IOException { + pos = mark; + } + } +} Propchange: tomcat/trunk/java/org/apache/tomcat/util/http/parser/Host.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: tomcat/trunk/java/org/apache/tomcat/util/http/parser/HttpParser.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/http/parser/HttpParser.java?rev=1787662&r1=1787661&r2=1787662&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/http/parser/HttpParser.java (original) +++ tomcat/trunk/java/org/apache/tomcat/util/http/parser/HttpParser.java Sun Mar 19 20:50:36 2017 @@ -17,7 +17,7 @@ package org.apache.tomcat.util.http.parser; import java.io.IOException; -import java.io.StringReader; +import java.io.Reader; /** * HTTP header value parser implementation. Parsing HTTP headers as per RFC2616 @@ -42,6 +42,8 @@ public class HttpParser { 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]; + private static final boolean[] IS_ALPHA = new boolean[ARRAY_SIZE]; + private static final boolean[] IS_NUMERIC = new boolean[ARRAY_SIZE]; static { for (int i = 0; i < ARRAY_SIZE; i++) { @@ -82,6 +84,14 @@ public class HttpParser { if (i == 'H' || i == 'T' || i == 'P' || i == '/' || i == '.' || (i >= '0' && i <= '9')) { IS_HTTP_PROTOCOL[i] = true; } + + if (i >= '0' && i <= '9') { + IS_NUMERIC[i] = true; + } + + if (i >= 'a' && i <= 'z' || i >= 'A' && i <= 'Z') { + IS_ALPHA[i] = true; + } } } @@ -159,8 +169,30 @@ public class HttpParser { } + public static boolean isAlpha(int c) { + // Fast for valid alpha characters, slower for some incorrect + // ones + try { + return IS_ALPHA[c]; + } catch (ArrayIndexOutOfBoundsException ex) { + return false; + } + } + + + public static boolean isNumeric(int c) { + // Fast for valid numeric characters, slower for some incorrect + // ones + try { + return IS_NUMERIC[c]; + } catch (ArrayIndexOutOfBoundsException ex) { + return false; + } + } + + // Skip any LWS and return the next char - static int skipLws(StringReader input, boolean withReset) throws IOException { + static int skipLws(Reader input, boolean withReset) throws IOException { if (withReset) { input.mark(1); @@ -180,7 +212,7 @@ public class HttpParser { return c; } - static SkipResult skipConstant(StringReader input, String constant) throws IOException { + static SkipResult skipConstant(Reader input, String constant) throws IOException { int len = constant.length(); int c = skipLws(input, false); @@ -205,7 +237,7 @@ public class HttpParser { * available to read or <code>null</code> if data other than a * token was found */ - static String readToken(StringReader input) throws IOException { + static String readToken(Reader input) throws IOException { StringBuilder result = new StringBuilder(); int c = skipLws(input, false); @@ -229,7 +261,7 @@ public class HttpParser { * quoted string was found or null if the end of data was reached * before the quoted string was terminated */ - static String readQuotedString(StringReader input, boolean returnQuoted) throws IOException { + static String readQuotedString(Reader input, boolean returnQuoted) throws IOException { int c = skipLws(input, false); @@ -264,7 +296,7 @@ public class HttpParser { return result.toString(); } - static String readTokenOrQuotedString(StringReader input, boolean returnQuoted) + static String readTokenOrQuotedString(Reader input, boolean returnQuoted) throws IOException { // Go back so first non-LWS character is available to be read again @@ -289,7 +321,7 @@ public class HttpParser { * quoted token was found or null if the end of data was reached * before a quoted token was terminated */ - static String readQuotedToken(StringReader input) throws IOException { + static String readQuotedToken(Reader input) throws IOException { StringBuilder result = new StringBuilder(); boolean quoted = false; @@ -340,7 +372,7 @@ public class HttpParser { * @return the sequence of LHEX (minus any surrounding quotes) if any was * found, or <code>null</code> if data other LHEX was found */ - static String readLhex(StringReader input) throws IOException { + static String readLhex(Reader input) throws IOException { StringBuilder result = new StringBuilder(); boolean quoted = false; @@ -383,7 +415,7 @@ public class HttpParser { } } - static double readWeight(StringReader input, char delimiter) throws IOException { + static double readWeight(Reader input, char delimiter) throws IOException { int c = skipLws(input, false); if (c == -1 || c == delimiter) { // No q value just whitespace @@ -447,10 +479,177 @@ public class HttpParser { /** + * @return If inIPv6 us false, the position of ':' that separates the host + * from the port or -1 if it is not present. If inIPv6 is true, the + * number of characters read + */ + static int readHostIPv4(Reader reader, boolean inIPv6) throws IOException { + int octect = -1; + int octectCount = 1; + int c; + int pos = 0; + + do { + c = reader.read(); + if (c == '.') { + if (octect > -1 && octect < 256) { + // Valid + octectCount++; + octect = -1; + } else { + throw new IllegalArgumentException(); + } + } else if (isNumeric(c)) { + if (octect == -1) { + octect = c - '0'; + } else { + octect = octect * 10 + c - '0'; + } + } else if (c == ':') { + break; + } else if (c == -1) { + if (inIPv6) { + throw new IllegalArgumentException(); + } else { + pos = -1; + break; + } + } else if (c == ']') { + if (inIPv6) { + pos++; + break; + } else { + throw new IllegalArgumentException(); + } + } else { + throw new IllegalArgumentException(); + } + pos++; + } while (true); + + if (octectCount != 4) { + throw new IllegalArgumentException(); + } + if (octect < 0 || octect > 255) { + throw new IllegalArgumentException(); + } + + return pos; + } + + + /** + * @return The position of ':' that separates the host from the port or -1 + * if it is not present + */ + static int readHostIPv6(Reader reader) throws IOException { + // Must start with '[' + int c = reader.read(); + if (c != '[') { + throw new IllegalArgumentException(); + } + + int h16Count = 0; + int h16Size = 0; + int pos = 1; + boolean parsedDoubleColon = false; + boolean previousWasColon = false; + + do { + c = reader.read(); + if (h16Count == 0 && previousWasColon && c != ':') { + // Can't start with a single : + throw new IllegalArgumentException(); + } + if (HttpParser.isHex(c)) { + if (h16Size == 0) { + // Start of a new h16 block + previousWasColon = false; + h16Count++; + reader.mark(4); + } + h16Size++; + if (h16Size > 4) { + throw new IllegalArgumentException(); + } + } else if (c == ':') { + if (previousWasColon) { + // End of :: + if (parsedDoubleColon) { + // Only allowed one :: sequence + throw new IllegalArgumentException(); + } + parsedDoubleColon = true; + previousWasColon = false; + // :: represents at least one h16 block + h16Count++; + } else { + previousWasColon = true; + } + h16Size = 0; + } else if (c == ']') { + if (previousWasColon) { + // Can't end on a single ':' + throw new IllegalArgumentException(); + } + break; + } else if (c == '.') { + if (h16Count == 7 || h16Count < 7 && parsedDoubleColon) { + reader.reset(); + pos -= h16Size; + pos += readHostIPv4(reader, true); + h16Count++; + break; + } else { + throw new IllegalArgumentException(); + } + } else { + throw new IllegalArgumentException(); + } + pos++; + } while (true); + + if (h16Count > 8) { + throw new IllegalArgumentException(); + } else if (h16Count != 8 && !parsedDoubleColon) { + throw new IllegalArgumentException(); + } + + c = reader.read(); + if (c == ':') { + return pos + 1; + } else { + return -1; + } + } + + /** + * @return The position of ':' that separates the host from the port or -1 + * if it is not present + */ + static int readHostDomainName(Reader reader) throws IOException { + DomainParseState state = DomainParseState.NEW; + int pos = 0; + + while (state.mayContinue()) { + state = state.next(reader.read()); + pos++; + } + + if (DomainParseState.COLON == state) { + // State identifies the state of the previous character + return pos - 1; + } else { + return -1; + } + } + + + /** * Skips all characters until EOF or the specified target is found. Normally * used to skip invalid input until the next separator. */ - static SkipResult skipUntil(StringReader input, int c, char target) throws IOException { + static SkipResult skipUntil(Reader input, int c, char target) throws IOException { while (c != -1 && c != target) { c = input.read(); } @@ -460,4 +659,74 @@ public class HttpParser { return SkipResult.FOUND; } } + + + private static enum DomainParseState { + NEW( true, false, false, false, false, false), + ALPHA( true, true, true, true, true, true), + NUMERIC( true, true, true, true, true, true), + PERIOD( true, false, false, false, true, true), + HYPHEN( true, true, true, false, false, false), + COLON( false, false, false, false, false, false), + END( false, false, false, false, false, false); + + private final boolean mayContinue; + private final boolean allowsNumeric; + private final boolean allowsHyphen; + private final boolean allowsPeriod; + private final boolean allowsColon; + private final boolean allowsEnd; + + private DomainParseState(boolean mayContinue, boolean allowsNumeric, boolean allowsHyphen, + boolean allowsPeriod, boolean allowsColon, boolean allowsEnd) { + this.mayContinue = mayContinue; + this.allowsNumeric = allowsNumeric; + this.allowsHyphen = allowsHyphen; + this.allowsPeriod = allowsPeriod; + this.allowsColon = allowsColon; + this.allowsEnd = allowsEnd; + } + + public boolean mayContinue() { + return mayContinue; + } + + public DomainParseState next(int c) { + if (HttpParser.isAlpha(c)) { + return ALPHA; + } else if (HttpParser.isNumeric(c)) { + if (allowsNumeric) { + return NUMERIC; + } else { + throw new IllegalArgumentException(); + } + } else if (c == '.') { + if (allowsPeriod) { + return PERIOD; + } else { + throw new IllegalArgumentException(); + } + } else if (c == ':') { + if (allowsColon) { + return COLON; + } else { + throw new IllegalArgumentException(); + } + } else if (c == -1) { + if (allowsEnd) { + return END; + } else { + throw new IllegalArgumentException(); + } + } else if (c == '-') { + if (allowsHyphen) { + return HYPHEN; + } else { + throw new IllegalArgumentException(); + } + } else { + throw new IllegalArgumentException(); + } + } + } } Added: tomcat/trunk/test/org/apache/tomcat/util/http/parser/TestHttpParserHost.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/util/http/parser/TestHttpParserHost.java?rev=1787662&view=auto ============================================================================== --- tomcat/trunk/test/org/apache/tomcat/util/http/parser/TestHttpParserHost.java (added) +++ tomcat/trunk/test/org/apache/tomcat/util/http/parser/TestHttpParserHost.java Sun Mar 19 20:50:36 2017 @@ -0,0 +1,178 @@ +/* + * 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.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class TestHttpParserHost { + + private static final Class<? extends Exception> IAE = IllegalArgumentException.class; + + @Parameter(0) + public TestType testType; + + @Parameter(1) + public String input; + + @Parameter(2) + public Integer expectedResult; + + @Parameter(3) + public Class<? extends Exception> expectedException; + + + @Parameters + public static Collection<Object[]> inputs() { + List<Object[]> result = new ArrayList<>(); + // IPv4 - valid + result.add(new Object[] { TestType.IPv4, "127.0.0.1", Integer.valueOf(-1), null} ); + result.add(new Object[] { TestType.IPv4, "127.0.0.1:8080", Integer.valueOf(9), null} ); + result.add(new Object[] { TestType.IPv4, "0.0.0.0", Integer.valueOf(-1), null} ); + result.add(new Object[] { TestType.IPv4, "0.0.0.0:8080", Integer.valueOf(7), null} ); + // IPv4 - invalid + result.add(new Object[] { TestType.IPv4, "0", Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.IPv4, "0.0", Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.IPv4, "0.0.0", Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.IPv4, ".0.0.0", Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.IPv4, "0.0.0.", Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.IPv4, "256.0.0.0", Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.IPv4, "0.256.0.0", Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.IPv4, "0.0.256.0", Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.IPv4, "0.0.0.256", Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.IPv4, "0.a.0.0", Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.IPv4, "0..0.0", Integer.valueOf(-1), IAE} ); + // Domain Name - valid + result.add(new Object[] { TestType.DOMAIN_NAME, "localhost", Integer.valueOf(-1), null} ); + result.add(new Object[] { TestType.DOMAIN_NAME, "localhost:8080", Integer.valueOf(9), null} ); + result.add(new Object[] { TestType.DOMAIN_NAME, "tomcat.apache.org", Integer.valueOf(-1), null} ); + result.add(new Object[] { TestType.DOMAIN_NAME, "tomcat.apache.org:8080", Integer.valueOf(17), null} ); + // Domain Name - invalid + result.add(new Object[] { TestType.DOMAIN_NAME, ".foo.bar", Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.DOMAIN_NAME, "2foo.bar", Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.DOMAIN_NAME, "-foo.bar", Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.DOMAIN_NAME, "^foo.bar", Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.DOMAIN_NAME, "foo-.bar", Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.DOMAIN_NAME, "f*oo.bar", Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.DOMAIN_NAME, "foo..bar", Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.DOMAIN_NAME, "foo.2bar", Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.DOMAIN_NAME, "foo.-bar", Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.DOMAIN_NAME, "foo.^bar", Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.DOMAIN_NAME, "foo.bar-", Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.DOMAIN_NAME, "foo.b*ar", Integer.valueOf(-1), IAE} ); + // IPv6 - valid + result.add(new Object[] { TestType.IPv6, "[::1]", Integer.valueOf(-1), null} ); + result.add(new Object[] { TestType.IPv6, "[::1]:8080", Integer.valueOf(5), null} ); + result.add(new Object[] { TestType.IPv6, "[1::1]", Integer.valueOf(-1), null} ); + result.add(new Object[] { TestType.IPv6, "[1::1]:8080", Integer.valueOf(6), null} ); + result.add(new Object[] { TestType.IPv6, "[A::A]", Integer.valueOf(-1), null} ); + result.add(new Object[] { TestType.IPv6, "[A::A]:8080", Integer.valueOf(6), null} ); + result.add(new Object[] { TestType.IPv6, "[A:0::A]", Integer.valueOf(-1), null} ); + result.add(new Object[] { TestType.IPv6, "[A:0::A]:8080", Integer.valueOf(8), null} ); + result.add(new Object[] { TestType.IPv6, "[1234:5678:90AB:CDEF:1234:5678:90AB:CDEF]", + Integer.valueOf(-1), null} ); + result.add(new Object[] { TestType.IPv6, "[1234:5678:90AB:CDEF:1234:5678:90AB:CDEF]:8080", + Integer.valueOf(41), null} ); + result.add(new Object[] { TestType.IPv6, "[::5678:90AB:CDEF:1234:5678:90AB:CDEF]:8080", + Integer.valueOf(38), null} ); + result.add(new Object[] { TestType.IPv6, "[1234:5678:90AB:CDEF:1234:5678:90AB::]:8080", + Integer.valueOf(38), null} ); + result.add(new Object[] { TestType.IPv6, "[0:0:0:0:0:0:0:0]", Integer.valueOf(-1), null} ); + result.add(new Object[] { TestType.IPv6, "[0:0:0:0:0:0:0:0]:8080", + Integer.valueOf(17), null} ); + result.add(new Object[] { TestType.IPv6, "[::127.0.0.1]", Integer.valueOf(-1), null} ); + result.add(new Object[] { TestType.IPv6, "[::127.0.0.1]:8080", Integer.valueOf(13), null} ); + result.add(new Object[] { TestType.IPv6, "[1::127.0.0.1]", Integer.valueOf(-1), null} ); + result.add(new Object[] { TestType.IPv6, "[1::127.0.0.1]:8080", Integer.valueOf(14), null} ); + result.add(new Object[] { TestType.IPv6, "[A::127.0.0.1]", Integer.valueOf(-1), null} ); + result.add(new Object[] { TestType.IPv6, "[A::127.0.0.1]:8080", Integer.valueOf(14), null} ); + result.add(new Object[] { TestType.IPv6, "[A:0::127.0.0.1]", Integer.valueOf(-1), null} ); + result.add(new Object[] { TestType.IPv6, "[A:0::127.0.0.1]:8080", Integer.valueOf(16), null} ); + result.add(new Object[] { TestType.IPv6, "[1234:5678:90AB:CDEF:1234:5678:127.0.0.1]", + Integer.valueOf(-1), null} ); + result.add(new Object[] { TestType.IPv6, "[1234:5678:90AB:CDEF:1234:5678:127.0.0.1]:8080", + Integer.valueOf(41), null} ); + result.add(new Object[] { TestType.IPv6, "[::5678:90AB:CDEF:1234:5678:127.0.0.1]:8080", + Integer.valueOf(38), null} ); + result.add(new Object[] { TestType.IPv6, "[0:0:0:0:0:0:127.0.0.1]", Integer.valueOf(-1), null} ); + result.add(new Object[] { TestType.IPv6, "[0:0:0:0:0:0:127.0.0.1]:8080", + Integer.valueOf(23), null} ); + // IPv6 - invalid + result.add(new Object[] { TestType.IPv6, "[1234:5678:90AB:CDEF:1234:127.0.0.1]", + Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.IPv6, "[1234:5678:90AB:CDEF:1234:5678:127.0.0.1", + Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.IPv6, "[0::0::0]", Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.IPv6, "[0:0:G:0:0:0:0:0]", Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.IPv6, "[00000:0:0:0:0:0:0:0]", Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.IPv6, "[1234:5678:90AB:CDEF:1234:5678:90AB:]", + Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.IPv6, "[1234:5678:90AB:CDEF:1234:5678:90AB:CDEF", + Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.IPv6, "[0::0::127.0.0.1]", Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.IPv6, "[0:0:G:0:0:0:127.0.0.1]", Integer.valueOf(-1), IAE} ); + result.add(new Object[] { TestType.IPv6, "[00000:0:0:0:0:0:127.0.0.1]", Integer.valueOf(-1), IAE} ); + return result; + } + + + @Test + public void testHost() { + Class<? extends Exception> exceptionClass = null; + int result = -1; + try { + StringReader sr = new StringReader(input); + switch(testType) { + case IPv4: + result = HttpParser.readHostIPv4(sr, false); + break; + case IPv6: + result = HttpParser.readHostIPv6(sr); + break; + case DOMAIN_NAME: + result = HttpParser.readHostDomainName(sr); + break; + + } + } catch (Exception e) { + exceptionClass = e.getClass(); + } + Assert.assertEquals(input, expectedResult.intValue(), result); + if (expectedException == null) { + Assert.assertNull(input, exceptionClass); + } else { + Assert.assertTrue(input, expectedException.isAssignableFrom(exceptionClass)); + } + } + + + private static enum TestType { + IPv4, + IPv6, + DOMAIN_NAME + } +} Propchange: tomcat/trunk/test/org/apache/tomcat/util/http/parser/TestHttpParserHost.java ------------------------------------------------------------------------------ svn:eol-style = native Added: tomcat/trunk/test/org/apache/tomcat/util/http/parser/TesterHostPerformance.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/util/http/parser/TesterHostPerformance.java?rev=1787662&view=auto ============================================================================== --- tomcat/trunk/test/org/apache/tomcat/util/http/parser/TesterHostPerformance.java (added) +++ tomcat/trunk/test/org/apache/tomcat/util/http/parser/TesterHostPerformance.java Sun Mar 19 20:50:36 2017 @@ -0,0 +1,75 @@ +/* + * 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.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +import org.apache.tomcat.util.buf.MessageBytes; + +@RunWith(Parameterized.class) +public class TesterHostPerformance { + + @Parameters + public static Collection<Object[]> inputs() { + List<Object[]> result = new ArrayList<>(); + result.add(new Object[] { "localhost" }); + result.add(new Object[] { "tomcat.apache.org" }); + result.add(new Object[] { "127.0.0.1" }); + result.add(new Object[] { "255.255.255.255" }); + result.add(new Object[] { "[::1]" }); + result.add(new Object[] { "[0123:4567:89AB:CDEF:0123:4567:89AB:CDEF]" }); + return result; + } + + @Parameter(0) + public String hostname; + + private static final int ITERATIONS = 100000000; + + @Test + public void testParseHost() throws Exception { + long start = System.nanoTime(); + for (int i = 0; i < ITERATIONS; i++) { + Host.parse(hostname); + } + long time = System.nanoTime() - start; + + System.out.println("St " + hostname + ": " + ITERATIONS + " iterations in " + time + "ns"); + System.out.println("St " + hostname + ": " + ITERATIONS * 1000000000.0/time + " iterations per second"); + + MessageBytes mb = MessageBytes.newInstance(); + mb.setString(hostname); + mb.toBytes(); + + start = System.nanoTime(); + for (int i = 0; i < ITERATIONS; i++) { + Host.parse(mb); + } + time = System.nanoTime() - start; + + System.out.println("MB " + hostname + ": " + ITERATIONS + " iterations in " + time + "ns"); + System.out.println("MB " + hostname + ": " + ITERATIONS * 1000000000.0/time + " iterations per second"); + } +} Propchange: tomcat/trunk/test/org/apache/tomcat/util/http/parser/TesterHostPerformance.java ------------------------------------------------------------------------------ svn:eol-style = native --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org