Index: VERSION.txt
===================================================================
--- VERSION.txt	(revision 3861)
+++ VERSION.txt	(working copy)
@@ -46,6 +46,7 @@
  + JETTY-747 Handle HttpClient exceptions better
  + JETTY-757 Unhide JAAS classes 
  + JETTY-758 Update JSP to glassfish tag SJSAS-9_1_1-B51-18_Sept_2008
+ + JETTY-760 Handle wildcard VirtualHost and normalize hostname in ContextHandlerCollection
 
 jetty-6.1.12.rc3 10 October 2008
  + JETTY-241 Support for web application overlays in rapid application development (jetty:run)
Index: modules/server/jetty/src/main/java/org/mortbay/jetty/handler/ContextHandlerCollection.java
===================================================================
--- modules/server/jetty/src/main/java/org/mortbay/jetty/handler/ContextHandlerCollection.java	(revision 3861)
+++ modules/server/jetty/src/main/java/org/mortbay/jetty/handler/ContextHandlerCollection.java	(working copy)
@@ -168,20 +168,31 @@
 	    return;
 
 	Request base_request = HttpConnection.getCurrentConnection().getRequest();
+	
+	// data structure which maps a request to a context
+	// each match is called in turn until the request is handled
+	// { context path => 
+	//     { virtual host => context } 
+	// }
 	PathMap map = _contextMap;
 	if (map!=null && target!=null && target.startsWith("/"))
 	{
+	    // first, get all contexts matched by context path
 	    Object contexts = map.getLazyMatches(target);
 
             for (int i=0; i<LazyList.size(contexts); i++)
             {
+                // then, match against the virtualhost of each context
                 Map.Entry entry = (Map.Entry)LazyList.get(contexts, i);
                 Object list = entry.getValue();
 
                 if (list instanceof Map)
                 {
                     Map hosts = (Map)list;
-                    list=hosts.get(request.getServerName());
+                    String host = normalizeHostname(request.getServerName());
+           
+                    // explicitly-defined virtual hosts, most specific
+                    list=hosts.get(host);
                     for (int j=0; j<LazyList.size(list); j++)
                     {
                         Handler handler = (Handler)LazyList.get(list,j);
@@ -189,6 +200,19 @@
                         if (base_request.isHandled())
                             return;
                     }
+                    
+                    // wildcard for one level of names 
+                    list=hosts.get("*."+host.substring(host.indexOf(".")+1));
+                    for (int j=0; j<LazyList.size(list); j++)
+                    {
+                        Handler handler = (Handler)LazyList.get(list,j);
+                        handler.handle(target,request, response, dispatch);
+                        if (base_request.isHandled())
+                            return;
+                    }
+                    
+                    // no virtualhosts defined for the context, least specific
+                    // will handle any request that does not match to a specific virtual host above
                     list=hosts.get("*");
                     for (int j=0; j<LazyList.size(list); j++)
                     {
@@ -270,5 +294,16 @@
         _contextClass = contextClass;
     }
     
+    /* ------------------------------------------------------------ */
+    private String normalizeHostname( String host )
+    {
+        if ( host == null )
+            return null;
+        
+        if ( host.endsWith( "." ) )
+            return host.substring( 0, host.length() -1);
+      
+        return host;
+    }
     
 }
Index: modules/server/jetty/src/main/java/org/mortbay/jetty/handler/ContextHandlerCollectionTest.java
===================================================================
--- modules/server/jetty/src/main/java/org/mortbay/jetty/handler/ContextHandlerCollectionTest.java	(revision 0)
+++ modules/server/jetty/src/main/java/org/mortbay/jetty/handler/ContextHandlerCollectionTest.java	(revision 0)
@@ -0,0 +1,165 @@
+package org.mortbay.jetty.handler;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.mortbay.jetty.Connector;
+import org.mortbay.jetty.Handler;
+import org.mortbay.jetty.HttpConnection;
+import org.mortbay.jetty.LocalConnector;
+import org.mortbay.jetty.Request;
+import org.mortbay.jetty.Server;
+import org.mortbay.jetty.handler.ContextHandlerTest.IsHandledHandler;
+import org.mortbay.jetty.nio.SelectChannelConnector;
+import junit.framework.TestCase;
+
+public class ContextHandlerCollectionTest extends TestCase
+{
+    public void testVirtualHostNormalization() throws Exception
+    {
+        Server server = new Server();
+        LocalConnector connector = new LocalConnector();
+        server.setConnectors(new Connector[]
+        { connector });
+
+        ContextHandler contextA = new ContextHandler("/");
+        contextA.setVirtualHosts(new String[]
+        { "www.example.com" });
+        IsHandledHandler handlerA = new IsHandledHandler();
+        contextA.setHandler(handlerA);
+
+        ContextHandler contextB = new ContextHandler("/");
+        IsHandledHandler handlerB = new IsHandledHandler();
+        contextB.setHandler(handlerB);
+        contextB.setVirtualHosts(new String[]
+        { "www.example2.com." });
+
+        ContextHandler contextC = new ContextHandler("/");
+        IsHandledHandler handlerC = new IsHandledHandler();
+        contextC.setHandler(handlerC);
+
+        ContextHandlerCollection c = new ContextHandlerCollection();
+
+        c.addHandler(contextA);
+        c.addHandler(contextB);
+        c.addHandler(contextC);
+
+        server.setHandler(c);
+
+        try
+        {
+            server.start();
+            connector.getResponses("GET / HTTP/1.1\n" + "Host: www.example.com.\n\n");
+
+            assertTrue(handlerA.isHandled());
+            assertFalse(handlerB.isHandled());
+            assertFalse(handlerC.isHandled());
+
+            handlerA.reset();
+            handlerB.reset();
+            handlerC.reset();
+
+            connector.getResponses("GET / HTTP/1.1\n" + "Host: www.example2.com\n\n");
+
+            assertFalse(handlerA.isHandled());
+            assertTrue(handlerB.isHandled());
+            assertFalse(handlerC.isHandled());
+
+        }
+        finally
+        {
+            server.stop();
+        }
+
+    }
+    
+    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);
+
+        ContextHandlerCollection c = new ContextHandlerCollection();
+        c.addHandler(context);
+        
+        server.setHandler(c);
+
+        try
+        {
+            server.start();
+            checkWildcardHost(true,server,null,new String[] {"example.com", ".example.com", "vhost.example.com"});
+            checkWildcardHost(false,server,new String[] {null},new String[] {"example.com", ".example.com", "vhost.example.com"});
+            
+            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];
+        ContextHandlerCollection handlerCollection = (ContextHandlerCollection)server.getHandler();
+        ContextHandler context = (ContextHandler)handlerCollection.getHandlers()[0];
+        IsHandledHandler handler = (IsHandledHandler)context.getHandler();
+
+        context.setVirtualHosts(contextHosts);
+        // trigger this manually; it's supposed to be called when adding the handler
+        handlerCollection.mapContexts();
+        
+        for(String host : requestHosts)
+        {
+            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;
+
+        public boolean isHandled()
+        {
+            return handled;
+        }
+
+        public void handle(String s, HttpServletRequest request, HttpServletResponse response, int i) throws IOException, ServletException
+        {
+            Request base_request = (request instanceof Request)?(Request)request:HttpConnection.getCurrentConnection().getRequest();
+            base_request.setHandled(true);
+            this.handled = true;
+        }
+
+        public void reset()
+        {
+            handled = false;
+        }
+    }
+
+}

