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 2016/07/03 20:24:18 UTC

svn commit: r1751173 - in /tomcat/trunk: java/org/apache/catalina/mapper/Mapper.java test/org/apache/catalina/mapper/TestMapper.java test/org/apache/catalina/mapper/TestMapperPerformance.java webapps/docs/changelog.xml webapps/docs/config/host.xml

Author: markt
Date: Sun Jul  3 20:24:18 2016
New Revision: 1751173

URL: http://svn.apache.org/viewvc?rev=1751173&view=rev
Log:
The original request for regular expression support would be too expensive to implement.
This commit adds support for wild card host names.
It adds overhead for requests where the Host header is not an exact match since the code now has to convert the name in the header to the wild card form and then search for that.
However, this overhead is offset by caching the default host so it is not necessary to do a look up for the default host.
I've also expanded the performance tests. On my laptop the before and after results are broadly similar with some small improvements and some small increases.

Modified:
    tomcat/trunk/java/org/apache/catalina/mapper/Mapper.java
    tomcat/trunk/test/org/apache/catalina/mapper/TestMapper.java
    tomcat/trunk/test/org/apache/catalina/mapper/TestMapperPerformance.java
    tomcat/trunk/webapps/docs/changelog.xml
    tomcat/trunk/webapps/docs/config/host.xml

