]> WPIA git - gigi.git/blob - lib/jetty/org/eclipse/jetty/server/handler/ContextHandlerCollection.java
Merge "Update notes about password security"
[gigi.git] / lib / jetty / org / eclipse / jetty / server / handler / ContextHandlerCollection.java
1 //
2 //  ========================================================================
3 //  Copyright (c) 1995-2016 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.lang.reflect.Array;
23 import java.util.Arrays;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29 import java.util.concurrent.ConcurrentHashMap;
30 import java.util.concurrent.ConcurrentMap;
31
32 import javax.servlet.ServletException;
33 import javax.servlet.http.HttpServletRequest;
34 import javax.servlet.http.HttpServletResponse;
35
36 import org.eclipse.jetty.server.Handler;
37 import org.eclipse.jetty.server.HandlerContainer;
38 import org.eclipse.jetty.server.HttpChannelState;
39 import org.eclipse.jetty.server.Request;
40 import org.eclipse.jetty.util.ArrayTernaryTrie;
41 import org.eclipse.jetty.util.ArrayUtil;
42 import org.eclipse.jetty.util.Trie;
43 import org.eclipse.jetty.util.annotation.ManagedObject;
44 import org.eclipse.jetty.util.annotation.ManagedOperation;
45 import org.eclipse.jetty.util.log.Log;
46 import org.eclipse.jetty.util.log.Logger;
47
48 /* ------------------------------------------------------------ */
49 /** ContextHandlerCollection.
50  *
51  * This {@link org.eclipse.jetty.server.handler.HandlerCollection} is creates a
52  * {@link org.eclipse.jetty.http.PathMap} to it's contained handlers based
53  * on the context path and virtual hosts of any contained {@link org.eclipse.jetty.server.handler.ContextHandler}s.
54  * The contexts do not need to be directly contained, only children of the contained handlers.
55  * Multiple contexts may have the same context path and they are called in order until one
56  * handles the request.
57  *
58  */
59 @ManagedObject("Context Handler Collection")
60 public class ContextHandlerCollection extends HandlerCollection
61 {
62     private static final Logger LOG = Log.getLogger(ContextHandlerCollection.class);
63
64     private final ConcurrentMap<ContextHandler,Handler> _contextBranches = new ConcurrentHashMap<>();
65     private volatile Trie<Map.Entry<String,Branch[]>> _pathBranches;
66     private Class<? extends ContextHandler> _contextClass = ContextHandler.class;
67
68     /* ------------------------------------------------------------ */
69     public ContextHandlerCollection()
70     {
71         super(true);
72     }
73
74
75     /* ------------------------------------------------------------ */
76     /**
77      * Remap the context paths.
78      */
79     @ManagedOperation("update the mapping of context path to context")
80     public void mapContexts()
81     {
82         _contextBranches.clear();
83         
84         if (getHandlers()==null)
85         {
86             _pathBranches=new ArrayTernaryTrie<>(false,16);
87             return;
88         }
89         
90         // Create map of contextPath to handler Branch
91         Map<String,Branch[]> map = new HashMap<>();
92         for (Handler handler:getHandlers())
93         {
94             Branch branch=new Branch(handler);
95             for (String contextPath : branch.getContextPaths())
96             {
97                 Branch[] branches=map.get(contextPath);
98                 map.put(contextPath, ArrayUtil.addToArray(branches, branch, Branch.class));
99             }
100             
101             for (ContextHandler context : branch.getContextHandlers())
102                 _contextBranches.putIfAbsent(context, branch.getHandler());
103         }
104         
105         // Sort the branches so those with virtual hosts are considered before those without
106         for (Map.Entry<String,Branch[]> entry: map.entrySet())
107         {
108             Branch[] branches=entry.getValue();
109             Branch[] sorted=new Branch[branches.length];
110             int i=0;
111             for (Branch branch:branches)
112                 if (branch.hasVirtualHost())
113                     sorted[i++]=branch;
114             for (Branch branch:branches)
115                 if (!branch.hasVirtualHost())
116                     sorted[i++]=branch;
117             entry.setValue(sorted);
118         }
119         
120         // Loop until we have a big enough trie to hold all the context paths
121         int capacity=512;
122         Trie<Map.Entry<String,Branch[]>> trie;
123         loop: while(true)
124         {
125             trie=new ArrayTernaryTrie<>(false,capacity);
126             for (Map.Entry<String,Branch[]> entry: map.entrySet())
127             {
128                 if (!trie.put(entry.getKey().substring(1),entry))
129                 {
130                     capacity+=512;
131                     continue loop;
132                 }
133             }
134             break loop;
135         }
136             
137         
138         if (LOG.isDebugEnabled())
139         {
140             for (String ctx : trie.keySet())
141                 LOG.debug("{}->{}",ctx,Arrays.asList(trie.get(ctx).getValue()));
142         }
143         _pathBranches=trie;
144     }
145
146     /* ------------------------------------------------------------ */
147     /*
148      * @see org.eclipse.jetty.server.server.handler.HandlerCollection#setHandlers(org.eclipse.jetty.server.server.Handler[])
149      */
150     @Override
151     public void setHandlers(Handler[] handlers)
152     {
153         super.setHandlers(handlers);
154         if (isStarted())
155             mapContexts();
156     }
157
158     /* ------------------------------------------------------------ */
159     @Override
160     protected void doStart() throws Exception
161     {
162         mapContexts();
163         super.doStart();
164     }
165
166
167     /* ------------------------------------------------------------ */
168     /*
169      * @see org.eclipse.jetty.server.server.Handler#handle(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
170      */
171     @Override
172     public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
173     {
174         Handler[] handlers = getHandlers();
175         if (handlers==null || handlers.length==0)
176             return;
177
178         HttpChannelState async = baseRequest.getHttpChannelState();
179         if (async.isAsync())
180         {
181             ContextHandler context=async.getContextHandler();
182             if (context!=null)
183             {
184                 Handler branch = _contextBranches.get(context);
185                 
186                 if (branch==null)
187                     context.handle(target,baseRequest,request, response);
188                 else
189                     branch.handle(target, baseRequest, request, response);
190                 return;
191             }
192         }
193         
194         // data structure which maps a request to a context; first-best match wins
195         // { context path => [ context ] }
196         // }
197         if (target.startsWith("/"))
198         {
199             int limit = target.length()-1;
200
201             while (limit>=0)
202             {
203                 // Get best match
204                 Map.Entry<String,Branch[]> branches = _pathBranches.getBest(target,1,limit);
205                 
206                 
207                 if (branches==null)
208                     break;
209                 
210                 int l=branches.getKey().length();
211                 if (l==1 || target.length()==l || target.charAt(l)=='/')
212                 {
213                     for (Branch branch : branches.getValue())
214                     {
215                         branch.getHandler().handle(target,baseRequest, request, response);
216                         if (baseRequest.isHandled())
217                             return;
218                     }
219                 }
220                 
221                 limit=l-2;
222             }
223         }
224         else
225         {
226             // This may not work in all circumstances... but then I think it should never be called
227             for (int i=0;i<handlers.length;i++)
228             {
229                 handlers[i].handle(target,baseRequest, request, response);
230                 if ( baseRequest.isHandled())
231                     return;
232             }
233         }
234     }
235
236     /* ------------------------------------------------------------ */
237     /** Add a context handler.
238      * @param contextPath  The context path to add
239      * @return the ContextHandler just added
240      */
241     public ContextHandler addContext(String contextPath,String resourceBase)
242     {
243         try
244         {
245             ContextHandler context = _contextClass.newInstance();
246             context.setContextPath(contextPath);
247             context.setResourceBase(resourceBase);
248             addHandler(context);
249             return context;
250         }
251         catch (Exception e)
252         {
253             LOG.debug(e);
254             throw new Error(e);
255         }
256     }
257
258
259
260     /* ------------------------------------------------------------ */
261     /**
262      * @return The class to use to add new Contexts
263      */
264     public Class<?> getContextClass()
265     {
266         return _contextClass;
267     }
268
269
270     /* ------------------------------------------------------------ */
271     /**
272      * @param contextClass The class to use to add new Contexts
273      */
274     public void setContextClass(Class<? extends ContextHandler> contextClass)
275     {
276         if (contextClass ==null || !(ContextHandler.class.isAssignableFrom(contextClass)))
277             throw new IllegalArgumentException();
278         _contextClass = contextClass;
279     }
280
281     /* ------------------------------------------------------------ */
282     /* ------------------------------------------------------------ */
283     /* ------------------------------------------------------------ */
284     private final static class Branch
285     {
286         private final Handler _handler;
287         private final ContextHandler[] _contexts;
288         
289         Branch(Handler handler)
290         {
291             _handler=handler;
292
293             if (handler instanceof ContextHandler)
294             {
295                 _contexts = new ContextHandler[]{(ContextHandler)handler};
296             }
297             else if (handler instanceof HandlerContainer)
298             {
299                 Handler[] contexts=((HandlerContainer)handler).getChildHandlersByClass(ContextHandler.class);
300                 _contexts = new ContextHandler[contexts.length];
301                 System.arraycopy(contexts, 0, _contexts, 0, contexts.length);
302             }
303             else
304                 _contexts = new ContextHandler[0];
305         }
306         
307         Set<String> getContextPaths()
308         {
309             Set<String> set = new HashSet<String>();
310             for (ContextHandler context:_contexts)
311                 set.add(context.getContextPath());
312             return set;
313         }
314         
315         boolean hasVirtualHost()
316         {
317             for (ContextHandler context:_contexts)
318                 if (context.getVirtualHosts()!=null && context.getVirtualHosts().length>0)
319                     return true;
320             return false;
321         }
322         
323         ContextHandler[] getContextHandlers()
324         {
325             return _contexts;
326         }
327         
328         Handler getHandler()
329         {
330             return _handler;
331         }
332         
333         @Override
334         public String toString()
335         {
336             return String.format("{%s,%s}",_handler,Arrays.asList(_contexts));
337         }
338     }
339
340
341 }