/** * Sets up the handlers in the servlet chain. We setup a chain for every path + extension match * possibility. (i.e. if there a m path mappings and n extension mappings we have n*m chains). * * <p>If a chain consists of only the default servlet then we add it as an async handler, so that * resources can be served up directly without using blocking operations. * * <p>TODO: this logic is a bit convoluted at the moment, we should look at simplifying it * * @param servletContext * @param threadSetupAction * @param listeners */ private ServletPathMatches setupServletChains( final ServletContextImpl servletContext, final CompositeThreadSetupAction threadSetupAction, final ApplicationListeners listeners) { final List<Lifecycle> lifecycles = new ArrayList<Lifecycle>(); // create the default servlet ServletChain defaultHandler = null; ServletHandler defaultServlet = null; final Map<String, ManagedFilter> managedFilterMap = new LinkedHashMap<String, ManagedFilter>(); final Map<String, ServletHandler> allServlets = new HashMap<String, ServletHandler>(); final Map<String, ServletHandler> extensionServlets = new HashMap<String, ServletHandler>(); final Map<String, ServletHandler> pathServlets = new HashMap<String, ServletHandler>(); final Set<String> pathMatches = new HashSet<String>(); final Set<String> extensionMatches = new HashSet<String>(); DeploymentInfo deploymentInfo = deployment.getDeploymentInfo(); for (Map.Entry<String, FilterInfo> entry : deploymentInfo.getFilters().entrySet()) { final ManagedFilter mf = new ManagedFilter(entry.getValue(), servletContext); managedFilterMap.put(entry.getValue().getName(), mf); lifecycles.add(mf); } for (FilterMappingInfo mapping : deploymentInfo.getFilterMappings()) { if (mapping.getMappingType() == FilterMappingInfo.MappingType.URL) { String path = mapping.getMapping(); if (!path.startsWith("*.")) { pathMatches.add(path); } else { extensionMatches.add(path.substring(2)); } } } for (Map.Entry<String, ServletInfo> entry : deploymentInfo.getServlets().entrySet()) { ServletInfo servlet = entry.getValue(); final ManagedServlet managedServlet = new ManagedServlet(servlet, servletContext); lifecycles.add(managedServlet); final ServletHandler handler = new ServletHandler(managedServlet); allServlets.put(entry.getKey(), handler); for (String path : entry.getValue().getMappings()) { if (path.equals("/")) { // the default servlet pathMatches.add("/*"); if (pathServlets.containsKey("/*")) { throw UndertowServletMessages.MESSAGES.twoServletsWithSameMapping(path); } defaultServlet = handler; defaultHandler = servletChain(handler, managedServlet); } else if (!path.startsWith("*.")) { pathMatches.add(path); if (pathServlets.containsKey(path)) { throw UndertowServletMessages.MESSAGES.twoServletsWithSameMapping(path); } pathServlets.put(path, handler); } else { String ext = path.substring(2); extensionMatches.add(ext); extensionServlets.put(ext, handler); } } } if (defaultServlet == null) { final DefaultServletConfig config = deploymentInfo.getDefaultServletConfig() == null ? new DefaultServletConfig() : deploymentInfo.getDefaultServletConfig(); DefaultServlet defaultInstance = new DefaultServlet(deployment, config, deploymentInfo.getWelcomePages()); final ManagedServlet managedDefaultServlet = new ManagedServlet( new ServletInfo( "io.undertow.DefaultServlet", DefaultServlet.class, new ImmediateInstanceFactory<Servlet>(defaultInstance)), servletContext); lifecycles.add(managedDefaultServlet); pathMatches.add("/*"); defaultServlet = new ServletHandler(managedDefaultServlet); defaultHandler = new ServletChain(defaultServlet, managedDefaultServlet); } final ServletPathMatches.Builder builder = ServletPathMatches.builder(); for (final String path : pathMatches) { ServletHandler targetServlet = resolveServletForPath(path, pathServlets); final Map<DispatcherType, List<ManagedFilter>> noExtension = new HashMap<DispatcherType, List<ManagedFilter>>(); final Map<String, Map<DispatcherType, List<ManagedFilter>>> extension = new HashMap<String, Map<DispatcherType, List<ManagedFilter>>>(); for (String ext : extensionMatches) { extension.put(ext, new HashMap<DispatcherType, List<ManagedFilter>>()); } for (final FilterMappingInfo filterMapping : deploymentInfo.getFilterMappings()) { ManagedFilter filter = managedFilterMap.get(filterMapping.getFilterName()); if (filterMapping.getMappingType() == FilterMappingInfo.MappingType.SERVLET) { if (targetServlet != null) { if (filterMapping .getMapping() .equals(targetServlet.getManagedServlet().getServletInfo().getName())) { addToListMap(noExtension, filterMapping.getDispatcher(), filter); for (Map<DispatcherType, List<ManagedFilter>> l : extension.values()) { addToListMap(l, filterMapping.getDispatcher(), filter); } } } } else { if (filterMapping.getMapping().isEmpty() || !filterMapping.getMapping().startsWith("*.")) { if (isFilterApplicable(path, filterMapping.getMapping())) { addToListMap(noExtension, filterMapping.getDispatcher(), filter); for (Map<DispatcherType, List<ManagedFilter>> l : extension.values()) { addToListMap(l, filterMapping.getDispatcher(), filter); } } } else { addToListMap( extension.get(filterMapping.getMapping().substring(2)), filterMapping.getDispatcher(), filter); } } } final ServletChain initialHandler; if (noExtension.isEmpty()) { if (targetServlet != null) { initialHandler = servletChain(targetServlet, targetServlet.getManagedServlet()); } else { initialHandler = defaultHandler; } } else { FilterHandler handler; if (targetServlet != null) { handler = new FilterHandler(noExtension, targetServlet); } else { handler = new FilterHandler(noExtension, defaultServlet); } initialHandler = servletChain( handler, targetServlet == null ? defaultServlet.getManagedServlet() : targetServlet.getManagedServlet()); } if (path.endsWith("/*")) { String prefix = path.substring(0, path.length() - 2); builder.addPrefixMatch(prefix, initialHandler); for (Map.Entry<String, Map<DispatcherType, List<ManagedFilter>>> entry : extension.entrySet()) { ServletHandler pathServlet = targetServlet; if (pathServlet == null) { pathServlet = extensionServlets.get(entry.getKey()); } if (pathServlet == null) { pathServlet = defaultServlet; } HttpHandler handler = pathServlet; if (!entry.getValue().isEmpty()) { handler = new FilterHandler(entry.getValue(), handler); } builder.addExtensionMatch( prefix, entry.getKey(), servletChain(handler, pathServlet.getManagedServlet())); } } else if (path.isEmpty()) { builder.addExactMatch("/", initialHandler); } else { builder.addExactMatch(path, initialHandler); } } // now setup name based mappings // these are used for name based dispatch for (Map.Entry<String, ServletHandler> entry : allServlets.entrySet()) { final Map<DispatcherType, List<ManagedFilter>> filters = new HashMap<DispatcherType, List<ManagedFilter>>(); for (final FilterMappingInfo filterMapping : deploymentInfo.getFilterMappings()) { ManagedFilter filter = managedFilterMap.get(filterMapping.getFilterName()); if (filterMapping.getMappingType() == FilterMappingInfo.MappingType.SERVLET) { if (filterMapping.getMapping().equals(entry.getKey())) { addToListMap(filters, filterMapping.getDispatcher(), filter); } } } if (filters.isEmpty()) { builder.addNameMatch( entry.getKey(), servletChain(entry.getValue(), entry.getValue().getManagedServlet())); } else { builder.addNameMatch( entry.getKey(), servletChain( new FilterHandler(filters, entry.getValue()), entry.getValue().getManagedServlet())); } } builder.setDefaultServlet(defaultHandler); deployment.addLifecycleObjects(lifecycles); return builder.build(); }
@Override public void deploy() { final DeploymentInfo deploymentInfo = originalDeployment.clone(); if (deploymentInfo.getServletStackTraces() == ServletStackTraces.ALL) { UndertowServletLogger.REQUEST_LOGGER.servletStackTracesAll( deploymentInfo.getDeploymentName()); } deploymentInfo.validate(); final DeploymentImpl deployment = new DeploymentImpl(this, deploymentInfo, servletContainer); this.deployment = deployment; final List<ThreadSetupHandler> setup = new ArrayList<>(); setup.add(ServletRequestContextThreadSetupAction.INSTANCE); setup.add(new ContextClassLoaderSetupAction(deploymentInfo.getClassLoader())); setup.addAll(deploymentInfo.getThreadSetupActions()); deployment.setThreadSetupActions(setup); final ServletContextImpl servletContext = new ServletContextImpl(servletContainer, deployment); deployment.setServletContext(servletContext); handleExtensions(deploymentInfo, servletContext); deployment.getServletPaths().setWelcomePages(deploymentInfo.getWelcomePages()); deployment.setDefaultCharset(Charset.forName(deploymentInfo.getDefaultEncoding())); handleDeploymentSessionConfig(deploymentInfo, servletContext); deployment.setSessionManager( deploymentInfo.getSessionManagerFactory().createSessionManager(deployment)); deployment .getSessionManager() .setDefaultSessionTimeout(deploymentInfo.getDefaultSessionTimeout()); try { deployment .createThreadSetupAction( new ThreadSetupHandler.Action<Void, Object>() { @Override public Void call(HttpServerExchange exchange, Object ignore) throws Exception { final ApplicationListeners listeners = createListeners(); listeners.start(); deployment.setApplicationListeners(listeners); // now create the servlets and filters that we know about. We can still get more // later createServletsAndFilters(deployment, deploymentInfo); // first run the SCI's for (final ServletContainerInitializerInfo sci : deploymentInfo.getServletContainerInitializers()) { final InstanceHandle<? extends ServletContainerInitializer> instance = sci.getInstanceFactory().createInstance(); try { instance.getInstance().onStartup(sci.getHandlesTypes(), servletContext); } finally { instance.release(); } } deployment .getSessionManager() .registerSessionListener( new SessionListenerBridge(deployment, listeners, servletContext)); for (SessionListener listener : deploymentInfo.getSessionListeners()) { deployment.getSessionManager().registerSessionListener(listener); } initializeErrorPages(deployment, deploymentInfo); initializeMimeMappings(deployment, deploymentInfo); initializeTempDir(servletContext, deploymentInfo); listeners.contextInitialized(); // run HttpHandler wrappedHandlers = ServletDispatchingHandler.INSTANCE; wrappedHandlers = wrapHandlers(wrappedHandlers, deploymentInfo.getInnerHandlerChainWrappers()); if (!deploymentInfo.isSecurityDisabled()) { HttpHandler securityHandler = setupSecurityHandlers(wrappedHandlers); wrappedHandlers = new PredicateHandler( DispatcherTypePredicate.REQUEST, securityHandler, wrappedHandlers); } HttpHandler outerHandlers = wrapHandlers(wrappedHandlers, deploymentInfo.getOuterHandlerChainWrappers()); wrappedHandlers = new PredicateHandler( DispatcherTypePredicate.REQUEST, outerHandlers, wrappedHandlers); wrappedHandlers = handleDevelopmentModePersistentSessions( wrappedHandlers, deploymentInfo, deployment.getSessionManager(), servletContext); MetricsCollector metrics = deploymentInfo.getMetricsCollector(); if (metrics != null) { wrappedHandlers = new MetricsChainHandler(wrappedHandlers, metrics, deployment); } if (deploymentInfo.getCrawlerSessionManagerConfig() != null) { wrappedHandlers = new CrawlerSessionManagerHandler( deploymentInfo.getCrawlerSessionManagerConfig(), wrappedHandlers); } final ServletInitialHandler servletInitialHandler = SecurityActions.createServletInitialHandler( deployment.getServletPaths(), wrappedHandlers, deployment, servletContext); HttpHandler initialHandler = wrapHandlers( servletInitialHandler, deployment.getDeploymentInfo().getInitialHandlerChainWrappers()); initialHandler = new HttpContinueReadHandler(initialHandler); if (deploymentInfo.getUrlEncoding() != null) { initialHandler = Handlers.urlDecodingHandler( deploymentInfo.getUrlEncoding(), initialHandler); } deployment.setInitialHandler(initialHandler); deployment.setServletHandler(servletInitialHandler); deployment .getServletPaths() .invalidate(); // make sure we have a fresh set of servlet paths servletContext.initDone(); return null; } }) .call(null, null); } catch (Exception e) { throw new RuntimeException(e); } state = State.DEPLOYED; }