Index: VERSION.txt
===================================================================
--- VERSION.txt	(revision 3699)
+++ VERSION.txt	(working copy)
@@ -4,6 +4,7 @@
  + JETTY-716 NPE for empty cometd message
  + JETTY-718 during ssl unwrap, return true if some bytes were read, even if underflow
  + JETTY-720 fix HttpExchange.waitForStatus
+ + JETTY-721 Support wildcard in VirtualHosts configuration
  + JETTY-722 jndi related threadlocal not cleared after deploying webapp
  + JETTY-723 jetty.sh does not check if TMP already is set
  + JETTY-725 port JETTY-708 (jndi scoping) to jetty-6
Index: modules/jetty/src/test/java/org/mortbay/jetty/handler/ContextHandlerTest.java
===================================================================
--- modules/jetty/src/test/java/org/mortbay/jetty/handler/ContextHandlerTest.java	(revision 3699)
+++ modules/jetty/src/test/java/org/mortbay/jetty/handler/ContextHandlerTest.java	(working copy)
@@ -139,6 +139,61 @@
 
     }
 
+    public void testVirtualHostWildcard() throws Exception
+    {
+        Server server = new Server();
+        LocalConnector connector = new LocalConnector();
+        server.setConnectors(new Connector[] { connector });
+
+        ContextHandler context = new ContextHandler("/");
+        
+        IsHandledHandler handler = new IsHandledHandler();
+        context.setHandler(handler);
+
+        server.addHandler(context);
+
+        try
+        {
+            server.start();
+            checkWildcardHost(true,server,new String[] {"example.com", "*.example.com"}, new String[] {"example.com", ".example.com", "vhost.example.com"});
+            checkWildcardHost(false,server,new String[] {"example.com", "*.example.com"}, new String[] {"badexample.com", ".badexample.com", "vhost.badexample.com"});
+            
+            checkWildcardHost(false,server,new String[] {"*."}, new String[] {"anything.anything"});
+            
+            checkWildcardHost(true,server,new String[] {"*.example.com"}, new String[] {"vhost.example.com", ".example.com"});
+            checkWildcardHost(false,server,new String[] {"*.example.com"}, new String[] {"vhost.www.example.com", "example.com", "www.vhost.example.com"});
+
+            checkWildcardHost(true,server,new String[] {"*.sub.example.com"}, new String[] {"vhost.sub.example.com", ".sub.example.com"});
+            checkWildcardHost(false,server,new String[] {"*.sub.example.com"}, new String[] {".example.com", "sub.example.com", "vhost.example.com"});
+            
+            checkWildcardHost(false,server,new String[] {"example.*.com","example.com.*"}, new String[] {"example.vhost.com", "example.com.vhost", "example.com"});            
+        }
+        finally
+        {
+            server.stop();
+        }
+    }
+
+    private void checkWildcardHost(boolean succeed, Server server, String[] contextHosts, String[] requestHosts) throws Exception
+    {
+        LocalConnector connector = (LocalConnector)server.getConnectors()[0];
+        ContextHandler context = (ContextHandler)server.getHandler();
+        context.setVirtualHosts(contextHosts);
+        
+        IsHandledHandler handler = (IsHandledHandler)context.getHandler();
+        for(int i=0; i < requestHosts.length; ++i)
+        {
+            String host = requestHosts[i];
+            connector.getResponses("GET / HTTP/1.1\n" + "Host: "+host+"\n\n");
+            if(succeed) 
+                assertTrue("'"+host+"' should have been handled.",handler.isHandled());
+            else
+                assertFalse("'"+host + "' should not have been handled.", handler.isHandled());
+            handler.reset();
+        }
+
+    }
+
     public static final class IsHandledHandler extends AbstractHandler
     {
         private boolean handled;
Index: modules/jetty/src/main/java/org/mortbay/jetty/handler/ContextHandler.java
===================================================================
--- modules/jetty/src/main/java/org/mortbay/jetty/handler/ContextHandler.java	(revision 3699)
+++ modules/jetty/src/main/java/org/mortbay/jetty/handler/ContextHandler.java	(working copy)
@@ -221,7 +221,8 @@
      * matching virtual host name.
      * @param vhosts Array of virtual hosts that this context responds to. A
      * null host name or null/empty array means any hostname is acceptable.
-     * Host names may String representation of IP addresses.
+     * Host names may be String representation of IP addresses. Host names may
+     * start with '*.' to wildcard one level of names.
      */
     public void setVirtualHosts( String[] vhosts )
     {
@@ -247,6 +248,7 @@
      * @return Array of virtual hosts that this context responds to. A
      * null host name or empty array means any hostname is acceptable.
      * Host names may be String representation of IP addresses.
+     * Host names may start with '*.' to wildcard one level of names.
      */
     public String[] getVirtualHosts()
     {
@@ -656,7 +658,15 @@
                 
                 // TODO non-linear lookup
                 for (int i=0;!match && i<_vhosts.length;i++)
-                    match=_vhosts[i]!=null && _vhosts[i].equalsIgnoreCase(vhost);
+                {
+                    String contextVhost = _vhosts[i];
+                    if(contextVhost==null) continue;
+                    if(contextVhost.startsWith("*.")) {
+                        // wildcard only at the beginning, and only for one additional subdomain level
+                        match=contextVhost.regionMatches(true,2,vhost,vhost.indexOf(".")+1,contextVhost.length()-2);
+                    } else
+                        match=contextVhost.equalsIgnoreCase(vhost);
+                }
                 if (!match)
                     return;
             }
Index: contrib/jetty-rewrite-handler/src/test/java/org/mortbay/jetty/handler/rewrite/VirtualHostRuleContainerTest.java
===================================================================
--- contrib/jetty-rewrite-handler/src/test/java/org/mortbay/jetty/handler/rewrite/VirtualHostRuleContainerTest.java	(revision 1230)
+++ contrib/jetty-rewrite-handler/src/test/java/org/mortbay/jetty/handler/rewrite/VirtualHostRuleContainerTest.java	(working copy)
@@ -126,7 +126,42 @@
         assertEquals("{_fooContainerRule: vhosts[cheese.com, foo.com], Host: foo.com}: apply _fooRule", "/cheese/fooRule", _request.getRequestURI());
     }
     
+    public void testWildcardVirtualHosts() throws Exception
+    {
+        checkWildcardHost(true,null,new String[] {"foo.com", ".foo.com", "vhost.foo.com"});
+        checkWildcardHost(true,new String[] {null},new String[] {"foo.com", ".foo.com", "vhost.foo.com"});
+
+        checkWildcardHost(true,new String[] {"foo.com", "*.foo.com"}, new String[] {"foo.com", ".foo.com", "vhost.foo.com"});
+        checkWildcardHost(false,new String[] {"foo.com", "*.foo.com"}, new String[] {"badfoo.com", ".badfoo.com", "vhost.badfoo.com"});
+        
+        checkWildcardHost(false,new String[] {"*."}, new String[] {"anything.anything"});
+        
+        checkWildcardHost(true,new String[] {"*.foo.com"}, new String[] {"vhost.foo.com", ".foo.com"});
+        checkWildcardHost(false,new String[] {"*.foo.com"}, new String[] {"vhost.www.foo.com", "foo.com", "www.vhost.foo.com"});
+
+        checkWildcardHost(true,new String[] {"*.sub.foo.com"}, new String[] {"vhost.sub.foo.com", ".sub.foo.com"});
+        checkWildcardHost(false,new String[] {"*.sub.foo.com"}, new String[] {".foo.com", "sub.foo.com", "vhost.foo.com"});
+        
+        checkWildcardHost(false,new String[] {"foo.*.com","foo.com.*"}, new String[] {"foo.vhost.com", "foo.com.vhost", "foo.com"});                    
+    }
     
+    private void checkWildcardHost(boolean succeed, String[] ruleHosts, String[] requestHosts) throws Exception
+    {
+        _fooContainerRule.setVirtualHosts(ruleHosts);
+        _handler.setRules(new Rule[] { _fooContainerRule });
+
+        for(String host: requestHosts)
+        {
+            _request.setServerName(host);
+            _request.setRequestURI("/cheese/bar");
+            handleRequest();
+            if(succeed)
+                assertEquals("{_fooContainerRule, Host: "+host+"}: should apply _fooRule", "/cheese/fooRule", _request.getRequestURI());
+            else
+                assertEquals("{_fooContainerRule, Host: "+host+"}: should not apply _fooRule", "/cheese/bar", _request.getRequestURI());
+        }
+    }
+
     private void handleRequest() throws Exception
     {
         _server.handle("/cheese/bar", _request, _response, 0);
Index: contrib/jetty-rewrite-handler/src/main/java/org/mortbay/jetty/handler/rewrite/VirtualHostRuleContainer.java
===================================================================
--- contrib/jetty-rewrite-handler/src/main/java/org/mortbay/jetty/handler/rewrite/VirtualHostRuleContainer.java	(revision 1230)
+++ contrib/jetty-rewrite-handler/src/main/java/org/mortbay/jetty/handler/rewrite/VirtualHostRuleContainer.java	(working copy)
@@ -71,10 +71,9 @@
             String requestHost = normalizeHostname( request.getServerName() );
             for( String ruleHost : _virtualHosts )
             {
-                if(ruleHost == null || requestHost.equalsIgnoreCase(ruleHost))
-                {
+                if(ruleHost == null || ruleHost.equalsIgnoreCase(requestHost)
+                        || (ruleHost.startsWith("*.") && ruleHost.regionMatches(true,2,requestHost,requestHost.indexOf(".")+1,ruleHost.length()-2)))
                     return apply(target, request, response);
-                }
             }
         }
         else