Modified: tomcat/trunk/java/org/apache/catalina/mapper/Mapper.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/mapper/Mapper.java?rev=1751173&r1=1751172&r2=1751173&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/mapper/Mapper.java (original)
+++ tomcat/trunk/java/org/apache/catalina/mapper/Mapper.java Sun Jul  3 20:24:18 2016
@@ -66,6 +66,7 @@ public final class Mapper {
      * Default host name.
      */
     private String defaultHostName = null;
+    private volatile MappedHost defaultHost = null;
 
 
     /**
@@ -83,10 +84,16 @@ public final class Mapper {
      *
      * @param defaultHostName Default host name
      */
-    public void setDefaultHostName(String defaultHostName) {
-        this.defaultHostName = defaultHostName;
+    public synchronized void setDefaultHostName(String defaultHostName) {
+        this.defaultHostName = renameWildcardHost(defaultHostName);
+        if (this.defaultHostName == null) {
+            defaultHost = null;
+        } else {
+            defaultHost = exactFind(hosts, this.defaultHostName);
+        }
     }
 
+
     /**
      * Add a new host to the mapper.
      *
@@ -96,10 +103,14 @@ public final class Mapper {
      */
     public synchronized void addHost(String name, String[] aliases,
                                      Host host) {
+        name = renameWildcardHost(name);
         MappedHost[] newHosts = new MappedHost[hosts.length + 1];
         MappedHost newHost = new MappedHost(name, host);
         if (insertMap(hosts, newHosts, newHost)) {
             hosts = newHosts;
+            if (newHost.name.equals(defaultHostName)) {
+                defaultHost = newHost;
+            }
             if (log.isDebugEnabled()) {
                 log.debug(sm.getString("mapper.addHost.success", name));
             }
@@ -122,6 +133,7 @@ public final class Mapper {
         }
         List<MappedHost> newAliases = new ArrayList<>(aliases.length);
         for (String alias : aliases) {
+            alias = renameWildcardHost(alias);
             MappedHost newAlias = new MappedHost(alias, newHost);
             if (addHostAliasImpl(newAlias)) {
                 newAliases.add(newAlias);
@@ -137,6 +149,7 @@ public final class Mapper {
      * @param name Virtual host name
      */
     public synchronized void removeHost(String name) {
+        name = renameWildcardHost(name);
         // Find and remove the old host
         MappedHost host = exactFind(hosts, name);
         if (host == null || host.isAlias()) {
@@ -165,6 +178,7 @@ public final class Mapper {
             // just in case...
             return;
         }
+        alias = renameWildcardHost(alias);
         MappedHost newAlias = new MappedHost(alias, realHost);
         if (addHostAliasImpl(newAlias)) {
             realHost.addAlias(newAlias);
@@ -175,6 +189,9 @@ public final class Mapper {
         MappedHost[] newHosts = new MappedHost[hosts.length + 1];
         if (insertMap(hosts, newHosts, newAlias)) {
             hosts = newHosts;
+            if (newAlias.name.equals(defaultHostName)) {
+                defaultHost = newAlias;
+            }
             if (log.isDebugEnabled()) {
                 log.debug(sm.getString("mapper.addHostAlias.success",
                         newAlias.name, newAlias.getRealHostName()));
@@ -203,6 +220,7 @@ public final class Mapper {
      * @param alias The alias to remove
      */
     public synchronized void removeHostAlias(String alias) {
+        alias = renameWildcardHost(alias);
         // Find and remove the alias
         MappedHost hostMapping = exactFind(hosts, alias);
         if (hostMapping == null || !hostMapping.isAlias()) {
@@ -245,6 +263,8 @@ public final class Mapper {
             String version, Context context, String[] welcomeResources,
             WebResourceRoot resources, Collection<WrapperMappingInfo> wrappers) {
 
+        hostName = renameWildcardHost(hostName);
+
         MappedHost mappedHost  = exactFind(hosts, hostName);
         if (mappedHost == null) {
             addHost(hostName, new String[0], host);
@@ -309,6 +329,7 @@ public final class Mapper {
     public void removeContextVersion(Context ctxt, String hostName,
             String path, String version) {
 
+        hostName = renameWildcardHost(hostName);
         contextObjectToContextVersionMap.remove(ctxt);
 
         MappedHost host = exactFind(hosts, hostName);
@@ -352,7 +373,7 @@ public final class Mapper {
      */
     public void pauseContextVersion(Context ctxt, String hostName,
             String contextPath, String version) {
-
+        hostName = renameWildcardHost(hostName);
         ContextVersion contextVersion = findContextVersion(hostName,
                 contextPath, version, true);
         if (contextVersion == null || !ctxt.equals(contextVersion.object)) {
@@ -394,6 +415,7 @@ public final class Mapper {
     public void addWrapper(String hostName, String contextPath, String version,
                            String path, Wrapper wrapper, boolean jspWildCard,
                            boolean resourceOnly) {
+        hostName = renameWildcardHost(hostName);
         ContextVersion contextVersion = findContextVersion(hostName,
                 contextPath, version, false);
         if (contextVersion == null) {
@@ -404,6 +426,7 @@ public final class Mapper {
 
     public void addWrappers(String hostName, String contextPath,
             String version, Collection<WrapperMappingInfo> wrappers) {
+        hostName = renameWildcardHost(hostName);
         ContextVersion contextVersion = findContextVersion(hostName,
                 contextPath, version, false);
         if (contextVersion == null) {
@@ -504,6 +527,7 @@ public final class Mapper {
      */
     public void removeWrapper(String hostName, String contextPath,
             String version, String path) {
+        hostName = renameWildcardHost(hostName);
         ContextVersion contextVersion = findContextVersion(hostName,
                 contextPath, version, true);
         if (contextVersion == null || contextVersion.isPaused()) {
@@ -588,6 +612,7 @@ public final class Mapper {
      */
     public void addWelcomeFile(String hostName, String contextPath, String version,
             String welcomeFile) {
+        hostName = renameWildcardHost(hostName);
         ContextVersion contextVersion = findContextVersion(hostName, contextPath, version, false);
         if (contextVersion == null) {
             return;
@@ -610,6 +635,7 @@ public final class Mapper {
      */
     public void removeWelcomeFile(String hostName, String contextPath,
             String version, String welcomeFile) {
+        hostName = renameWildcardHost(hostName);
         ContextVersion contextVersion = findContextVersion(hostName, contextPath, version, false);
         if (contextVersion == null || contextVersion.isPaused()) {
             return;
@@ -642,6 +668,7 @@ public final class Mapper {
      * @param version     The version of the context to be cleared
      */
     public void clearWelcomeFiles(String hostName, String contextPath, String version) {
+        hostName = renameWildcardHost(hostName);
         ContextVersion contextVersion = findContextVersion(hostName, contextPath, version, false);
         if (contextVersion == null) {
             return;
@@ -720,12 +747,24 @@ public final class Mapper {
         MappedHost[] hosts = this.hosts;
         MappedHost mappedHost = exactFindIgnoreCase(hosts, host);
         if (mappedHost == null) {
-            if (defaultHostName == null) {
-                return;
+            // Note: Internally, the Mapper does not use the leading * on a
+            //       wildcard host. This is to allow this shortcut.
+            int firstDot = host.indexOf('.');
+            if (firstDot > -1) {
+                int offset = host.getOffset();
+                try {
+                    host.setOffset(firstDot + offset);
+                    mappedHost = exactFindIgnoreCase(hosts, host);
+                } finally {
+                    // Make absolutely sure this gets reset
+                    host.setOffset(offset);
+                }
             }
-            mappedHost = exactFind(hosts, defaultHostName);
             if (mappedHost == null) {
-                return;
+                mappedHost = defaultHost;
+                if (mappedHost == null) {
+                    return;
+                }
             }
         }
         mappingData.host = mappedHost.object;
@@ -1497,6 +1536,22 @@ public final class Mapper {
     }
 
 
+    /*
+     * To simplify the mapping process, wild card hosts take the form
+     * ".apache.org" rather than "*.apache.org" internally. However, for ease
+     * of use the external form remains "*.apache.org". Any host name passed
+     * into this class needs to be passed through this method to rename and
+     * wild card host names from the external to internal form.
+     */
+    private static String renameWildcardHost(String hostName) {
+        if (hostName.startsWith("*.")) {
+            return hostName.substring(1);
+        } else {
+            return hostName;
+        }
+    }
+
+
     // ------------------------------------------------- MapElement Inner Class
 
 

Modified: tomcat/trunk/test/org/apache/catalina/mapper/TestMapper.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/mapper/TestMapper.java?rev=1751173&r1=1751172&r2=1751173&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/mapper/TestMapper.java (original)
+++ tomcat/trunk/test/org/apache/catalina/mapper/TestMapper.java Sun Jul  3 20:24:18 2016
@@ -88,6 +88,9 @@ public class TestMapper extends LoggingB
         mapper.addHost("zzzuyopjvewpovewjhfewoih", new String[0], createHost("blah12"));
         mapper.addHost("xxxxgqwiwoih", new String[0], createHost("blah13"));
         mapper.addHost("qwigqwiwoih", new String[0], createHost("blah14"));
+        mapper.addHost("qwerty.net", new String[0], createHost("blah15"));
+        mapper.addHost("*.net", new String[0], createHost("blah16"));
+        mapper.addHost("zzz.com", new String[0], createHost("blah17"));
         mapper.addHostAlias("iowejoiejfoiew", "iowejoiejfoiew_alias");
 
         mapper.setDefaultHostName("ylwrehirkuewh");
@@ -135,6 +138,14 @@ public class TestMapper extends LoggingB
                 null,
                 Arrays.asList(new WrapperMappingInfo[] { new WrapperMappingInfo(
                         "/bobou/*", createWrapper("wrapper7"), false, false) }));
+
+        host = createHost("blah16");
+        mapper.addContextVersion("*.net", host, "", "0", createContext("context4"),
+                new String[0], null, null);
+        mapper.addWrappers("*.net", "", "0", Arrays
+                .asList(new WrapperMappingInfo[] {
+                        new WrapperMappingInfo("/",
+                                createWrapper("context4-defaultWrapper"), false, false) }));
     }
 
     @Test
@@ -153,14 +164,14 @@ public class TestMapper extends LoggingB
         mapper.addHostAlias("iowejoiejfoiew", "iowejoiejfoiew_alias");
 
         // Check we have the right number
-        // (added 16 including one host alias. Three duplicates do not increase the count.)
-        assertEquals(16, mapper.hosts.length);
+        // (added 17 including one host alias. Three duplicates do not increase the count.)
+        assertEquals(19, mapper.hosts.length);
 
         // Make sure adding a duplicate *does not* overwrite
-        final int iowPos = 3;
+        final int iowPos = 4;
         assertEquals("blah7", mapper.hosts[iowPos].object.getName());
 
-        final int qwigPos = 8;
+        final int qwigPos = 10;
         assertEquals("blah14", mapper.hosts[qwigPos].object.getName());
 
         // Check for alphabetical order of host names
@@ -185,38 +196,38 @@ public class TestMapper extends LoggingB
         Host hostZ = createHost("zzzz");
         Context contextZ = createContext("contextZ");
 
-        assertEquals(16, mapper.hosts.length);
+        assertEquals(19, mapper.hosts.length);
         mapper.addContextVersion("zzzz", hostZ, "/", "", contextZ, null, null,
                 null);
-        assertEquals(17, mapper.hosts.length);
+        assertEquals(20, mapper.hosts.length);
 
         mapper.addHost("zzzz", new String[] { "zzzz_alias1", "zzzz_alias2" },
                 hostZ);
-        assertEquals(19, mapper.hosts.length);
+        assertEquals(22, mapper.hosts.length);
 
-        assertEquals("zzzz", mapper.hosts[16].name);
-        assertEquals("zzzz_alias1", mapper.hosts[17].name);
-        assertEquals("zzzz_alias2", mapper.hosts[18].name);
-        assertEquals(2, mapper.hosts[16].getAliases().size());
+        assertEquals("zzzz", mapper.hosts[19].name);
+        assertEquals("zzzz_alias1", mapper.hosts[20].name);
+        assertEquals("zzzz_alias2", mapper.hosts[21].name);
+        assertEquals(2, mapper.hosts[19].getAliases().size());
         assertSame(contextZ,
-                mapper.hosts[16].contextList.contexts[0].versions[0].object);
+                mapper.hosts[19].contextList.contexts[0].versions[0].object);
         assertSame(contextZ,
-                mapper.hosts[18].contextList.contexts[0].versions[0].object);
+                mapper.hosts[21].contextList.contexts[0].versions[0].object);
     }
 
     @Test
     public void testRemoveHost() {
-        assertEquals(16, mapper.hosts.length);
+        assertEquals(19, mapper.hosts.length);
         mapper.removeHostAlias("iowejoiejfoiew");
         mapper.removeHost("iowejoiejfoiew_alias");
-        assertEquals(16, mapper.hosts.length); // No change
+        assertEquals(19, mapper.hosts.length); // No change
         mapper.removeHostAlias("iowejoiejfoiew_alias");
-        assertEquals(15, mapper.hosts.length); // Removed
+        assertEquals(18, mapper.hosts.length); // Removed
 
         mapper.addHostAlias("iowejoiejfoiew", "iowejoiejfoiew_alias");
-        assertEquals(16, mapper.hosts.length);
+        assertEquals(19, mapper.hosts.length);
 
-        final int iowPos = 3;
+        final int iowPos = 4;
         Mapper.MappedHost hostMapping = mapper.hosts[iowPos];
         Mapper.MappedHost aliasMapping = mapper.hosts[iowPos + 1];
         assertEquals("iowejoiejfoiew_alias", aliasMapping.name);
@@ -229,7 +240,7 @@ public class TestMapper extends LoggingB
         assertSame(hostMapping, aliasMapping.getRealHost());
 
         mapper.removeHost("iowejoiejfoiew");
-        assertEquals(14, mapper.hosts.length); // Both host and alias removed
+        assertEquals(17, mapper.hosts.length); // Both host and alias removed
         for (Mapper.MappedHost host : mapper.hosts) {
             assertTrue(host.name, !host.name.startsWith("iowejoiejfoiew"));
         }
@@ -240,6 +251,8 @@ public class TestMapper extends LoggingB
         MappingData mappingData = new MappingData();
         MessageBytes host = MessageBytes.newInstance();
         host.setString("iowejoiejfoiew");
+        MessageBytes wildcard = MessageBytes.newInstance();
+        wildcard.setString("foo.net");
         MessageBytes alias = MessageBytes.newInstance();
         alias.setString("iowejoiejfoiew_alias");
         MessageBytes uri = MessageBytes.newInstance();
@@ -271,6 +284,20 @@ public class TestMapper extends LoggingB
         assertTrue(mappingData.redirectPath.isNull());
 
         mappingData.recycle();
+        uri.recycle();
+        uri.setString("/foo/bar/bla/bobou/foo");
+        uri.toChars();
+        uri.getCharChunk().setLimit(-1);
+        mapper.map(wildcard, uri, null, mappingData);
+        assertEquals("blah16", mappingData.host.getName());
+        assertEquals("context4", mappingData.context.getName());
+        assertEquals("context4-defaultWrapper", mappingData.wrapper.getName());
+        assertEquals("", mappingData.contextPath.toString());
+        assertEquals("/foo/bar/bla/bobou/foo", mappingData.wrapperPath.toString());
+        assertTrue(mappingData.pathInfo.isNull());
+        assertTrue(mappingData.redirectPath.isNull());
+
+        mappingData.recycle();
         uri.setString("/foo/bar/bla/bobou/foo");
         uri.toChars();
         uri.getCharChunk().setLimit(-1);
@@ -287,7 +314,7 @@ public class TestMapper extends LoggingB
     @Test
     public void testAddRemoveContextVersion() throws Exception {
         final String hostName = "iowejoiejfoiew";
-        final int iowPos = 3;
+        final int iowPos = 4;
         final String contextPath = "/foo/bar";
         final int contextPos = 2;
 
@@ -394,7 +421,7 @@ public class TestMapper extends LoggingB
     @Test
     public void testReloadContextVersion() throws Exception {
         final String hostName = "iowejoiejfoiew";
-        final int iowPos = 3;
+        final int iowPos = 4;
         final String contextPath = "/foo/bar";
         final int contextPos = 2;
 

Modified: tomcat/trunk/test/org/apache/catalina/mapper/TestMapperPerformance.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/mapper/TestMapperPerformance.java?rev=1751173&r1=1751172&r2=1751173&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/mapper/TestMapperPerformance.java (original)
+++ tomcat/trunk/test/org/apache/catalina/mapper/TestMapperPerformance.java Sun Jul  3 20:24:18 2016
@@ -26,24 +26,42 @@ public class TestMapperPerformance exten
 
     @Test
     public void testPerformance() throws Exception {
+        String[] requestedHostNames = new String[] {
+                "xxxxxxxxxxx",
+                "iowejoiejfoiew",
+                "iowejoiejfoiex",
+                "owefojiwefoi",
+                "owefojiwefoix",
+                "qwerty.net",
+                "foo.net",
+                "zzz.com",
+                "abc.com"};
+
+        for (String requestedHostName : requestedHostNames) {
+            testPerformance(requestedHostName);
+        }
+    }
+
+    private void testPerformance(String requestedHostName) throws Exception {
         // Takes ~1s on markt's laptop. If this takes more than 5s something
         // probably needs looking at. If this fails repeatedly then we may need
         // to increase this limit.
         final long maxTime = 5000;
-        long time = testPerformanceImpl();
+        long time = testPerformanceImpl(requestedHostName);
+        log.info("Host [" + requestedHostName + "], Time [" + time + "]ms");
         if (time >= maxTime) {
             // Rerun to reject occasional failures, e.g. because of gc
             log.warn("testPerformance() test completed in " + time + " ms");
-            time = testPerformanceImpl();
+            time = testPerformanceImpl(requestedHostName);
             log.warn("testPerformance() test rerun completed in " + time + " ms");
         }
         assertTrue(String.valueOf(time), time < maxTime);
     }
 
-    private long testPerformanceImpl() throws Exception {
+    private long testPerformanceImpl(String requestedHostName) throws Exception {
         MappingData mappingData = new MappingData();
         MessageBytes host = MessageBytes.newInstance();
-        host.setString("iowejoiejfoiew");
+        host.setString(requestedHostName);
         MessageBytes uri = MessageBytes.newInstance();
         uri.setString("/foo/bar/blah/bobou/foo");
         uri.toChars();

Modified: tomcat/trunk/webapps/docs/changelog.xml
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1751173&r1=1751172&r2=1751173&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Sun Jul  3 20:24:18 2016
@@ -48,6 +48,12 @@
   <subsection name="Catalina">
     <changelog>
       <fix>
+        <bug>18500</bug>: Add limited support for wildcard host names and host
+        aliases. Names of the form <code>*.domainname</code> are now permitted.
+        Note that an exact host name match takes precedence over a wild card
+        host name match. (markt)
+      </fix>
+      <fix>
         <bug>57705</bug>: Add debug logging for requests denied by the remote
         host and remote address valves and filters. Based on a patch by Graham
         Leggett. (markt)

Modified: tomcat/trunk/webapps/docs/config/host.xml
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/host.xml?rev=1751173&r1=1751172&r2=1751173&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/config/host.xml (original)
+++ tomcat/trunk/webapps/docs/config/host.xml Sun Jul  3 20:24:18 2016
@@ -192,7 +192,10 @@
         have a name that matches the <code>defaultHost</code> setting for that
         Engine.  See <a href="#Host_Name_Aliases">Host Name Aliases</a> for
         information on how to assign more than one network name to the same
-        virtual host.</p>
+        virtual host. If the name takes the form of <code>*.domainname</code>
+        (e.g. <code>*.apache.org</code>) then it will be treated as a match for
+        any host in that domain unless a host that has an exactly matching name
+        is found.</p>
       </attribute>
 
       <attribute name="startStopThreads" required="false">
@@ -476,6 +479,10 @@
     involved must be registered in your DNS server to resolve to the
     same computer that is running this instance of Catalina.</p>
 
+    <p>Aliases may also use the wildcard form (<code>*.domainname</code>)
+       permitted for the <strong>name</strong> attribute of a
+       <strong>Host</strong>.</p>
+
   </subsection>
 
 



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


Re: svn commit: r1751173 - in /tomcat/trunk: java/org/apache/catalina/mapper/Mapper.java test/org/apache/catalina/mapper/TestMapper.java test/org/apache/catalina/mapper/TestMapperPerformance.java webapps/docs/changelog.xml webapps/docs/config/host.xml

Posted by Christopher Schultz <ch...@christopherschultz.net>.
Mark,

On 7/7/16 11:53 AM, Mark Thomas wrote:
> On 06/07/2016 22:55, Christopher Schultz wrote:
>> Mark,
>>
>> On 7/3/16 4:24 PM, markt@apache.org wrote:
>>> Author: markt
>>> Date: Sun Jul  3 20:24:18 2016
>>> New Revision: 1751173
>>>
>>> URL: http://svn.apache.org/viewvc?rev=1751173&view=rev
>>> Log:
>>> The original request for regular expression support would be too expensive to implement.
>>> This commit adds support for wild card host names.
>>> It adds overhead for requests where the Host header is not an exact match since the code now has to convert the name in the header to the wild card form and then search for that.
>>> However, this overhead is offset by caching the default host so it is not necessary to do a look up for the default host.
>>> I've also expanded the performance tests. On my laptop the before and after results are broadly similar with some small improvements and some small increases.
> 
> <snip/>
> 
> 
>>> @@ -720,12 +747,24 @@ public final class Mapper {
>>>          MappedHost[] hosts = this.hosts;
>>>          MappedHost mappedHost = exactFindIgnoreCase(hosts, host);
>>>          if (mappedHost == null) {
>>> -            if (defaultHostName == null) {
>>> -                return;
>>> +            // Note: Internally, the Mapper does not use the leading * on a
>>> +            //       wildcard host. This is to allow this shortcut.
>>> +            int firstDot = host.indexOf('.');
>>> +            if (firstDot > -1) {
>>> +                int offset = host.getOffset();
>>> +                try {
>>> +                    host.setOffset(firstDot + offset);
>>> +                    mappedHost = exactFindIgnoreCase(hosts, host);
>>> +                } finally {
>>> +                    // Make absolutely sure this gets reset
>>> +                    host.setOffset(offset);
>>> +                }
>>>              }
>>> -            mappedHost = exactFind(hosts, defaultHostName);
>>>              if (mappedHost == null) {
>>> -                return;
>>> +                mappedHost = defaultHost;
>>> +                if (mappedHost == null) {
>>> +                    return;
>>> +                }
>>>              }
>>>          }
>>>          mappingData.host = mappedHost.object;
>>> @@ -1497,6 +1536,22 @@ public final class Mapper {
>>>      }
>>>  
>>>  
>>> +    /*
>>> +     * To simplify the mapping process, wild card hosts take the form
>>> +     * ".apache.org" rather than "*.apache.org" internally. However, for ease
>>> +     * of use the external form remains "*.apache.org". Any host name passed
>>> +     * into this class needs to be passed through this method to rename and
>>> +     * wild card host names from the external to internal form.
>>> +     */
>>> +    private static String renameWildcardHost(String hostName) {
>>> +        if (hostName.startsWith("*.")) {
>>> +            return hostName.substring(1);
>>> +        } else {
>>> +            return hostName;
>>> +        }
>>> +    }
>>> +
>>> +
>>>      // ------------------------------------------------- MapElement Inner Class
>>>  
>>>  
>>>
> 
>> It's tough to tell from this diff... does the server take a performance
>> hit of wildcard-matching if no wildcards are in use?
> 
> I've removed the noise and left the key parts above.
> 
> Marginally.
> 
> The difference comes if no exact match to the requested host is found.
> 
> The old code then searched for the default host
> 
> The new code searches for a wildcard host and if not match is found
> returns the cached default host.
> 
> The new code is slightly slower in some cases and faster in others but
> the performance difference was marginal. It was the order of 10
> nanoseconds per request.
> 
>> My (long overdue) plan for doing regular-expression matching for things
>> like this was going to be to encapsulate the searching algorithm into a
>> few classes that would behave differently depending upon what needed to
>> be searched. For example, if all hostnames were explicitly-named, then
>> the existing binary search algorithm could be used, or if there was only
>> the one default host, it would always return that default host. For
>> regular expressions, the entire algorithm would change.
>>
>> I can't see from the diff where the change to the map() function is, so
>> I suspect it's quite a small change indeed, which is why I was thinking
>> that it might affect all requests even when wildcards weren't in use.
> 
> A 'wild cards in use flag' could be added that would improve things
> considerably since it would save an entire lookup. Any patch along those
> lines would need to be careful to keep track of whether or not wild
> cards were being used.

Yes, that was why I was thinking of having a separate class for each
search-type... basically, once a wildcard host had been added, we'd just
convert from the "simple" (and fast) search algorithm to the
wildcard-aware one. Switching back when all wildcard mappings had been
removed (e.g. host-manager) might be tricky but possible.

-chris


Re: svn commit: r1751173 - in /tomcat/trunk: java/org/apache/catalina/mapper/Mapper.java test/org/apache/catalina/mapper/TestMapper.java test/org/apache/catalina/mapper/TestMapperPerformance.java webapps/docs/changelog.xml webapps/docs/config/host.xml

Posted by Mark Thomas <ma...@apache.org>.
On 06/07/2016 22:55, Christopher Schultz wrote:
> Mark,
> 
> On 7/3/16 4:24 PM, markt@apache.org wrote:
>> Author: markt
>> Date: Sun Jul  3 20:24:18 2016
>> New Revision: 1751173
>>
>> URL: http://svn.apache.org/viewvc?rev=1751173&view=rev
>> Log:
>> The original request for regular expression support would be too expensive to implement.
>> This commit adds support for wild card host names.
>> It adds overhead for requests where the Host header is not an exact match since the code now has to convert the name in the header to the wild card form and then search for that.
>> However, this overhead is offset by caching the default host so it is not necessary to do a look up for the default host.
>> I've also expanded the performance tests. On my laptop the before and after results are broadly similar with some small improvements and some small increases.

<snip/>


>> @@ -720,12 +747,24 @@ public final class Mapper {
>>          MappedHost[] hosts = this.hosts;
>>          MappedHost mappedHost = exactFindIgnoreCase(hosts, host);
>>          if (mappedHost == null) {
>> -            if (defaultHostName == null) {
>> -                return;
>> +            // Note: Internally, the Mapper does not use the leading * on a
>> +            //       wildcard host. This is to allow this shortcut.
>> +            int firstDot = host.indexOf('.');
>> +            if (firstDot > -1) {
>> +                int offset = host.getOffset();
>> +                try {
>> +                    host.setOffset(firstDot + offset);
>> +                    mappedHost = exactFindIgnoreCase(hosts, host);
>> +                } finally {
>> +                    // Make absolutely sure this gets reset
>> +                    host.setOffset(offset);
>> +                }
>>              }
>> -            mappedHost = exactFind(hosts, defaultHostName);
>>              if (mappedHost == null) {
>> -                return;
>> +                mappedHost = defaultHost;
>> +                if (mappedHost == null) {
>> +                    return;
>> +                }
>>              }
>>          }
>>          mappingData.host = mappedHost.object;
>> @@ -1497,6 +1536,22 @@ public final class Mapper {
>>      }
>>  
>>  
>> +    /*
>> +     * To simplify the mapping process, wild card hosts take the form
>> +     * ".apache.org" rather than "*.apache.org" internally. However, for ease
>> +     * of use the external form remains "*.apache.org". Any host name passed
>> +     * into this class needs to be passed through this method to rename and
>> +     * wild card host names from the external to internal form.
>> +     */
>> +    private static String renameWildcardHost(String hostName) {
>> +        if (hostName.startsWith("*.")) {
>> +            return hostName.substring(1);
>> +        } else {
>> +            return hostName;
>> +        }
>> +    }
>> +
>> +
>>      // ------------------------------------------------- MapElement Inner Class
>>  
>>  
>>

> It's tough to tell from this diff... does the server take a performance
> hit of wildcard-matching if no wildcards are in use?

I've removed the noise and left the key parts above.

Marginally.

The difference comes if no exact match to the requested host is found.

The old code then searched for the default host

The new code searches for a wildcard host and if not match is found
returns the cached default host.

The new code is slightly slower in some cases and faster in others but
the performance difference was marginal. It was the order of 10
nanoseconds per request.

> My (long overdue) plan for doing regular-expression matching for things
> like this was going to be to encapsulate the searching algorithm into a
> few classes that would behave differently depending upon what needed to
> be searched. For example, if all hostnames were explicitly-named, then
> the existing binary search algorithm could be used, or if there was only
> the one default host, it would always return that default host. For
> regular expressions, the entire algorithm would change.
> 
> I can't see from the diff where the change to the map() function is, so
> I suspect it's quite a small change indeed, which is why I was thinking
> that it might affect all requests even when wildcards weren't in use.

A 'wild cards in use flag' could be added that would improve things
considerably since it would save an entire lookup. Any patch along those
lines would need to be careful to keep track of whether or not wild
cards were being used.

As always, patches welcome ;)

Mark


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


Re: svn commit: r1751173 - in /tomcat/trunk: java/org/apache/catalina/mapper/Mapper.java test/org/apache/catalina/mapper/TestMapper.java test/org/apache/catalina/mapper/TestMapperPerformance.java webapps/docs/changelog.xml webapps/docs/config/host.xml

Posted by Christopher Schultz <ch...@christopherschultz.net>.
Mark,

On 7/3/16 4:24 PM, markt@apache.org wrote:
> Author: markt
> Date: Sun Jul  3 20:24:18 2016
> New Revision: 1751173
> 
> URL: http://svn.apache.org/viewvc?rev=1751173&view=rev
> Log:
> The original request for regular expression support would be too expensive to implement.
> This commit adds support for wild card host names.
> It adds overhead for requests where the Host header is not an exact match since the code now has to convert the name in the header to the wild card form and then search for that.
> However, this overhead is offset by caching the default host so it is not necessary to do a look up for the default host.
> I've also expanded the performance tests. On my laptop the before and after results are broadly similar with some small improvements and some small increases.
> 
> Modified:
>     tomcat/trunk/java/org/apache/catalina/mapper/Mapper.java
>     tomcat/trunk/test/org/apache/catalina/mapper/TestMapper.java
>     tomcat/trunk/test/org/apache/catalina/mapper/TestMapperPerformance.java
>     tomcat/trunk/webapps/docs/changelog.xml
>     tomcat/trunk/webapps/docs/config/host.xml
> 
> Modified: tomcat/trunk/java/org/apache/catalina/mapper/Mapper.java
> URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/mapper/Mapper.java?rev=1751173&r1=1751172&r2=1751173&view=diff
> ==============================================================================
> --- tomcat/trunk/java/org/apache/catalina/mapper/Mapper.java (original)
> +++ tomcat/trunk/java/org/apache/catalina/mapper/Mapper.java Sun Jul  3 20:24:18 2016
> @@ -66,6 +66,7 @@ public final class Mapper {
>       * Default host name.
>       */
>      private String defaultHostName = null;
> +    private volatile MappedHost defaultHost = null;
>  
>  
>      /**
> @@ -83,10 +84,16 @@ public final class Mapper {
>       *
>       * @param defaultHostName Default host name
>       */
> -    public void setDefaultHostName(String defaultHostName) {
> -        this.defaultHostName = defaultHostName;
> +    public synchronized void setDefaultHostName(String defaultHostName) {
> +        this.defaultHostName = renameWildcardHost(defaultHostName);
> +        if (this.defaultHostName == null) {
> +            defaultHost = null;
> +        } else {
> +            defaultHost = exactFind(hosts, this.defaultHostName);
> +        }
>      }
>  
> +
>      /**
>       * Add a new host to the mapper.
>       *
> @@ -96,10 +103,14 @@ public final class Mapper {
>       */
>      public synchronized void addHost(String name, String[] aliases,
>                                       Host host) {
> +        name = renameWildcardHost(name);
>          MappedHost[] newHosts = new MappedHost[hosts.length + 1];
>          MappedHost newHost = new MappedHost(name, host);
>          if (insertMap(hosts, newHosts, newHost)) {
>              hosts = newHosts;
> +            if (newHost.name.equals(defaultHostName)) {
> +                defaultHost = newHost;
> +            }
>              if (log.isDebugEnabled()) {
>                  log.debug(sm.getString("mapper.addHost.success", name));
>              }
> @@ -122,6 +133,7 @@ public final class Mapper {
>          }
>          List<MappedHost> newAliases = new ArrayList<>(aliases.length);
>          for (String alias : aliases) {
> +            alias = renameWildcardHost(alias);
>              MappedHost newAlias = new MappedHost(alias, newHost);
>              if (addHostAliasImpl(newAlias)) {
>                  newAliases.add(newAlias);
> @@ -137,6 +149,7 @@ public final class Mapper {
>       * @param name Virtual host name
>       */
>      public synchronized void removeHost(String name) {
> +        name = renameWildcardHost(name);
>          // Find and remove the old host
>          MappedHost host = exactFind(hosts, name);
>          if (host == null || host.isAlias()) {
> @@ -165,6 +178,7 @@ public final class Mapper {
>              // just in case...
>              return;
>          }
> +        alias = renameWildcardHost(alias);
>          MappedHost newAlias = new MappedHost(alias, realHost);
>          if (addHostAliasImpl(newAlias)) {
>              realHost.addAlias(newAlias);
> @@ -175,6 +189,9 @@ public final class Mapper {
>          MappedHost[] newHosts = new MappedHost[hosts.length + 1];
>          if (insertMap(hosts, newHosts, newAlias)) {
>              hosts = newHosts;
> +            if (newAlias.name.equals(defaultHostName)) {
> +                defaultHost = newAlias;
> +            }
>              if (log.isDebugEnabled()) {
>                  log.debug(sm.getString("mapper.addHostAlias.success",
>                          newAlias.name, newAlias.getRealHostName()));
> @@ -203,6 +220,7 @@ public final class Mapper {
>       * @param alias The alias to remove
>       */
>      public synchronized void removeHostAlias(String alias) {
> +        alias = renameWildcardHost(alias);
>          // Find and remove the alias
>          MappedHost hostMapping = exactFind(hosts, alias);
>          if (hostMapping == null || !hostMapping.isAlias()) {
> @@ -245,6 +263,8 @@ public final class Mapper {
>              String version, Context context, String[] welcomeResources,
>              WebResourceRoot resources, Collection<WrapperMappingInfo> wrappers) {
>  
> +        hostName = renameWildcardHost(hostName);
> +
>          MappedHost mappedHost  = exactFind(hosts, hostName);
>          if (mappedHost == null) {
>              addHost(hostName, new String[0], host);
> @@ -309,6 +329,7 @@ public final class Mapper {
>      public void removeContextVersion(Context ctxt, String hostName,
>              String path, String version) {
>  
> +        hostName = renameWildcardHost(hostName);
>          contextObjectToContextVersionMap.remove(ctxt);
>  
>          MappedHost host = exactFind(hosts, hostName);
> @@ -352,7 +373,7 @@ public final class Mapper {
>       */
>      public void pauseContextVersion(Context ctxt, String hostName,
>              String contextPath, String version) {
> -
> +        hostName = renameWildcardHost(hostName);
>          ContextVersion contextVersion = findContextVersion(hostName,
>                  contextPath, version, true);
>          if (contextVersion == null || !ctxt.equals(contextVersion.object)) {
> @@ -394,6 +415,7 @@ public final class Mapper {
>      public void addWrapper(String hostName, String contextPath, String version,
>                             String path, Wrapper wrapper, boolean jspWildCard,
>                             boolean resourceOnly) {
> +        hostName = renameWildcardHost(hostName);
>          ContextVersion contextVersion = findContextVersion(hostName,
>                  contextPath, version, false);
>          if (contextVersion == null) {
> @@ -404,6 +426,7 @@ public final class Mapper {
>  
>      public void addWrappers(String hostName, String contextPath,
>              String version, Collection<WrapperMappingInfo> wrappers) {
> +        hostName = renameWildcardHost(hostName);
>          ContextVersion contextVersion = findContextVersion(hostName,
>                  contextPath, version, false);
>          if (contextVersion == null) {
> @@ -504,6 +527,7 @@ public final class Mapper {
>       */
>      public void removeWrapper(String hostName, String contextPath,
>              String version, String path) {
> +        hostName = renameWildcardHost(hostName);
>          ContextVersion contextVersion = findContextVersion(hostName,
>                  contextPath, version, true);
>          if (contextVersion == null || contextVersion.isPaused()) {
> @@ -588,6 +612,7 @@ public final class Mapper {
>       */
>      public void addWelcomeFile(String hostName, String contextPath, String version,
>              String welcomeFile) {
> +        hostName = renameWildcardHost(hostName);
>          ContextVersion contextVersion = findContextVersion(hostName, contextPath, version, false);
>          if (contextVersion == null) {
>              return;
> @@ -610,6 +635,7 @@ public final class Mapper {
>       */
>      public void removeWelcomeFile(String hostName, String contextPath,
>              String version, String welcomeFile) {
> +        hostName = renameWildcardHost(hostName);
>          ContextVersion contextVersion = findContextVersion(hostName, contextPath, version, false);
>          if (contextVersion == null || contextVersion.isPaused()) {
>              return;
> @@ -642,6 +668,7 @@ public final class Mapper {
>       * @param version     The version of the context to be cleared
>       */
>      public void clearWelcomeFiles(String hostName, String contextPath, String version) {
> +        hostName = renameWildcardHost(hostName);
>          ContextVersion contextVersion = findContextVersion(hostName, contextPath, version, false);
>          if (contextVersion == null) {
>              return;
> @@ -720,12 +747,24 @@ public final class Mapper {
>          MappedHost[] hosts = this.hosts;
>          MappedHost mappedHost = exactFindIgnoreCase(hosts, host);
>          if (mappedHost == null) {
> -            if (defaultHostName == null) {
> -                return;
> +            // Note: Internally, the Mapper does not use the leading * on a
> +            //       wildcard host. This is to allow this shortcut.
> +            int firstDot = host.indexOf('.');
> +            if (firstDot > -1) {
> +                int offset = host.getOffset();
> +                try {
> +                    host.setOffset(firstDot + offset);
> +                    mappedHost = exactFindIgnoreCase(hosts, host);
> +                } finally {
> +                    // Make absolutely sure this gets reset
> +                    host.setOffset(offset);
> +                }
>              }
> -            mappedHost = exactFind(hosts, defaultHostName);
>              if (mappedHost == null) {
> -                return;
> +                mappedHost = defaultHost;
> +                if (mappedHost == null) {
> +                    return;
> +                }
>              }
>          }
>          mappingData.host = mappedHost.object;
> @@ -1497,6 +1536,22 @@ public final class Mapper {
>      }
>  
>  
> +    /*
> +     * To simplify the mapping process, wild card hosts take the form
> +     * ".apache.org" rather than "*.apache.org" internally. However, for ease
> +     * of use the external form remains "*.apache.org". Any host name passed
> +     * into this class needs to be passed through this method to rename and
> +     * wild card host names from the external to internal form.
> +     */
> +    private static String renameWildcardHost(String hostName) {
> +        if (hostName.startsWith("*.")) {
> +            return hostName.substring(1);
> +        } else {
> +            return hostName;
> +        }
> +    }
> +
> +
>      // ------------------------------------------------- MapElement Inner Class
>  
>  
> 
> Modified: tomcat/trunk/test/org/apache/catalina/mapper/TestMapper.java
> URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/mapper/TestMapper.java?rev=1751173&r1=1751172&r2=1751173&view=diff
> ==============================================================================
> --- tomcat/trunk/test/org/apache/catalina/mapper/TestMapper.java (original)
> +++ tomcat/trunk/test/org/apache/catalina/mapper/TestMapper.java Sun Jul  3 20:24:18 2016
> @@ -88,6 +88,9 @@ public class TestMapper extends LoggingB
>          mapper.addHost("zzzuyopjvewpovewjhfewoih", new String[0], createHost("blah12"));
>          mapper.addHost("xxxxgqwiwoih", new String[0], createHost("blah13"));
>          mapper.addHost("qwigqwiwoih", new String[0], createHost("blah14"));
> +        mapper.addHost("qwerty.net", new String[0], createHost("blah15"));
> +        mapper.addHost("*.net", new String[0], createHost("blah16"));
> +        mapper.addHost("zzz.com", new String[0], createHost("blah17"));
>          mapper.addHostAlias("iowejoiejfoiew", "iowejoiejfoiew_alias");
>  
>          mapper.setDefaultHostName("ylwrehirkuewh");
> @@ -135,6 +138,14 @@ public class TestMapper extends LoggingB
>                  null,
>                  Arrays.asList(new WrapperMappingInfo[] { new WrapperMappingInfo(
>                          "/bobou/*", createWrapper("wrapper7"), false, false) }));
> +
> +        host = createHost("blah16");
> +        mapper.addContextVersion("*.net", host, "", "0", createContext("context4"),
> +                new String[0], null, null);
> +        mapper.addWrappers("*.net", "", "0", Arrays
> +                .asList(new WrapperMappingInfo[] {
> +                        new WrapperMappingInfo("/",
> +                                createWrapper("context4-defaultWrapper"), false, false) }));
>      }
>  
>      @Test
> @@ -153,14 +164,14 @@ public class TestMapper extends LoggingB
>          mapper.addHostAlias("iowejoiejfoiew", "iowejoiejfoiew_alias");
>  
>          // Check we have the right number
> -        // (added 16 including one host alias. Three duplicates do not increase the count.)
> -        assertEquals(16, mapper.hosts.length);
> +        // (added 17 including one host alias. Three duplicates do not increase the count.)
> +        assertEquals(19, mapper.hosts.length);
>  
>          // Make sure adding a duplicate *does not* overwrite
> -        final int iowPos = 3;
> +        final int iowPos = 4;
>          assertEquals("blah7", mapper.hosts[iowPos].object.getName());
>  
> -        final int qwigPos = 8;
> +        final int qwigPos = 10;
>          assertEquals("blah14", mapper.hosts[qwigPos].object.getName());
>  
>          // Check for alphabetical order of host names
> @@ -185,38 +196,38 @@ public class TestMapper extends LoggingB
>          Host hostZ = createHost("zzzz");
>          Context contextZ = createContext("contextZ");
>  
> -        assertEquals(16, mapper.hosts.length);
> +        assertEquals(19, mapper.hosts.length);
>          mapper.addContextVersion("zzzz", hostZ, "/", "", contextZ, null, null,
>                  null);
> -        assertEquals(17, mapper.hosts.length);
> +        assertEquals(20, mapper.hosts.length);
>  
>          mapper.addHost("zzzz", new String[] { "zzzz_alias1", "zzzz_alias2" },
>                  hostZ);
> -        assertEquals(19, mapper.hosts.length);
> +        assertEquals(22, mapper.hosts.length);
>  
> -        assertEquals("zzzz", mapper.hosts[16].name);
> -        assertEquals("zzzz_alias1", mapper.hosts[17].name);
> -        assertEquals("zzzz_alias2", mapper.hosts[18].name);
> -        assertEquals(2, mapper.hosts[16].getAliases().size());
> +        assertEquals("zzzz", mapper.hosts[19].name);
> +        assertEquals("zzzz_alias1", mapper.hosts[20].name);
> +        assertEquals("zzzz_alias2", mapper.hosts[21].name);
> +        assertEquals(2, mapper.hosts[19].getAliases().size());
>          assertSame(contextZ,
> -                mapper.hosts[16].contextList.contexts[0].versions[0].object);
> +                mapper.hosts[19].contextList.contexts[0].versions[0].object);
>          assertSame(contextZ,
> -                mapper.hosts[18].contextList.contexts[0].versions[0].object);
> +                mapper.hosts[21].contextList.contexts[0].versions[0].object);
>      }
>  
>      @Test
>      public void testRemoveHost() {
> -        assertEquals(16, mapper.hosts.length);
> +        assertEquals(19, mapper.hosts.length);
>          mapper.removeHostAlias("iowejoiejfoiew");
>          mapper.removeHost("iowejoiejfoiew_alias");
> -        assertEquals(16, mapper.hosts.length); // No change
> +        assertEquals(19, mapper.hosts.length); // No change
>          mapper.removeHostAlias("iowejoiejfoiew_alias");
> -        assertEquals(15, mapper.hosts.length); // Removed
> +        assertEquals(18, mapper.hosts.length); // Removed
>  
>          mapper.addHostAlias("iowejoiejfoiew", "iowejoiejfoiew_alias");
> -        assertEquals(16, mapper.hosts.length);
> +        assertEquals(19, mapper.hosts.length);
>  
> -        final int iowPos = 3;
> +        final int iowPos = 4;
>          Mapper.MappedHost hostMapping = mapper.hosts[iowPos];
>          Mapper.MappedHost aliasMapping = mapper.hosts[iowPos + 1];
>          assertEquals("iowejoiejfoiew_alias", aliasMapping.name);
> @@ -229,7 +240,7 @@ public class TestMapper extends LoggingB
>          assertSame(hostMapping, aliasMapping.getRealHost());
>  
>          mapper.removeHost("iowejoiejfoiew");
> -        assertEquals(14, mapper.hosts.length); // Both host and alias removed
> +        assertEquals(17, mapper.hosts.length); // Both host and alias removed
>          for (Mapper.MappedHost host : mapper.hosts) {
>              assertTrue(host.name, !host.name.startsWith("iowejoiejfoiew"));
>          }
> @@ -240,6 +251,8 @@ public class TestMapper extends LoggingB
>          MappingData mappingData = new MappingData();
>          MessageBytes host = MessageBytes.newInstance();
>          host.setString("iowejoiejfoiew");
> +        MessageBytes wildcard = MessageBytes.newInstance();
> +        wildcard.setString("foo.net");
>          MessageBytes alias = MessageBytes.newInstance();
>          alias.setString("iowejoiejfoiew_alias");
>          MessageBytes uri = MessageBytes.newInstance();
> @@ -271,6 +284,20 @@ public class TestMapper extends LoggingB
>          assertTrue(mappingData.redirectPath.isNull());
>  
>          mappingData.recycle();
> +        uri.recycle();
> +        uri.setString("/foo/bar/bla/bobou/foo");
> +        uri.toChars();
> +        uri.getCharChunk().setLimit(-1);
> +        mapper.map(wildcard, uri, null, mappingData);
> +        assertEquals("blah16", mappingData.host.getName());
> +        assertEquals("context4", mappingData.context.getName());
> +        assertEquals("context4-defaultWrapper", mappingData.wrapper.getName());
> +        assertEquals("", mappingData.contextPath.toString());
> +        assertEquals("/foo/bar/bla/bobou/foo", mappingData.wrapperPath.toString());
> +        assertTrue(mappingData.pathInfo.isNull());
> +        assertTrue(mappingData.redirectPath.isNull());
> +
> +        mappingData.recycle();
>          uri.setString("/foo/bar/bla/bobou/foo");
>          uri.toChars();
>          uri.getCharChunk().setLimit(-1);
> @@ -287,7 +314,7 @@ public class TestMapper extends LoggingB
>      @Test
>      public void testAddRemoveContextVersion() throws Exception {
>          final String hostName = "iowejoiejfoiew";
> -        final int iowPos = 3;
> +        final int iowPos = 4;
>          final String contextPath = "/foo/bar";
>          final int contextPos = 2;
>  
> @@ -394,7 +421,7 @@ public class TestMapper extends LoggingB
>      @Test
>      public void testReloadContextVersion() throws Exception {
>          final String hostName = "iowejoiejfoiew";
> -        final int iowPos = 3;
> +        final int iowPos = 4;
>          final String contextPath = "/foo/bar";
>          final int contextPos = 2;
>  
> 
> Modified: tomcat/trunk/test/org/apache/catalina/mapper/TestMapperPerformance.java
> URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/mapper/TestMapperPerformance.java?rev=1751173&r1=1751172&r2=1751173&view=diff
> ==============================================================================
> --- tomcat/trunk/test/org/apache/catalina/mapper/TestMapperPerformance.java (original)
> +++ tomcat/trunk/test/org/apache/catalina/mapper/TestMapperPerformance.java Sun Jul  3 20:24:18 2016
> @@ -26,24 +26,42 @@ public class TestMapperPerformance exten
>  
>      @Test
>      public void testPerformance() throws Exception {
> +        String[] requestedHostNames = new String[] {
> +                "xxxxxxxxxxx",
> +                "iowejoiejfoiew",
> +                "iowejoiejfoiex",
> +                "owefojiwefoi",
> +                "owefojiwefoix",
> +                "qwerty.net",
> +                "foo.net",
> +                "zzz.com",
> +                "abc.com"};
> +
> +        for (String requestedHostName : requestedHostNames) {
> +            testPerformance(requestedHostName);
> +        }
> +    }
> +
> +    private void testPerformance(String requestedHostName) throws Exception {
>          // Takes ~1s on markt's laptop. If this takes more than 5s something
>          // probably needs looking at. If this fails repeatedly then we may need
>          // to increase this limit.
>          final long maxTime = 5000;
> -        long time = testPerformanceImpl();
> +        long time = testPerformanceImpl(requestedHostName);
> +        log.info("Host [" + requestedHostName + "], Time [" + time + "]ms");
>          if (time >= maxTime) {
>              // Rerun to reject occasional failures, e.g. because of gc
>              log.warn("testPerformance() test completed in " + time + " ms");
> -            time = testPerformanceImpl();
> +            time = testPerformanceImpl(requestedHostName);
>              log.warn("testPerformance() test rerun completed in " + time + " ms");
>          }
>          assertTrue(String.valueOf(time), time < maxTime);
>      }
>  
> -    private long testPerformanceImpl() throws Exception {
> +    private long testPerformanceImpl(String requestedHostName) throws Exception {
>          MappingData mappingData = new MappingData();
>          MessageBytes host = MessageBytes.newInstance();
> -        host.setString("iowejoiejfoiew");
> +        host.setString(requestedHostName);
>          MessageBytes uri = MessageBytes.newInstance();
>          uri.setString("/foo/bar/blah/bobou/foo");
>          uri.toChars();
> 
> Modified: tomcat/trunk/webapps/docs/changelog.xml
> URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1751173&r1=1751172&r2=1751173&view=diff
> ==============================================================================
> --- tomcat/trunk/webapps/docs/changelog.xml (original)
> +++ tomcat/trunk/webapps/docs/changelog.xml Sun Jul  3 20:24:18 2016
> @@ -48,6 +48,12 @@
>    <subsection name="Catalina">
>      <changelog>
>        <fix>
> +        <bug>18500</bug>: Add limited support for wildcard host names and host
> +        aliases. Names of the form <code>*.domainname</code> are now permitted.
> +        Note that an exact host name match takes precedence over a wild card
> +        host name match. (markt)
> +      </fix>
> +      <fix>
>          <bug>57705</bug>: Add debug logging for requests denied by the remote
>          host and remote address valves and filters. Based on a patch by Graham
>          Leggett. (markt)
> 
> Modified: tomcat/trunk/webapps/docs/config/host.xml
> URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/host.xml?rev=1751173&r1=1751172&r2=1751173&view=diff
> ==============================================================================
> --- tomcat/trunk/webapps/docs/config/host.xml (original)
> +++ tomcat/trunk/webapps/docs/config/host.xml Sun Jul  3 20:24:18 2016
> @@ -192,7 +192,10 @@
>          have a name that matches the <code>defaultHost</code> setting for that
>          Engine.  See <a href="#Host_Name_Aliases">Host Name Aliases</a> for
>          information on how to assign more than one network name to the same
> -        virtual host.</p>
> +        virtual host. If the name takes the form of <code>*.domainname</code>
> +        (e.g. <code>*.apache.org</code>) then it will be treated as a match for
> +        any host in that domain unless a host that has an exactly matching name
> +        is found.</p>
>        </attribute>
>  
>        <attribute name="startStopThreads" required="false">
> @@ -476,6 +479,10 @@
>      involved must be registered in your DNS server to resolve to the
>      same computer that is running this instance of Catalina.</p>
>  
> +    <p>Aliases may also use the wildcard form (<code>*.domainname</code>)
> +       permitted for the <strong>name</strong> attribute of a
> +       <strong>Host</strong>.</p>
> +
>    </subsection>

It's tough to tell from this diff... does the server take a performance
hit of wildcard-matching if no wildcards are in use?

My (long overdue) plan for doing regular-expression matching for things
like this was going to be to encapsulate the searching algorithm into a
few classes that would behave differently depending upon what needed to
be searched. For example, if all hostnames were explicitly-named, then
the existing binary search algorithm could be used, or if there was only
the one default host, it would always return that default host. For
regular expressions, the entire algorithm would change.

I can't see from the diff where the change to the map() function is, so
I suspect it's quite a small change indeed, which is why I was thinking
that it might affect all requests even when wildcards weren't in use.

-chris