Search in sources :

Example 1 with MappingMatch

use of javax.servlet.http.MappingMatch in project undertow by undertow-io.

the class ServletPathMatches method setupServletChains.

/**
 * 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
 */
private ServletPathMatchesData setupServletChains() {
    // create the default servlet
    ServletHandler defaultServlet = null;
    final ManagedServlets servlets = deployment.getServlets();
    final ManagedFilters filters = deployment.getFilters();
    final Map<String, ServletHandler> extensionServlets = new HashMap<>();
    final Map<String, ServletHandler> pathServlets = new HashMap<>();
    final Set<String> pathMatches = new HashSet<>();
    final Set<String> extensionMatches = new HashSet<>();
    DeploymentInfo deploymentInfo = deployment.getDeploymentInfo();
    // loop through all filter mappings, and add them to the set of known paths
    for (FilterMappingInfo mapping : deploymentInfo.getFilterMappings()) {
        if (mapping.getMappingType() == FilterMappingInfo.MappingType.URL) {
            String path = mapping.getMapping();
            if (path.equals("*")) {
                // UNDERTOW-95, support this non-standard filter mapping
                path = "/*";
            }
            if (!path.startsWith("*.")) {
                pathMatches.add(path);
            } else {
                extensionMatches.add(path.substring(2));
            }
        }
    }
    // now loop through all servlets.
    for (Map.Entry<String, ServletHandler> entry : servlets.getServletHandlers().entrySet()) {
        final ServletHandler handler = entry.getValue();
        // add the servlet to the appropriate path maps
        for (String path : handler.getManagedServlet().getServletInfo().getMappings()) {
            if (path.equals("/")) {
                // the default servlet
                pathMatches.add("/*");
                if (defaultServlet != null) {
                    throw UndertowServletMessages.MESSAGES.twoServletsWithSameMapping(path);
                }
                defaultServlet = handler;
            } else if (!path.startsWith("*.")) {
                // either an exact or a /* based path match
                if (path.isEmpty()) {
                    path = "/";
                }
                pathMatches.add(path);
                if (pathServlets.containsKey(path)) {
                    throw UndertowServletMessages.MESSAGES.twoServletsWithSameMapping(path);
                }
                pathServlets.put(path, handler);
            } else {
                // an extension match based servlet
                String ext = path.substring(2);
                extensionMatches.add(ext);
                if (extensionServlets.containsKey(ext)) {
                    throw UndertowServletMessages.MESSAGES.twoServletsWithSameMapping(path);
                }
                extensionServlets.put(ext, handler);
            }
        }
    }
    ServletHandler managedDefaultServlet = servlets.getServletHandler(DEFAULT_SERVLET_NAME);
    if (managedDefaultServlet == null) {
        // we always create a default servlet, even if it is not going to have any path mappings registered
        managedDefaultServlet = servlets.addServlet(new ServletInfo(DEFAULT_SERVLET_NAME, DefaultServlet.class));
    }
    if (defaultServlet == null) {
        // no explicit default servlet was specified, so we register our mapping
        pathMatches.add("/*");
        defaultServlet = managedDefaultServlet;
    }
    final ServletPathMatchesData.Builder builder = ServletPathMatchesData.builder();
    // these paths contain both /* and exact matches.
    for (final String path : pathMatches) {
        // resolve the target servlet, will return null if this is the default servlet
        MatchData targetServletMatch = resolveServletForPath(path, pathServlets, extensionServlets, defaultServlet);
        final Map<DispatcherType, List<ManagedFilter>> noExtension = new EnumMap<>(DispatcherType.class);
        final Map<String, Map<DispatcherType, List<ManagedFilter>>> extension = new HashMap<>();
        // any filters that match the extension key
        for (String ext : extensionMatches) {
            extension.put(ext, new EnumMap<DispatcherType, List<ManagedFilter>>(DispatcherType.class));
        }
        // loop over all the filters, and add them to the appropriate map in the correct order
        for (final FilterMappingInfo filterMapping : deploymentInfo.getFilterMappings()) {
            ManagedFilter filter = filters.getManagedFilter(filterMapping.getFilterName());
            if (filterMapping.getMappingType() == FilterMappingInfo.MappingType.SERVLET) {
                if (targetServletMatch.handler != null) {
                    if (filterMapping.getMapping().equals(targetServletMatch.handler.getManagedServlet().getServletInfo().getName()) || filterMapping.getMapping().equals("*")) {
                        addToListMap(noExtension, filterMapping.getDispatcher(), filter);
                    }
                }
                for (Map.Entry<String, Map<DispatcherType, List<ManagedFilter>>> entry : extension.entrySet()) {
                    ServletHandler pathServlet = targetServletMatch.handler;
                    boolean defaultServletMatch = targetServletMatch.defaultServlet;
                    if (defaultServletMatch && extensionServlets.containsKey(entry.getKey())) {
                        pathServlet = extensionServlets.get(entry.getKey());
                    }
                    if (filterMapping.getMapping().equals(pathServlet.getManagedServlet().getServletInfo().getName()) || filterMapping.getMapping().equals("*")) {
                        addToListMap(extension.get(entry.getKey()), 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);
                }
            }
        }
        // resolve any matches and add them to the builder
        if (path.endsWith("/*")) {
            String prefix = path.substring(0, path.length() - 2);
            // add the default non-extension match
            builder.addPrefixMatch(prefix, createHandler(deploymentInfo, targetServletMatch.handler, noExtension, targetServletMatch.matchedPath, targetServletMatch.defaultServlet, targetServletMatch.mappingMatch, targetServletMatch.userPath), targetServletMatch.defaultServlet || targetServletMatch.handler.getManagedServlet().getServletInfo().isRequireWelcomeFileMapping());
            // build up the chain for each non-extension match
            for (Map.Entry<String, Map<DispatcherType, List<ManagedFilter>>> entry : extension.entrySet()) {
                ServletHandler pathServlet = targetServletMatch.handler;
                String pathMatch = targetServletMatch.matchedPath;
                final boolean defaultServletMatch;
                final String servletMatchPattern;
                final MappingMatch mappingMatch;
                if (targetServletMatch.defaultServlet) {
                    // Path matches always take precedence over extension matches, however the default servlet is matched
                    // at a lower priority, after extension matches. The "/*" pattern is applied implicitly onto the
                    // default servlet. If there's an extension match in addition to a non-default servlet path match,
                    // the servlet path match is higher priority. However if the path match is the default servlets
                    // default catch-all path, the extension match is a higher priority.
                    ServletHandler extensionServletHandler = extensionServlets.get(entry.getKey());
                    if (extensionServletHandler != null) {
                        defaultServletMatch = false;
                        pathServlet = extensionServletHandler;
                        servletMatchPattern = "*." + entry.getKey();
                        mappingMatch = MappingMatch.EXTENSION;
                    } else {
                        defaultServletMatch = true;
                        servletMatchPattern = "/";
                        mappingMatch = MappingMatch.DEFAULT;
                    }
                } else {
                    defaultServletMatch = false;
                    servletMatchPattern = path;
                    mappingMatch = MappingMatch.PATH;
                }
                HttpHandler handler = pathServlet;
                if (!entry.getValue().isEmpty()) {
                    handler = new FilterHandler(entry.getValue(), deploymentInfo.isAllowNonStandardWrappers(), handler);
                }
                builder.addExtensionMatch(prefix, entry.getKey(), servletChain(handler, pathServlet.getManagedServlet(), entry.getValue(), pathMatch, deploymentInfo, defaultServletMatch, mappingMatch, servletMatchPattern));
            }
        } else if (path.isEmpty()) {
            // the context root match
            builder.addExactMatch("/", createHandler(deploymentInfo, targetServletMatch.handler, noExtension, targetServletMatch.matchedPath, targetServletMatch.defaultServlet, targetServletMatch.mappingMatch, targetServletMatch.userPath));
        } else {
            // we need to check for an extension match, so paths like /exact.txt will have the correct filter applied
            int lastSegmentIndex = path.lastIndexOf('/');
            String lastSegment;
            if (lastSegmentIndex > 0) {
                lastSegment = path.substring(lastSegmentIndex);
            } else {
                lastSegment = path;
            }
            if (lastSegment.contains(".")) {
                String ext = lastSegment.substring(lastSegment.lastIndexOf('.') + 1);
                if (extension.containsKey(ext)) {
                    Map<DispatcherType, List<ManagedFilter>> extMap = extension.get(ext);
                    builder.addExactMatch(path, createHandler(deploymentInfo, targetServletMatch.handler, extMap, targetServletMatch.matchedPath, targetServletMatch.defaultServlet, targetServletMatch.mappingMatch, targetServletMatch.userPath));
                } else {
                    builder.addExactMatch(path, createHandler(deploymentInfo, targetServletMatch.handler, noExtension, targetServletMatch.matchedPath, targetServletMatch.defaultServlet, targetServletMatch.mappingMatch, targetServletMatch.userPath));
                }
            } else {
                builder.addExactMatch(path, createHandler(deploymentInfo, targetServletMatch.handler, noExtension, targetServletMatch.matchedPath, targetServletMatch.defaultServlet, targetServletMatch.mappingMatch, targetServletMatch.userPath));
            }
        }
    }
    // these are used for name based dispatch
    for (Map.Entry<String, ServletHandler> entry : servlets.getServletHandlers().entrySet()) {
        final Map<DispatcherType, List<ManagedFilter>> filtersByDispatcher = new EnumMap<>(DispatcherType.class);
        for (final FilterMappingInfo filterMapping : deploymentInfo.getFilterMappings()) {
            ManagedFilter filter = filters.getManagedFilter(filterMapping.getFilterName());
            if (filterMapping.getMappingType() == FilterMappingInfo.MappingType.SERVLET) {
                if (filterMapping.getMapping().equals(entry.getKey())) {
                    addToListMap(filtersByDispatcher, filterMapping.getDispatcher(), filter);
                }
            }
        }
        if (filtersByDispatcher.isEmpty()) {
            builder.addNameMatch(entry.getKey(), servletChain(entry.getValue(), entry.getValue().getManagedServlet(), filtersByDispatcher, null, deploymentInfo, false, MappingMatch.EXACT, ""));
        } else {
            builder.addNameMatch(entry.getKey(), servletChain(new FilterHandler(filtersByDispatcher, deploymentInfo.isAllowNonStandardWrappers(), entry.getValue()), entry.getValue().getManagedServlet(), filtersByDispatcher, null, deploymentInfo, false, MappingMatch.EXACT, ""));
        }
    }
    return builder.build();
}
Also used : ManagedServlets(io.undertow.servlet.core.ManagedServlets) FilterMappingInfo(io.undertow.servlet.api.FilterMappingInfo) HashMap(java.util.HashMap) ServletInfo(io.undertow.servlet.api.ServletInfo) ArrayList(java.util.ArrayList) List(java.util.List) ManagedFilter(io.undertow.servlet.core.ManagedFilter) DeploymentInfo(io.undertow.servlet.api.DeploymentInfo) DispatcherType(javax.servlet.DispatcherType) EnumMap(java.util.EnumMap) ManagedFilters(io.undertow.servlet.core.ManagedFilters) HashSet(java.util.HashSet) HttpHandler(io.undertow.server.HttpHandler) MappingMatch(javax.servlet.http.MappingMatch) HashMap(java.util.HashMap) Map(java.util.Map) EnumMap(java.util.EnumMap)

Aggregations

HttpHandler (io.undertow.server.HttpHandler)1 DeploymentInfo (io.undertow.servlet.api.DeploymentInfo)1 FilterMappingInfo (io.undertow.servlet.api.FilterMappingInfo)1 ServletInfo (io.undertow.servlet.api.ServletInfo)1 ManagedFilter (io.undertow.servlet.core.ManagedFilter)1 ManagedFilters (io.undertow.servlet.core.ManagedFilters)1 ManagedServlets (io.undertow.servlet.core.ManagedServlets)1 ArrayList (java.util.ArrayList)1 EnumMap (java.util.EnumMap)1 HashMap (java.util.HashMap)1 HashSet (java.util.HashSet)1 List (java.util.List)1 Map (java.util.Map)1 DispatcherType (javax.servlet.DispatcherType)1 MappingMatch (javax.servlet.http.MappingMatch)1