You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2017/03/19 20:50:37 UTC

svn commit: r1787662 - in /tomcat/trunk: java/org/apache/tomcat/util/http/parser/ test/org/apache/tomcat/util/http/parser/

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-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org