• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 //  ========================================================================
3 //  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4 //  ------------------------------------------------------------------------
5 //  All rights reserved. This program and the accompanying materials
6 //  are made available under the terms of the Eclipse Public License v1.0
7 //  and Apache License v2.0 which accompanies this distribution.
8 //
9 //      The Eclipse Public License is available at
10 //      http://www.eclipse.org/legal/epl-v10.html
11 //
12 //      The Apache License v2.0 is available at
13 //      http://www.opensource.org/licenses/apache2.0.php
14 //
15 //  You may elect to redistribute this code under either of these licenses.
16 //  ========================================================================
17 //
18 
19 package org.eclipse.jetty.server.handler;
20 
21 import java.io.IOException;
22 import java.util.HashMap;
23 import java.util.Map;
24 
25 import javax.servlet.ServletException;
26 import javax.servlet.http.HttpServletRequest;
27 import javax.servlet.http.HttpServletResponse;
28 
29 import org.eclipse.jetty.http.PathMap;
30 import org.eclipse.jetty.server.AsyncContinuation;
31 import org.eclipse.jetty.server.Handler;
32 import org.eclipse.jetty.server.HandlerContainer;
33 import org.eclipse.jetty.server.Request;
34 import org.eclipse.jetty.util.LazyList;
35 import org.eclipse.jetty.util.log.Log;
36 import org.eclipse.jetty.util.log.Logger;
37 
38 /* ------------------------------------------------------------ */
39 /** ContextHandlerCollection.
40  *
41  * This {@link org.eclipse.jetty.server.handler.HandlerCollection} is creates a
42  * {@link org.eclipse.jetty.http.PathMap} to it's contained handlers based
43  * on the context path and virtual hosts of any contained {@link org.eclipse.jetty.server.handler.ContextHandler}s.
44  * The contexts do not need to be directly contained, only children of the contained handlers.
45  * Multiple contexts may have the same context path and they are called in order until one
46  * handles the request.
47  *
48  * @org.apache.xbean.XBean element="contexts"
49  */
50 public class ContextHandlerCollection extends HandlerCollection
51 {
52     private static final Logger LOG = Log.getLogger(ContextHandlerCollection.class);
53 
54     private volatile PathMap _contextMap;
55     private Class<? extends ContextHandler> _contextClass = ContextHandler.class;
56 
57     /* ------------------------------------------------------------ */
ContextHandlerCollection()58     public ContextHandlerCollection()
59     {
60         super(true);
61     }
62 
63 
64     /* ------------------------------------------------------------ */
65     /**
66      * Remap the context paths.
67      */
mapContexts()68     public void mapContexts()
69     {
70         PathMap contextMap = new PathMap();
71         Handler[] branches = getHandlers();
72 
73 
74         for (int b=0;branches!=null && b<branches.length;b++)
75         {
76             Handler[] handlers=null;
77 
78             if (branches[b] instanceof ContextHandler)
79             {
80                 handlers = new Handler[]{ branches[b] };
81             }
82             else if (branches[b] instanceof HandlerContainer)
83             {
84                 handlers = ((HandlerContainer)branches[b]).getChildHandlersByClass(ContextHandler.class);
85             }
86             else
87                 continue;
88 
89             for (int i=0;i<handlers.length;i++)
90             {
91                 ContextHandler handler=(ContextHandler)handlers[i];
92 
93                 String contextPath=handler.getContextPath();
94 
95                 if (contextPath==null || contextPath.indexOf(',')>=0 || contextPath.startsWith("*"))
96                     throw new IllegalArgumentException ("Illegal context spec:"+contextPath);
97 
98                 if(!contextPath.startsWith("/"))
99                     contextPath='/'+contextPath;
100 
101                 if (contextPath.length()>1)
102                 {
103                     if (contextPath.endsWith("/"))
104                         contextPath+="*";
105                     else if (!contextPath.endsWith("/*"))
106                         contextPath+="/*";
107                 }
108 
109                 Object contexts=contextMap.get(contextPath);
110                 String[] vhosts=handler.getVirtualHosts();
111 
112 
113                 if (vhosts!=null && vhosts.length>0)
114                 {
115                     Map hosts;
116 
117                     if (contexts instanceof Map)
118                         hosts=(Map)contexts;
119                     else
120                     {
121                         hosts=new HashMap();
122                         hosts.put("*",contexts);
123                         contextMap.put(contextPath, hosts);
124                     }
125 
126                     for (int j=0;j<vhosts.length;j++)
127                     {
128                         String vhost=vhosts[j];
129                         contexts=hosts.get(vhost);
130                         contexts=LazyList.add(contexts,branches[b]);
131                         hosts.put(vhost,contexts);
132                     }
133                 }
134                 else if (contexts instanceof Map)
135                 {
136                     Map hosts=(Map)contexts;
137                     contexts=hosts.get("*");
138                     contexts= LazyList.add(contexts, branches[b]);
139                     hosts.put("*",contexts);
140                 }
141                 else
142                 {
143                     contexts= LazyList.add(contexts, branches[b]);
144                     contextMap.put(contextPath, contexts);
145                 }
146             }
147         }
148         _contextMap=contextMap;
149 
150     }
151 
152 
153 
154     /* ------------------------------------------------------------ */
155     /*
156      * @see org.eclipse.jetty.server.server.handler.HandlerCollection#setHandlers(org.eclipse.jetty.server.server.Handler[])
157      */
158     @Override
setHandlers(Handler[] handlers)159     public void setHandlers(Handler[] handlers)
160     {
161         _contextMap=null;
162         super.setHandlers(handlers);
163         if (isStarted())
164             mapContexts();
165     }
166 
167     /* ------------------------------------------------------------ */
168     @Override
doStart()169     protected void doStart() throws Exception
170     {
171         mapContexts();
172         super.doStart();
173     }
174 
175 
176     /* ------------------------------------------------------------ */
177     /*
178      * @see org.eclipse.jetty.server.server.Handler#handle(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
179      */
180     @Override
handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)181     public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
182     {
183         Handler[] handlers = getHandlers();
184         if (handlers==null || handlers.length==0)
185 	    return;
186 
187 	AsyncContinuation async = baseRequest.getAsyncContinuation();
188 	if (async.isAsync())
189 	{
190 	    ContextHandler context=async.getContextHandler();
191 	    if (context!=null)
192 	    {
193 	        context.handle(target,baseRequest,request, response);
194 	        return;
195 	    }
196 	}
197 
198 	// data structure which maps a request to a context; first-best match wins
199 	// { context path =>
200 	//     { virtual host => context }
201 	// }
202 	PathMap map = _contextMap;
203 	if (map!=null && target!=null && target.startsWith("/"))
204 	{
205 	    // first, get all contexts matched by context path
206 	    Object contexts = map.getLazyMatches(target);
207 
208             for (int i=0; i<LazyList.size(contexts); i++)
209             {
210                 // then, match against the virtualhost of each context
211                 Map.Entry entry = (Map.Entry)LazyList.get(contexts, i);
212                 Object list = entry.getValue();
213 
214                 if (list instanceof Map)
215                 {
216                     Map hosts = (Map)list;
217                     String host = normalizeHostname(request.getServerName());
218 
219                     // explicitly-defined virtual hosts, most specific
220                     list=hosts.get(host);
221                     for (int j=0; j<LazyList.size(list); j++)
222                     {
223                         Handler handler = (Handler)LazyList.get(list,j);
224                         handler.handle(target,baseRequest, request, response);
225                         if (baseRequest.isHandled())
226                             return;
227                     }
228 
229                     // wildcard for one level of names
230                     list=hosts.get("*."+host.substring(host.indexOf(".")+1));
231                     for (int j=0; j<LazyList.size(list); j++)
232                     {
233                         Handler handler = (Handler)LazyList.get(list,j);
234                         handler.handle(target,baseRequest, request, response);
235                         if (baseRequest.isHandled())
236                             return;
237                     }
238 
239                     // no virtualhosts defined for the context, least specific
240                     // will handle any request that does not match to a specific virtual host above
241                     list=hosts.get("*");
242                     for (int j=0; j<LazyList.size(list); j++)
243                     {
244                         Handler handler = (Handler)LazyList.get(list,j);
245                         handler.handle(target,baseRequest, request, response);
246                         if (baseRequest.isHandled())
247                             return;
248                     }
249                 }
250                 else
251                 {
252                     for (int j=0; j<LazyList.size(list); j++)
253                     {
254                         Handler handler = (Handler)LazyList.get(list,j);
255                         handler.handle(target,baseRequest, request, response);
256                         if (baseRequest.isHandled())
257                             return;
258                     }
259                 }
260 	    }
261 	}
262 	else
263 	{
264             // This may not work in all circumstances... but then I think it should never be called
265 	    for (int i=0;i<handlers.length;i++)
266 	    {
267 		handlers[i].handle(target,baseRequest, request, response);
268 		if ( baseRequest.isHandled())
269 		    return;
270 	    }
271 	}
272     }
273 
274 
275     /* ------------------------------------------------------------ */
276     /** Add a context handler.
277      * @param contextPath  The context path to add
278      * @return the ContextHandler just added
279      */
addContext(String contextPath,String resourceBase)280     public ContextHandler addContext(String contextPath,String resourceBase)
281     {
282         try
283         {
284             ContextHandler context = _contextClass.newInstance();
285             context.setContextPath(contextPath);
286             context.setResourceBase(resourceBase);
287             addHandler(context);
288             return context;
289         }
290         catch (Exception e)
291         {
292             LOG.debug(e);
293             throw new Error(e);
294         }
295     }
296 
297 
298 
299     /* ------------------------------------------------------------ */
300     /**
301      * @return The class to use to add new Contexts
302      */
getContextClass()303     public Class getContextClass()
304     {
305         return _contextClass;
306     }
307 
308 
309     /* ------------------------------------------------------------ */
310     /**
311      * @param contextClass The class to use to add new Contexts
312      */
setContextClass(Class contextClass)313     public void setContextClass(Class contextClass)
314     {
315         if (contextClass ==null || !(ContextHandler.class.isAssignableFrom(contextClass)))
316             throw new IllegalArgumentException();
317         _contextClass = contextClass;
318     }
319 
320     /* ------------------------------------------------------------ */
normalizeHostname( String host )321     private String normalizeHostname( String host )
322     {
323         if ( host == null )
324             return null;
325 
326         if ( host.endsWith( "." ) )
327             return host.substring( 0, host.length() -1);
328 
329         return host;
330     }
331 
332 }
333