/*
* JBoss, Home of Professional Open Source.
* Copyright 2014 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.undertow.server.handlers;
import io.undertow.
Handlers;
import io.undertow.server.
HttpHandler;
import io.undertow.server.
HttpServerExchange;
import io.undertow.server.handlers.cache.
LRUCache;
import io.undertow.util.
PathMatcher;
/**
* Handler that dispatches to a given handler based of a prefix match of the path.
* <p>
* This only matches a single level of a request, e.g if you have a request that takes the form:
* <p>
* /foo/bar
* <p>
*
* @author Stuart Douglas
*/
public class
PathHandler implements
HttpHandler {
private final
PathMatcher<
HttpHandler>
pathMatcher = new
PathMatcher<>();
private final
LRUCache<
String,
PathMatcher.
PathMatch<
HttpHandler>>
cache;
public
PathHandler(final
HttpHandler defaultHandler) {
this(0);
pathMatcher.
addPrefixPath("/",
defaultHandler);
}
public
PathHandler(final
HttpHandler defaultHandler, int
cacheSize) {
this(
cacheSize);
pathMatcher.
addPrefixPath("/",
defaultHandler);
}
public
PathHandler() {
this(0);
}
public
PathHandler(int
cacheSize) {
if(
cacheSize > 0) {
cache = new
LRUCache<>(
cacheSize, -1, true);
} else {
cache = null;
}
}
@
Override
public void
handleRequest(
HttpServerExchange exchange) throws
Exception {
PathMatcher.
PathMatch<
HttpHandler>
match = null;
boolean
hit = false;
if(
cache != null) {
match =
cache.
get(
exchange.
getRelativePath());
hit = true;
}
if(
match == null) {
match =
pathMatcher.
match(
exchange.
getRelativePath());
}
if (
match.
getValue() == null) {
ResponseCodeHandler.
HANDLE_404.
handleRequest(
exchange);
return;
}
if(
hit) {
cache.
add(
exchange.
getRelativePath(),
match);
}
exchange.
setRelativePath(
match.
getRemaining());
if(
exchange.
getResolvedPath().
isEmpty()) {
//first path handler, we can just use the matched part
exchange.
setResolvedPath(
match.
getMatched());
} else {
//already something in the resolved path
StringBuilder sb = new
StringBuilder(
exchange.
getResolvedPath().
length() +
match.
getMatched().
length());
sb.
append(
exchange.
getResolvedPath());
sb.
append(
match.
getMatched());
exchange.
setResolvedPath(
sb.
toString());
}
match.
getValue().
handleRequest(
exchange);
}
/**
* Adds a path prefix and a handler for that path. If the path does not start
* with a / then one will be prepended.
* <p>
* The match is done on a prefix bases, so registering /foo will also match /bar. Exact
* path matches are taken into account first.
* <p>
* If / is specified as the path then it will replace the default handler.
*
* @param path The path
* @param handler The handler
* @see #addPrefixPath(String, io.undertow.server.HttpHandler)
* @deprecated Superseded by {@link #addPrefixPath(String, io.undertow.server.HttpHandler)}.
*/
@
Deprecated
public synchronized
PathHandler addPath(final
String path, final
HttpHandler handler) {
return
addPrefixPath(
path,
handler);
}
/**
* Adds a path prefix and a handler for that path. If the path does not start
* with a / then one will be prepended.
* <p>
* The match is done on a prefix bases, so registering /foo will also match /foo/bar.
* Though exact path matches are taken into account before prefix path matches. So
* if an exact path match exists it's handler will be triggered.
* <p>
* If / is specified as the path then it will replace the default handler.
*
* @param path If the request contains this prefix, run handler.
* @param handler The handler which is activated upon match.
* @return The resulting PathHandler after this path has been added to it.
*/
public synchronized
PathHandler addPrefixPath(final
String path, final
HttpHandler handler) {
Handlers.
handlerNotNull(
handler);
pathMatcher.
addPrefixPath(
path,
handler);
return this;
}
/**
* If the request path is exactly equal to the given path, run the handler.
* <p>
* Exact paths are prioritized higher than prefix paths.
*
* @param path If the request path is exactly this, run handler.
* @param handler Handler run upon exact path match.
* @return The resulting PathHandler after this path has been added to it.
*/
public synchronized
PathHandler addExactPath(final
String path, final
HttpHandler handler) {
Handlers.
handlerNotNull(
handler);
pathMatcher.
addExactPath(
path,
handler);
return this;
}
@
Deprecated
public synchronized
PathHandler removePath(final
String path) {
return
removePrefixPath(
path);
}
public synchronized
PathHandler removePrefixPath(final
String path) {
pathMatcher.
removePrefixPath(
path);
return this;
}
public synchronized
PathHandler removeExactPath(final
String path) {
pathMatcher.
removeExactPath(
path);
return this;
}
public synchronized
PathHandler clearPaths() {
pathMatcher.
clearPaths();
return this;
}
}