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