/*
* 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;
import io.undertow.attribute.
ExchangeAttribute;
import io.undertow.predicate.
Predicate;
import io.undertow.predicate.
PredicateParser;
import io.undertow.predicate.
PredicatesHandler;
import io.undertow.server.
HttpHandler;
import io.undertow.server.
JvmRouteHandler;
import io.undertow.server.
RoutingHandler;
import io.undertow.server.handlers.
AccessControlListHandler;
import io.undertow.server.handlers.
LearningPushHandler;
import io.undertow.server.handlers.
DateHandler;
import io.undertow.server.handlers.
DisableCacheHandler;
import io.undertow.server.handlers.
ExceptionHandler;
import io.undertow.server.handlers.
GracefulShutdownHandler;
import io.undertow.server.handlers.
HttpContinueAcceptingHandler;
import io.undertow.server.handlers.
HttpContinueReadHandler;
import io.undertow.server.handlers.
HttpTraceHandler;
import io.undertow.server.handlers.
IPAddressAccessControlHandler;
import io.undertow.server.handlers.
NameVirtualHostHandler;
import io.undertow.server.handlers.
PathHandler;
import io.undertow.server.handlers.
PathTemplateHandler;
import io.undertow.server.handlers.
PredicateContextHandler;
import io.undertow.server.handlers.
PredicateHandler;
import io.undertow.server.handlers.
ProxyPeerAddressHandler;
import io.undertow.server.handlers.
RedirectHandler;
import io.undertow.server.handlers.
RequestDumpingHandler;
import io.undertow.server.handlers.
RequestLimit;
import io.undertow.server.handlers.
RequestLimitingHandler;
import io.undertow.server.handlers.
ResponseRateLimitingHandler;
import io.undertow.server.handlers.
SetAttributeHandler;
import io.undertow.server.handlers.
SetHeaderHandler;
import io.undertow.server.handlers.
URLDecodingHandler;
import io.undertow.server.handlers.builder.
PredicatedHandler;
import io.undertow.server.handlers.proxy.
ProxyClient;
import io.undertow.server.handlers.proxy.
ProxyHandler;
import io.undertow.server.handlers.resource.
ResourceHandler;
import io.undertow.server.handlers.resource.
ResourceManager;
import io.undertow.server.handlers.sse.
ServerSentEventConnectionCallback;
import io.undertow.server.handlers.sse.
ServerSentEventHandler;
import io.undertow.websockets.
WebSocketConnectionCallback;
import io.undertow.websockets.
WebSocketProtocolHandshakeHandler;
import java.util.
List;
import java.util.concurrent.
TimeUnit;
/**
* Utility class with convenience methods for dealing with handlers
*
* @author Stuart Douglas
*/
public class
Handlers {
/**
* Creates a new path handler, with the default handler specified
*
* @param defaultHandler The default handler
* @return A new path handler
*/
public static
PathHandler path(final
HttpHandler defaultHandler) {
return new
PathHandler(
defaultHandler);
}
/**
* Creates a new path handler
*
* @return A new path handler
*/
public static
PathHandler path() {
return new
PathHandler();
}
/**
*
* @return a new path template handler
*/
public static
PathTemplateHandler pathTemplate() {
return new
PathTemplateHandler();
}
/**
*
* @param rewriteQueryParams If the query params should be rewritten
* @return The routing handler
*/
public static
RoutingHandler routing(boolean
rewriteQueryParams) {
return new
RoutingHandler(
rewriteQueryParams);
}
/**
*
* @return a new routing handler
*/
public static
RoutingHandler routing() {
return new
RoutingHandler();
}
/**
*
* @param rewriteQueryParams If the query params should be rewritten
* @return The path template handler
*/
public static
PathTemplateHandler pathTemplate(boolean
rewriteQueryParams) {
return new
PathTemplateHandler(
rewriteQueryParams);
}
/**
* Creates a new virtual host handler
*
* @return A new virtual host handler
*/
public static
NameVirtualHostHandler virtualHost() {
return new
NameVirtualHostHandler();
}
/**
* Creates a new virtual host handler using the provided default handler
*
* @return A new virtual host handler
*/
public static
NameVirtualHostHandler virtualHost(final
HttpHandler defaultHandler) {
return new
NameVirtualHostHandler().
setDefaultHandler(
defaultHandler);
}
/**
* Creates a new virtual host handler that uses the provided handler as the root handler for the given hostnames.
*
* @param hostHandler The host handler
* @param hostnames The host names
* @return A new virtual host handler
*/
public static
NameVirtualHostHandler virtualHost(final
HttpHandler hostHandler,
String...
hostnames) {
NameVirtualHostHandler handler = new
NameVirtualHostHandler();
for (
String host :
hostnames) {
handler.
addHost(
host,
hostHandler);
}
return
handler;
}
/**
* Creates a new virtual host handler that uses the provided handler as the root handler for the given hostnames.
*
* @param defaultHandler The default handler
* @param hostHandler The host handler
* @param hostnames The host names
* @return A new virtual host handler
*/
public static
NameVirtualHostHandler virtualHost(final
HttpHandler defaultHandler, final
HttpHandler hostHandler,
String...
hostnames) {
return
virtualHost(
hostHandler,
hostnames).
setDefaultHandler(
defaultHandler);
}
/**
* @param sessionHandler The web socket session handler
* @return The web socket handler
*/
public static
WebSocketProtocolHandshakeHandler websocket(final
WebSocketConnectionCallback sessionHandler) {
return new
WebSocketProtocolHandshakeHandler(
sessionHandler);
}
/**
* @param sessionHandler The web socket session handler
* @param next The handler to invoke if the web socket connection fails
* @return The web socket handler
*/
public static
WebSocketProtocolHandshakeHandler websocket(final
WebSocketConnectionCallback sessionHandler, final
HttpHandler next) {
return new
WebSocketProtocolHandshakeHandler(
sessionHandler,
next);
}
/**
* A handler for server sent events
*
*
* @param callback The server sent events callback
* @return A new server sent events handler
*/
public static
ServerSentEventHandler serverSentEvents(
ServerSentEventConnectionCallback callback) {
return new
ServerSentEventHandler(
callback);
}
/**
* A handler for server sent events
*
* @return A new server sent events handler
*/
public static
ServerSentEventHandler serverSentEvents() {
return new
ServerSentEventHandler();
}
/**
* Return a new resource handler
*
* @param resourceManager The resource manager to use
* @return A new resource handler
*/
public static
ResourceHandler resource(final
ResourceManager resourceManager) {
return new
ResourceHandler(
resourceManager).
setDirectoryListingEnabled(false);
}
/**
* Returns a new redirect handler
*
* @param location The redirect location
* @return A new redirect handler
*/
public static
RedirectHandler redirect(final
String location) {
return new
RedirectHandler(
location);
}
/**
* Returns a new HTTP trace handler. This handler will handle HTTP TRACE
* requests as per the RFC.
* <p>
* WARNING: enabling trace requests may leak information, in general it is recommended that
* these be disabled for security reasons.
*
* @param next The next handler in the chain
* @return A HTTP trace handler
*/
public static
HttpTraceHandler trace(final
HttpHandler next) {
return new
HttpTraceHandler(
next);
}
/**
* Returns a new HTTP handler that sets the Date: header.
*
* This is no longer necessary, as it is handled by the connectors directly.
*
* @param next The next handler in the chain
* @return A new date handler
*/
@
Deprecated
public static
DateHandler date(final
HttpHandler next) {
return new
DateHandler(
next);
}
/**
* Returns a new predicate handler, that will delegate to one of the two provided handlers based on the value of the
* provided predicate.
*
* @param predicate The predicate
* @param trueHandler The handler that will be executed if the predicate is true
* @param falseHandler The handler that will be exected if the predicate is false
* @return A new predicate handler
* @see Predicate
* @see io.undertow.predicate.Predicates
*/
public static
PredicateHandler predicate(final
Predicate predicate, final
HttpHandler trueHandler, final
HttpHandler falseHandler) {
return new
PredicateHandler(
predicate,
trueHandler,
falseHandler);
}
/**
* @param next The next handler
* @return a handler that sets up a new predicate context
*/
public static
HttpHandler predicateContext(
HttpHandler next) {
return new
PredicateContextHandler(
next);
}
public static
PredicatesHandler predicates(final
List<
PredicatedHandler>
handlers,
HttpHandler next) {
final
PredicatesHandler predicatesHandler = new
PredicatesHandler(
next);
for(
PredicatedHandler handler :
handlers) {
predicatesHandler.
addPredicatedHandler(
handler);
}
return
predicatesHandler;
}
/**
* Returns a handler that sets a response header
*
* @param next The next handler in the chain
* @param headerName The name of the header
* @param headerValue The header value
* @return A new set header handler
*/
public static
SetHeaderHandler header(final
HttpHandler next, final
String headerName, final
String headerValue) {
return new
SetHeaderHandler(
next,
headerName,
headerValue);
}
/**
* Returns a handler that sets a response header
*
* @param next The next handler in the chain
* @param headerName The name of the header
* @param headerValue The header value
* @return A new set header handler
*/
public static
SetHeaderHandler header(final
HttpHandler next, final
String headerName, final
ExchangeAttribute headerValue) {
return new
SetHeaderHandler(
next,
headerName,
headerValue);
}
/**
* Returns a new handler that can allow or deny access to a resource based on IP address
*
* @param next The next handler in the chain
* @param defaultAllow Determine if a non-matching address will be allowed by default
* @return A new IP access control handler
*/
public static final
IPAddressAccessControlHandler ipAccessControl(final
HttpHandler next, boolean
defaultAllow) {
return new
IPAddressAccessControlHandler(
next).
setDefaultAllow(
defaultAllow);
}
/**
* Returns a new handler that can allow or deny access to a resource based an at attribute of the exchange
*
* @param next The next handler in the chain
* @param defaultAllow Determine if a non-matching user agent will be allowed by default
* @return A new user agent access control handler
*/
public static final
AccessControlListHandler acl(final
HttpHandler next, boolean
defaultAllow,
ExchangeAttribute attribute) {
return new
AccessControlListHandler(
next,
attribute).
setDefaultAllow(
defaultAllow);
}
/**
* A handler that automatically handles HTTP 100-continue responses, by sending a continue
* response when the first attempt is made to read from the request channel.
*
* @param next The next handler in the chain
* @return A new continue handler
*/
public static final
HttpContinueReadHandler httpContinueRead(final
HttpHandler next) {
return new
HttpContinueReadHandler(
next);
}
/**
* Returns a handler that sends back a HTTP 100 continue response if the given predicate resolves to true.
*
* This handler differs from the one returned by {@link #httpContinueRead(io.undertow.server.HttpHandler)} in
* that it will eagerly send the response, and not wait for the first read attempt.
*
* @param next The next handler
* @param accept The predicate used to determine if the request should be accepted
* @return The accepting handler
*/
public static final
HttpContinueAcceptingHandler httpContinueAccepting(final
HttpHandler next, final
Predicate accept) {
return new
HttpContinueAcceptingHandler(
next,
accept);
}
/**
* Returns a handler that sends back a HTTP 100 continue response to all requests.
*
* This handler differs from the one returned by {@link #httpContinueRead(io.undertow.server.HttpHandler)} in
* that it will eagerly send the response, and not wait for the first read attempt.
*
* @param next The next handler
* @return The accepting handler
*/
public static final
HttpContinueAcceptingHandler httpContinueAccepting(final
HttpHandler next) {
return new
HttpContinueAcceptingHandler(
next);
}
/**
* A handler that will decode the URL, query parameters and to the specified charset.
* <p>
* If you are using this handler you must set the {@link io.undertow.UndertowOptions#DECODE_URL} parameter to false.
* <p>
* This is not as efficient as using the parsers built in UTF-8 decoder. Unless you need to decode to something other
* than UTF-8 you should rely on the parsers decoding instead.
*
* @param next The next handler in the chain
* @param charset The charset to decode to
* @return a new url decoding handler
*/
public static final
URLDecodingHandler urlDecoding(final
HttpHandler next, final
String charset) {
return new
URLDecodingHandler(
next,
charset);
}
/**
* Returns an attribute setting handler that can be used to set an arbitrary attribute on the exchange.
* This includes functions such as adding and removing headers etc.
*
* @param next The next handler
* @param attribute The attribute to set, specified as a string presentation of an {@link io.undertow.attribute.ExchangeAttribute}
* @param value The value to set, specified an a string representation of an {@link io.undertow.attribute.ExchangeAttribute}
* @param classLoader The class loader to use to parser the exchange attributes
* @return The handler
*/
public static
SetAttributeHandler setAttribute(final
HttpHandler next, final
String attribute, final
String value, final
ClassLoader classLoader) {
return new
SetAttributeHandler(
next,
attribute,
value,
classLoader);
}
/**
* Creates the set of handlers that are required to perform a simple rewrite.
* @param condition The rewrite condition
* @param target The rewrite target if the condition matches
* @param next The next handler
* @return
*/
public static
HttpHandler rewrite(final
String condition, final
String target, final
ClassLoader classLoader, final
HttpHandler next) {
return
predicateContext(
predicate(
PredicateParser.
parse(
condition,
classLoader),
setAttribute(
next, "%R",
target,
classLoader),
next));
}
/**
* Returns a new handler that decodes the URL and query parameters into the specified charset, assuming it
* has not already been done by the connector. For this handler to take effect the parameter
* {@link UndertowOptions#DECODE_URL} must have been set to false.
*
* @param charset The charset to decode
* @param next The next handler
* @return A handler that decodes the URL
*/
public static
HttpHandler urlDecodingHandler(final
String charset, final
HttpHandler next) {
return new
URLDecodingHandler(
next,
charset);
}
/**
* Returns a new handler that can be used to wait for all requests to finish before shutting down the server gracefully.
*
* @param next The next http handler
* @return The graceful shutdown handler
*/
public static
GracefulShutdownHandler gracefulShutdown(
HttpHandler next) {
return new
GracefulShutdownHandler(
next);
}
/**
* Returns a new handler that sets the peer address based on the X-Forwarded-For and
* X-Forwarded-Proto header
* @param next The next http handler
* @return The handler
*/
public static
ProxyPeerAddressHandler proxyPeerAddress(
HttpHandler next) {
return new
ProxyPeerAddressHandler(
next);
}
/**
* Handler that appends the JVM route to the session cookie
* @param sessionCookieName The session cookie name
* @param jvmRoute The JVM route to append
* @param next The next handler
* @return The handler
*/
public static
JvmRouteHandler jvmRoute(final
String sessionCookieName, final
String jvmRoute,
HttpHandler next) {
return new
JvmRouteHandler(
next,
sessionCookieName,
jvmRoute);
}
/**
* Returns a handler that limits the maximum number of requests that can run at a time.
*
* @param maxRequest The maximum number of requests
* @param queueSize The maximum number of queued requests
* @param next The next handler
* @return The handler
*/
public static
RequestLimitingHandler requestLimitingHandler(final int
maxRequest, final int
queueSize,
HttpHandler next) {
return new
RequestLimitingHandler(
maxRequest,
queueSize,
next);
}
/**
* Returns a handler that limits the maximum number of requests that can run at a time.
*
* @param requestLimit The request limit object that can be shared between handlers, to apply the same limits across multiple handlers
* @param next The next handler
* @return The handler
*/
public static
RequestLimitingHandler requestLimitingHandler(final
RequestLimit requestLimit,
HttpHandler next) {
return new
RequestLimitingHandler(
requestLimit,
next);
}
/**
* Returns a handler that can act as a load balancing reverse proxy.
*
* @param proxyClient The proxy client to use to connect to the remote server
* @param maxRequestTime The maximum amount of time a request can be in progress before it is forcibly closed
* @param next The next handler to invoke if the proxy client does not know how to proxy the request
* @return The proxy handler
*/
public static
ProxyHandler proxyHandler(
ProxyClient proxyClient, int
maxRequestTime,
HttpHandler next) {
return
ProxyHandler.
builder().
setProxyClient(
proxyClient).
setNext(
next).
setMaxRequestTime(
maxRequestTime).
build();
}
/**
* Returns a handler that can act as a load balancing reverse proxy.
*
* @param proxyClient The proxy client to use to connect to the remote server
* @param next The next handler to invoke if the proxy client does not know how to proxy the request
* @return The proxy handler
*/
public static
ProxyHandler proxyHandler(
ProxyClient proxyClient,
HttpHandler next) {
return
ProxyHandler.
builder().
setProxyClient(
proxyClient).
setNext(
next).
build();
}
/**
* Returns a handler that can act as a load balancing reverse proxy.
*
* @param proxyClient The proxy client to use to connect to the remote server
* @return The proxy handler
*/
public static
ProxyHandler proxyHandler(
ProxyClient proxyClient) {
return
ProxyHandler.
builder().
setProxyClient(
proxyClient).
build();
}
/**
* Handler that sets the headers that disable caching of the response
* @param next The next handler
* @return The handler
*/
public static
HttpHandler disableCache(final
HttpHandler next) {
return new
DisableCacheHandler(
next);
}
/**
* Returns a handler that dumps requests to the log for debugging purposes.
*
* @param next The next handler
* @return The request dumping handler
*/
public static
HttpHandler requestDump(final
HttpHandler next) {
return new
RequestDumpingHandler(
next);
}
/**
* Returns a handler that maps exceptions to additional handlers
* @param next The next handler
* @return The exception handler
*/
public static
ExceptionHandler exceptionHandler(final
HttpHandler next) {
return new
ExceptionHandler(
next);
}
/**
*
* A handler that limits the download speed to a set number of bytes/period
*
* @param next The next handler
* @param bytes The number of bytes per time period
* @param time The time period
* @param timeUnit The units of the time period
*/
public static
ResponseRateLimitingHandler responseRateLimitingHandler(
HttpHandler next, int
bytes,long
time,
TimeUnit timeUnit) {
return new
ResponseRateLimitingHandler(
next,
bytes,
time,
timeUnit);
}
/**
* Creates a handler that automatically learns which resources to push based on the referer header
*
* @param maxEntries The maximum number of entries to store
* @param maxAge The maximum age of the entries
* @param next The next handler
* @return A caching push handler
*/
public static
LearningPushHandler learningPushHandler(int
maxEntries, int
maxAge,
HttpHandler next) {
return new
LearningPushHandler(
maxEntries,
maxAge,
next);
}
/**
* Creates a handler that automatically learns which resources to push based on the referer header
*
* @param maxEntries The maximum number of entries to store
* @param next The next handler
* @return A caching push handler
*/
public static
LearningPushHandler learningPushHandler(int
maxEntries,
HttpHandler next) {
return new
LearningPushHandler(
maxEntries, -1,
next);
}
private
Handlers() {
}
public static void
handlerNotNull(final
HttpHandler handler) {
if (
handler == null) {
throw
UndertowMessages.
MESSAGES.
handlerCannotBeNull();
}
}
}