All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.vertx.ext.web.impl.RouteState Maven / Gradle / Ivy

There is a newer version: 5.0.0.CR1
Show newest version
/*
 * Copyright 2019 Red Hat, Inc.
 *
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 *  and Apache License v2.0 which accompanies this distribution.
 *
 *  The Eclipse Public License is available at
 *  http://www.eclipse.org/legal/epl-v10.html
 *
 *  The Apache License v2.0 is available at
 *  http://www.opensource.org/licenses/apache2.0.php
 *
 *  You may elect to redistribute this code under either of these licenses.
 */
package io.vertx.ext.web.impl;

import io.vertx.core.Handler;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.impl.logging.Logger;
import io.vertx.core.impl.logging.LoggerFactory;
import io.vertx.core.net.impl.URIDecoder;
import io.vertx.ext.web.MIMEHeader;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.*;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * This class encapsulates the route state, all mutations are atomic and return a new state with the mutation.
 * 

* This class is thread-safe * * @author Paulo Lopes */ final class RouteState { private static final Logger LOG = LoggerFactory.getLogger(RouteState.class); enum Priority { PLATFORM, SECURITY_POLICY, PROTOCOL_UPGRADE, BODY, MULTI_TENANT, AUTHENTICATION, INPUT_TRUST, AUTHORIZATION, USER } private static Priority weight(Handler handler) { if (handler instanceof PlatformHandler) { return Priority.PLATFORM; } if (handler instanceof SecurityPolicyHandler) { return Priority.SECURITY_POLICY; } if (handler instanceof ProtocolUpgradeHandler) { return Priority.PROTOCOL_UPGRADE; } if (handler instanceof BodyHandler) { return Priority.BODY; } if (handler instanceof MultiTenantHandler) { return Priority.MULTI_TENANT; } if (handler instanceof AuthenticationHandler) { return Priority.AUTHENTICATION; } if (handler instanceof InputTrustHandler) { return Priority.INPUT_TRUST; } if (handler instanceof AuthorizationHandler) { return Priority.AUTHORIZATION; } return Priority.USER; } private final RouteImpl route; private final Map metadata; private final String path; private final String name; private final int order; private final boolean enabled; private final Set methods; private final Set consumes; private final boolean emptyBodyPermittedWithConsumes; private final Set produces; private final List> contextHandlers; private final List> failureHandlers; private final boolean added; private final Pattern pattern; private final List groups; private final boolean useNormalizedPath; private final Set namedGroupsInRegex; private final Pattern virtualHostPattern; private final boolean pathEndsWithSlash; private final boolean exclusive; private final boolean exactPath; private RouteState(RouteImpl route, Map metadata, String path, String name, int order, boolean enabled, Set methods, Set consumes, boolean emptyBodyPermittedWithConsumes, Set produces, List> contextHandlers, List> failureHandlers, boolean added, Pattern pattern, List groups, boolean useNormalizedPath, Set namedGroupsInRegex, Pattern virtualHostPattern, boolean pathEndsWithSlash, boolean exclusive, boolean exactPath) { this.route = route; this.metadata = metadata; this.path = path; this.name = name; this.order = order; this.enabled = enabled; this.methods = methods; this.consumes = consumes; this.emptyBodyPermittedWithConsumes = emptyBodyPermittedWithConsumes; this.produces = produces; this.contextHandlers = contextHandlers; this.failureHandlers = failureHandlers; this.added = added; this.pattern = pattern; this.groups = groups; this.useNormalizedPath = useNormalizedPath; this.namedGroupsInRegex = namedGroupsInRegex; this.virtualHostPattern = virtualHostPattern; this.pathEndsWithSlash = pathEndsWithSlash; this.exclusive = exclusive; this.exactPath = exactPath; } RouteState(RouteImpl route, int order) { this( route, null, null, null, order, true, null, null, false, null, null, null, false, null, null, true, null, null, false, false, true); } public RouteImpl getRoute() { return route; } public RouterImpl getRouter() { return route.router(); } public RouteState putMetadata(String key, Object value) { Map metadata = this.metadata == null ? new HashMap<>() : new HashMap<>(this.metadata); if (value == null) { metadata.remove(key); } else { metadata.put(key, value); } return new RouteState( this.route, Collections.unmodifiableMap(metadata), this.path, this.name, this.order, this.enabled, this.methods, this.consumes, this.emptyBodyPermittedWithConsumes, this.produces, this.contextHandlers, this.failureHandlers, this.added, this.pattern, this.groups, this.useNormalizedPath, this.namedGroupsInRegex, this.virtualHostPattern, this.pathEndsWithSlash, this.exclusive, this.exactPath); } public Map getMetadata() { return metadata; } public String getPath() { return path; } RouteState setPath(String path) { return new RouteState( this.route, this.metadata, path, this.name, this.order, this.enabled, this.methods, this.consumes, this.emptyBodyPermittedWithConsumes, this.produces, this.contextHandlers, this.failureHandlers, this.added, this.pattern, this.groups, this.useNormalizedPath, this.namedGroupsInRegex, this.virtualHostPattern, this.pathEndsWithSlash, this.exclusive, this.exactPath); } public int getOrder() { return order; } RouteState setOrder(int order) { return new RouteState( this.route, this.metadata, this.path, this.name, order, this.enabled, this.methods, this.consumes, this.emptyBodyPermittedWithConsumes, this.produces, this.contextHandlers, this.failureHandlers, this.added, this.pattern, this.groups, this.useNormalizedPath, this.namedGroupsInRegex, this.virtualHostPattern, this.pathEndsWithSlash, this.exclusive, this.exactPath); } public boolean isEnabled() { return enabled; } RouteState setEnabled(boolean enabled) { return new RouteState( this.route, this.metadata, this.path, this.name, this.order, enabled, this.methods, this.consumes, this.emptyBodyPermittedWithConsumes, this.produces, this.contextHandlers, this.failureHandlers, this.added, this.pattern, this.groups, this.useNormalizedPath, this.namedGroupsInRegex, this.virtualHostPattern, this.pathEndsWithSlash, this.exclusive, this.exactPath); } public Set getMethods() { return methods; } RouteState setMethods(Set methods) { return new RouteState( this.route, this.metadata, this.path, this.name, this.order, this.enabled, methods, this.consumes, this.emptyBodyPermittedWithConsumes, this.produces, this.contextHandlers, this.failureHandlers, this.added, this.pattern, this.groups, this.useNormalizedPath, this.namedGroupsInRegex, this.virtualHostPattern, this.pathEndsWithSlash, this.exclusive, this.exactPath); } public RouteState addMethod(HttpMethod method) { RouteState newState = new RouteState( this.route, this.metadata, this.path, this.name, this.order, this.enabled, this.methods == null ? new HashSet<>() : new HashSet<>(this.methods), this.consumes, this.emptyBodyPermittedWithConsumes, this.produces, this.contextHandlers, this.failureHandlers, this.added, this.pattern, this.groups, this.useNormalizedPath, this.namedGroupsInRegex, this.virtualHostPattern, this.pathEndsWithSlash, this.exclusive, this.exactPath); newState.methods.add(method); return newState; } public Set getConsumes() { return consumes; } RouteState setConsumes(Set consumes) { return new RouteState( this.route, this.metadata, this.path, this.name, this.order, this.enabled, this.methods, consumes, this.emptyBodyPermittedWithConsumes, this.produces, this.contextHandlers, this.failureHandlers, this.added, this.pattern, this.groups, this.useNormalizedPath, this.namedGroupsInRegex, this.virtualHostPattern, this.pathEndsWithSlash, this.exclusive, this.exactPath); } RouteState addConsume(MIMEHeader mime) { RouteState newState = new RouteState( this.route, this.metadata, this.path, this.name, this.order, this.enabled, this.methods, this.consumes == null ? new LinkedHashSet<>() : new LinkedHashSet<>(this.consumes), this.emptyBodyPermittedWithConsumes, this.produces, this.contextHandlers, this.failureHandlers, this.added, this.pattern, this.groups, this.useNormalizedPath, this.namedGroupsInRegex, this.virtualHostPattern, this.pathEndsWithSlash, this.exclusive, this.exactPath); newState.consumes.add(mime); return newState; } public boolean isEmptyBodyPermittedWithConsumes() { return emptyBodyPermittedWithConsumes; } RouteState setEmptyBodyPermittedWithConsumes(boolean emptyBodyPermittedWithConsumes) { return new RouteState( this.route, this.metadata, this.path, this.name, this.order, this.enabled, this.methods, this.consumes, emptyBodyPermittedWithConsumes, this.produces, this.contextHandlers, this.failureHandlers, this.added, this.pattern, this.groups, this.useNormalizedPath, this.namedGroupsInRegex, this.virtualHostPattern, this.pathEndsWithSlash, this.exclusive, this.exactPath); } public Set getProduces() { return produces; } RouteState setProduces(Set produces) { return new RouteState( this.route, this.metadata, this.path, this.name, this.order, this.enabled, this.methods, this.consumes, this.emptyBodyPermittedWithConsumes, produces, this.contextHandlers, this.failureHandlers, this.added, this.pattern, this.groups, this.useNormalizedPath, this.namedGroupsInRegex, this.virtualHostPattern, this.pathEndsWithSlash, this.exclusive, this.exactPath); } RouteState addProduce(MIMEHeader mime) { RouteState newState = new RouteState( this.route, this.metadata, this.path, this.name, this.order, this.enabled, this.methods, this.consumes, this.emptyBodyPermittedWithConsumes, this.produces == null ? new LinkedHashSet<>() : new LinkedHashSet<>(this.produces), this.contextHandlers, this.failureHandlers, this.added, this.pattern, this.groups, this.useNormalizedPath, this.namedGroupsInRegex, this.virtualHostPattern, this.pathEndsWithSlash, this.exclusive, this.exactPath); newState.produces.add(mime); return newState; } public List> getContextHandlers() { return contextHandlers; } public int getContextHandlersLength() { return contextHandlers == null ? 0 : contextHandlers.size(); } RouteState setContextHandlers(List> contextHandlers) { return new RouteState( this.route, this.metadata, this.path, this.name, this.order, this.enabled, this.methods, this.consumes, this.emptyBodyPermittedWithConsumes, this.produces, contextHandlers, this.failureHandlers, this.added, this.pattern, this.groups, this.useNormalizedPath, this.namedGroupsInRegex, this.virtualHostPattern, this.pathEndsWithSlash, this.exclusive, this.exactPath); } RouteState addContextHandler(Handler contextHandler) { RouteState newState = new RouteState( this.route, this.metadata, this.path, this.name, this.order, this.enabled, this.methods, this.consumes, this.emptyBodyPermittedWithConsumes, this.produces, this.contextHandlers == null ? new ArrayList<>() : new ArrayList<>(this.contextHandlers), this.failureHandlers, this.added, this.pattern, this.groups, this.useNormalizedPath, this.namedGroupsInRegex, this.virtualHostPattern, this.pathEndsWithSlash, this.exclusive, this.exactPath); int len = newState.contextHandlers.size(); final Priority weight = weight(contextHandler); final Priority lastWeight; if (len > 0) { lastWeight = weight(newState.contextHandlers.get(len - 1)); if (lastWeight.ordinal() > weight.ordinal()) { String message = "Cannot add [" + weight.name() + "] handler to route with [" + lastWeight.name() + "] handler at index " + (len - 1); // when lenient mode is disabled, throw IllegalStateException to signal that the setup is incorrect if (!Boolean.getBoolean("io.vertx.web.router.setup.lenient")) { throw new IllegalStateException(message); } LOG.warn(message); } } else { lastWeight = null; } if (lastWeight == Priority.PROTOCOL_UPGRADE) { // when lenient mode is disabled, don't log to signal that the setup might be incorrect if (!Boolean.getBoolean("io.vertx.web.router.setup.lenient")) { LOG.warn("Adding an handler after PROTOCOL_UPGRADE handler may not be reachable"); } } newState.contextHandlers.add(contextHandler); return newState; } public List> getFailureHandlers() { return failureHandlers; } public int getFailureHandlersLength() { return failureHandlers == null ? 0 : failureHandlers.size(); } RouteState setFailureHandlers(List> failureHandlers) { return new RouteState( this.route, this.metadata, this.path, this.name, this.order, this.enabled, this.methods, this.consumes, this.emptyBodyPermittedWithConsumes, this.produces, this.contextHandlers, failureHandlers, this.added, this.pattern, this.groups, this.useNormalizedPath, this.namedGroupsInRegex, this.virtualHostPattern, this.pathEndsWithSlash, this.exclusive, this.exactPath); } RouteState addFailureHandler(Handler failureHandler) { RouteState newState = new RouteState( this.route, this.metadata, this.path, this.name, this.order, this.enabled, this.methods, this.consumes, this.emptyBodyPermittedWithConsumes, this.produces, this.contextHandlers, this.failureHandlers == null ? new ArrayList<>() : new ArrayList<>(this.failureHandlers), this.added, this.pattern, this.groups, this.useNormalizedPath, this.namedGroupsInRegex, this.virtualHostPattern, this.pathEndsWithSlash, this.exclusive, this.exactPath); newState.failureHandlers.add(failureHandler); return newState; } public boolean isAdded() { return added; } RouteState setAdded(boolean added) { return new RouteState( this.route, this.metadata, this.path, this.name, this.order, this.enabled, this.methods, this.consumes, this.emptyBodyPermittedWithConsumes, this.produces, this.contextHandlers, this.failureHandlers, added, this.pattern, this.groups, this.useNormalizedPath, this.namedGroupsInRegex, this.virtualHostPattern, this.pathEndsWithSlash, this.exclusive, this.exactPath); } public Pattern getPattern() { return pattern; } RouteState setPattern(Pattern pattern) { return new RouteState( this.route, this.metadata, this.path, this.name, this.order, this.enabled, this.methods, this.consumes, this.emptyBodyPermittedWithConsumes, this.produces, this.contextHandlers, this.failureHandlers, this.added, pattern, this.groups, this.useNormalizedPath, this.namedGroupsInRegex, this.virtualHostPattern, this.pathEndsWithSlash, this.exclusive, this.exactPath); } public List getGroups() { return groups; } RouteState setGroups(List groups) { return new RouteState( this.route, this.metadata, this.path, this.name, this.order, this.enabled, this.methods, this.consumes, this.emptyBodyPermittedWithConsumes, this.produces, this.contextHandlers, this.failureHandlers, this.added, this.pattern, groups, this.useNormalizedPath, this.namedGroupsInRegex, this.virtualHostPattern, this.pathEndsWithSlash, this.exclusive, this.exactPath); } RouteState addGroup(String group) { RouteState newState = new RouteState( this.route, this.metadata, this.path, this.name, this.order, this.enabled, this.methods, this.consumes, this.emptyBodyPermittedWithConsumes, this.produces, this.contextHandlers, this.failureHandlers, this.added, this.pattern, this.groups == null ? new ArrayList<>() : new ArrayList<>(groups), this.useNormalizedPath, this.namedGroupsInRegex, this.virtualHostPattern, this.pathEndsWithSlash, this.exclusive, this.exactPath); newState.groups.add(group); return newState; } public boolean isUseNormalizedPath() { return useNormalizedPath; } RouteState setUseNormalizedPath(boolean useNormalizedPath) { return new RouteState( this.route, this.metadata, this.path, this.name, this.order, this.enabled, this.methods, this.consumes, this.emptyBodyPermittedWithConsumes, this.produces, this.contextHandlers, this.failureHandlers, this.added, this.pattern, this.groups, useNormalizedPath, this.namedGroupsInRegex, this.virtualHostPattern, this.pathEndsWithSlash, this.exclusive, this.exactPath); } public Set getNamedGroupsInRegex() { return namedGroupsInRegex; } RouteState setNamedGroupsInRegex(Set namedGroupsInRegex) { return new RouteState( this.route, this.metadata, this.path, this.name, this.order, this.enabled, this.methods, this.consumes, this.emptyBodyPermittedWithConsumes, this.produces, this.contextHandlers, this.failureHandlers, this.added, this.pattern, this.groups, this.useNormalizedPath, namedGroupsInRegex, this.virtualHostPattern, this.pathEndsWithSlash, this.exclusive, this.exactPath); } RouteState addNamedGroupInRegex(String namedGroupInRegex) { RouteState newState = new RouteState( this.route, this.metadata, this.path, this.name, this.order, this.enabled, this.methods, this.consumes, this.emptyBodyPermittedWithConsumes, this.produces, this.contextHandlers, this.failureHandlers, this.added, this.pattern, this.groups, this.useNormalizedPath, this.namedGroupsInRegex == null ? new HashSet<>() : new HashSet<>(this.namedGroupsInRegex), this.virtualHostPattern, this.pathEndsWithSlash, this.exclusive, this.exactPath); newState.namedGroupsInRegex.add(namedGroupInRegex); return newState; } public Pattern getVirtualHostPattern() { return virtualHostPattern; } RouteState setVirtualHostPattern(Pattern virtualHostPattern) { return new RouteState( this.route, this.metadata, this.path, this.name, this.order, this.enabled, this.methods, this.consumes, this.emptyBodyPermittedWithConsumes, this.produces, this.contextHandlers, this.failureHandlers, this.added, this.pattern, this.groups, this.useNormalizedPath, this.namedGroupsInRegex, virtualHostPattern, this.pathEndsWithSlash, this.exclusive, this.exactPath); } public boolean isPathEndsWithSlash() { return pathEndsWithSlash; } RouteState setPathEndsWithSlash(boolean pathEndsWithSlash) { return new RouteState( this.route, this.metadata, this.path, this.name, this.order, this.enabled, this.methods, this.consumes, this.emptyBodyPermittedWithConsumes, this.produces, this.contextHandlers, this.failureHandlers, this.added, this.pattern, this.groups, this.useNormalizedPath, this.namedGroupsInRegex, this.virtualHostPattern, pathEndsWithSlash, this.exclusive, this.exactPath); } public boolean isExclusive() { return exclusive; } RouteState setExclusive(boolean exclusive) { return new RouteState( this.route, this.metadata, this.path, this.name, this.order, this.enabled, this.methods, this.consumes, this.emptyBodyPermittedWithConsumes, this.produces, this.contextHandlers, this.failureHandlers, this.added, this.pattern, this.groups, this.useNormalizedPath, this.namedGroupsInRegex, this.virtualHostPattern, this.pathEndsWithSlash, exclusive, this.exactPath); } public boolean isExactPath() { return exactPath; } RouteState setExactPath(boolean exactPath) { return new RouteState( this.route, this.metadata, this.path, this.name, this.order, this.enabled, this.methods, this.consumes, this.emptyBodyPermittedWithConsumes, this.produces, this.contextHandlers, this.failureHandlers, this.added, this.pattern, this.groups, this.useNormalizedPath, this.namedGroupsInRegex, this.virtualHostPattern, this.pathEndsWithSlash, this.exclusive, exactPath); } RouteState setName(String name) { return new RouteState( this.route, this.metadata, this.path, name, this.order, this.enabled, this.methods, this.consumes, this.emptyBodyPermittedWithConsumes, this.produces, this.contextHandlers, this.failureHandlers, this.added, this.pattern, this.groups, this.useNormalizedPath, this.namedGroupsInRegex, this.virtualHostPattern, this.pathEndsWithSlash, this.exclusive, this.exactPath); } private boolean containsMethod(HttpServerRequest request) { if (!isEmpty(methods)) { return methods.contains(request.method()); } return false; } private static boolean isEmpty(Collection collection) { return collection == null || collection.isEmpty(); } /** * @return 0 if route matches, otherwise it return the status code */ public int matches(RoutingContextImplBase context, String mountPoint, boolean failure) { if (failure && !hasNextFailureHandler(context) || !failure && !hasNextContextHandler(context)) { return 404; } if (!enabled) { return 404; } HttpServerRequest request = context.request(); if (path != null && pattern == null && !pathMatches(mountPoint, context)) { return 404; } if (pattern != null) { // need to reset "rest" context.pathParams() .remove("*"); String path = useNormalizedPath ? context.normalizedPath() : context.request().path(); if (mountPoint != null) { int strip = mountPoint.length(); // mount point can have significant slash if (mountPoint.charAt(strip - 1)== '/') { strip--; } if (path != null) { path = path.substring(strip); } } Matcher m; if (path != null && (m = pattern.matcher(path)).matches()) { if (!isEmpty(methods) && !containsMethod(request)) { // If I'm here path or path pattern matches, but the method is wrong return 405; } context.matchRest = -1; context.normalizedMatch = useNormalizedPath; if (m.groupCount() > 0) { if (!exactPath) { context.matchRest = m.start("rest"); // always replace context.pathParams() .put("*", path.substring(context.matchRest)); } if (!isEmpty(groups)) { // Pattern - named params // decode the path as it could contain escaped chars. final int len = Math.min(groups.size(), m.groupCount()); for (int i = 0; i < len; i++) { final String k = groups.get(i); String undecodedValue; // We try to take value in three ways: // 1. group name of type p0, p1, pN (most frequent and used by vertx params) // 2. group name inside the regex // 3. No group name try { undecodedValue = m.group("p" + i); } catch (IllegalArgumentException e) { try { undecodedValue = m.group(k); } catch (IllegalArgumentException e1) { // Groups starts from 1 (0 group is total match) undecodedValue = m.group(i + 1); } } if (undecodedValue != null) { addPathParam(context, k, undecodedValue); } } } else { // Straight regex - un-named params // decode the path as it could contain escaped chars. if (!isEmpty(namedGroupsInRegex)) { for (String namedGroup : namedGroupsInRegex) { String namedGroupValue = m.group(namedGroup); if (namedGroupValue != null) { addPathParam(context, namedGroup, namedGroupValue); } } } for (int i = 0; i < m.groupCount(); i++) { String group = m.group(i + 1); if (group != null) { final String k = "param" + i; addPathParam(context, k, group); } } } } } else { return 404; } } else { // no pattern check for wrong method if (!isEmpty(methods) && !containsMethod(request)) { // If I'm here path or path pattern matches, but the method is wrong return 405; } } if (!isEmpty(consumes)) { // Can this route consume the specified content type MIMEHeader contentType = context.parsedHeaders().contentType(); MIMEHeader consumal = contentType.findMatchedBy(consumes); if (consumal == null && !(contentType.rawValue().isEmpty() && emptyBodyPermittedWithConsumes)) { if (contentType.rawValue().isEmpty()) { return 400; } else { return 415; } } } if (!isEmpty(produces)) { List acceptableTypes = context.parsedHeaders().accept(); if(!acceptableTypes.isEmpty()) { MIMEHeader selectedAccept = context.parsedHeaders().findBestUserAcceptedIn(acceptableTypes, produces); if (selectedAccept != null) { context.setAcceptableContentType(selectedAccept.rawValue()); } else { return 406; } } } if (!virtualHostMatches(context.request())) { return 404; } return 0; } private boolean pathMatches(String mountPoint, RoutingContext ctx) { final boolean rootRouter = mountPoint == null; final boolean pathEndsWithSlash; final String thePath; if (rootRouter) { thePath = path; pathEndsWithSlash = this.pathEndsWithSlash; } else { boolean mountPointEndsWithSlash = mountPoint.charAt(mountPoint.length() - 1) == '/'; // path is "/" if (path.length() == 1) { // mount point is always assumed to be a directory so // we must ignore the final slash thePath = mountPoint; // so this is a special case we can't consider the configured route but the mount point itself pathEndsWithSlash = mountPointEndsWithSlash; } else { // solve the double slash when mount point ends with slash if (mountPointEndsWithSlash) { thePath = mountPoint + path.substring(1); } else { thePath = mountPoint + path; } pathEndsWithSlash = this.pathEndsWithSlash; } } String requestPath; if (useNormalizedPath) { // never null requestPath = ctx.normalizedPath(); } else { requestPath = ctx.request().path(); // can be null if (requestPath == null) { requestPath = "/"; } } if (exactPath) { // exact path has no "rest" ctx.pathParams() .remove("*"); return pathMatchesExact(thePath, requestPath, pathEndsWithSlash); } else { if (pathEndsWithSlash) { // the route expects a path that ends in "/*". This is a special case // we need to optionally allow any request just like if it was a "*" but // treat the slash final int pathLen = thePath.length(); final int reqLen = requestPath.length(); if (reqLen < pathLen - 2) { // we miss at least 2 characters return false; } if (reqLen == pathLen - 1) { // request misses 1 character, there is the chance that this request doesn't include the final slash // because the mount path ended with a wildcard we are relaxed in the check if (thePath.regionMatches(0, requestPath, 0, pathLen - 1)) { // handle the "rest" as path param *, always known to be empty ctx.pathParams() .put("*", "/"); return true; } } } if (requestPath.startsWith(thePath)) { // handle the "rest" as path param * ctx.pathParams() .put("*", URIDecoder.decodeURIComponent(requestPath.substring(thePath.length()), false)); return true; } return false; } } private boolean virtualHostMatches(HttpServerRequest request) { if (virtualHostPattern == null) { return true; } String host = request.host(); if (host == null) { return false; } int len = host.length(); // knowing that the shortest IPv6 is [::] if (len > 3 && host.charAt(0) == '[') { // attempt to parse IPv6 int delim = host.indexOf(']'); if (delim != -1) { // the delim must be the terminal character OR right before a ':' if (delim == len - 1 || host.charAt(delim + 1) == ':') { // OK host = host.substring(1, delim); return virtualHostPattern.matcher(host).matches(); } } } // assume IPv4 or name int portSeparatorIdx = host.lastIndexOf(':'); if (portSeparatorIdx != -1) { host = host.substring(0, portSeparatorIdx); } return virtualHostPattern.matcher(host).matches(); } private static boolean pathMatchesExact(String base, String other, boolean significantSlash) { // Ignore trailing slash when matching paths int len = other.length(); if (significantSlash) { if (other.charAt(len -1) != '/') { // final slash is significant but missing return false; } } else { if (other.charAt(len -1) == '/') { // final slash is not significant, ignore it len--; } } // lengths are not the same (fail) if (base.length() != len) { return false; } // content must match return other.regionMatches(0, base, 0, len); } private void addPathParam(RoutingContext context, String name, String value) { HttpServerRequest request = context.request(); final String decodedValue = URIDecoder.decodeURIComponent(value, false); if (!request.params().contains(name)) { request.params().add(name, decodedValue); } context.pathParams().put(name, decodedValue); } boolean hasNextContextHandler(RoutingContextImplBase context) { return context.currentRouteNextHandlerIndex() < getContextHandlersLength(); } boolean hasNextFailureHandler(RoutingContextImplBase context) { return context.currentRouteNextFailureHandlerIndex() < getFailureHandlersLength(); } void handleContext(RoutingContextImplBase context) { contextHandlers .get(context.currentRouteNextHandlerIndex() - 1) .handle(context); } void handleFailure(RoutingContextImplBase context) { failureHandlers .get(context.currentRouteNextFailureHandlerIndex() - 1) .handle(context); } public String getName() { if (name != null) { return name; } if (path != null) { return path; } if (pattern != null) { return pattern.pattern(); } return null; } @Override public String toString() { return "RouteState{" + "metadata=" + metadata + ", path='" + path + '\'' + ", name=" + name + ", order=" + order + ", enabled=" + enabled + ", methods=" + methods + ", consumes=" + consumes + ", emptyBodyPermittedWithConsumes=" + emptyBodyPermittedWithConsumes + ", produces=" + produces + ", contextHandlers=" + contextHandlers + ", failureHandlers=" + failureHandlers + ", added=" + added + ", pattern=" + pattern + ", groups=" + groups + ", useNormalizedPath=" + useNormalizedPath + ", namedGroupsInRegex=" + namedGroupsInRegex + ", virtualHostPattern=" + virtualHostPattern + ", pathEndsWithSlash=" + pathEndsWithSlash + ", exclusive=" + exclusive + ", exactPath=" + exactPath + '}'; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy