org.eclipse.jetty.server.Handler Maven / Gradle / Ivy
Show all versions of jetty-server Show documentation
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.server;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;
import org.eclipse.jetty.server.handler.ErrorProcessor;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.component.Destroyable;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.thread.Invocable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A Jetty component that handles HTTP requests, of any version (HTTP/1.1, HTTP/2 or HTTP/3).
* {@code Handler}s are organized in a tree structure.
* An incoming HTTP request is first delivered to the {@link Server} instance
* (itself the root {@code Handler}), which forwards it to one or more children {@code Handler}s,
* which may recursively forward it to their children {@code Handler}s, until one of them
* returns a non-null {@link Request.Processor}.
* Returning a non-null {@code Request.Processor} indicates that the {@code Handler}
* will process the HTTP request, and subsequent sibling or children {@code Handler}s
* are not invoked.
* If none of the {@code Handler}s returns a {@code Request.Processor}, a default HTTP 404
* response is generated.
* {@code Handler}s may wrap the {@link Request} and then forward the wrapped instance
* to their children, so that they see modified HTTP headers or a modified HTTP URI,
* or to intercept the read of the request content.
* Similarly, {@code Handler}s may wrap the {@link Request.Processor} returned by one
* of the descendants.
* A minimal tree structure could be:
*
* Server
* `- YourCustomHandler
*
* A more sophisticated tree structure:
*
* Server
* `- DelayedHandler.UntilContent
* `- GzipHandler
* `- ContextHandlerCollection
* +- ContextHandler (contextPath="/user")
* | `- YourUserHandler
* |- ContextHandler (contextPath="/admin")
* | `- YourAdminHandler
* `- DefaultHandler
*
* A simple {@code Handler} implementation could be:
* {@code
* class SimpleHandler extends Handler.Processor
* {
* @Override
* public void process(Request request, Response response, Callback callback)
* {
* // Mark the processing as completed.
* // Implicitly sends a 200 OK response with no content.
* callback.succeeded();
* }
* }
* }
* A more sophisticated example of a {@code Handler} that decides whether to handle
* requests based on their URI path:
* {@code
* class YourHelloHandler extends Handler.Abstract
* {
* @Override
* public Processor handle(Request request)
* {
* if (request.getHttpURI().getPath().startsWith("/yourPath")
* {
* // The request is for this Handler, process it.
* return this::process;
* }
* else
* {
* // The request is not for this Handler.
* return null;
* }
* }
*
* private void process(Request request, Response response, Callback callback)
* {
* response.setStatus(200);
* // The callback is completed when the write is completed.
* response.write(true, callback, "hello");
* }
* }
* }
*
* @see Request.Processor
*/
@ManagedObject("Handler")
public interface Handler extends LifeCycle, Destroyable, Invocable
{
/**
* Invoked to decide whether to handle the given HTTP request.
* If the HTTP request can be handled by this {@code Handler},
* this method must return a non-null {@link Request.Processor}.
* Otherwise, the HTTP request is not handled by this {@code Handler}
* (for example, the HTTP request's URI does not match those handled
* by this {@code Handler}), and this method must return {@code null}.
* This method may inspect the HTTP request with the following rules:
*
* - it may access read-only fields such as the HTTP headers, or the HTTP URI, etc.
* - it may wrap the {@link Request} in a {@link Request.Wrapper}, for example
* to modify HTTP headers or modify the HTTP URI, etc.
* - it may directly modify {@link Request#getAttribute(String) request attributes}
* - it may directly add/remove request listeners supported in the {@link Request} APIs
* - it must not read the request content (otherwise an {@link IllegalStateException}
* will be thrown)
*
* Exceptions thrown by this method are processed by an {@link ErrorProcessor},
* if present, otherwise a default HTTP 500 error is generated.
*
* @param request the incoming HTTP request to analyze
* @return a non-null {@link Request.Processor} that processes the request/response,
* or null if this {@code Handler} does not handle the request
* @throws Exception Thrown if there is a problem handling.
*/
Request.Processor handle(Request request) throws Exception;
/**
* @return the {@code Server} associated with this {@code Handler}
*/
@ManagedAttribute(value = "the Server instance associated to this Handler", readonly = true)
Server getServer();
/**
* @param server the {@code Server} to associate to this {@code Handler}
*/
void setServer(Server server);
@Override
void destroy();
/**
* A {@code Handler} that contains one or more other {@code Handler}s.
*/
interface Container extends Handler
{
void addHandler(Handler handler);
default void addHandler(Supplier supplier)
{
addHandler(supplier.get());
}
/**
* @return an immutable collection of {@code Handler}s directly contained by this {@code Handler}.
*/
@ManagedAttribute("The direct children Handlers of this container")
List getHandlers();
/**
* @return an immutable collection of {@code Handler}s descendants of this {@code Handler}.
*/
default List getDescendants()
{
return getDescendants(Handler.class);
}
/**
* Get a list of descendants of the passed type.
* The default implementation is not memory efficient and should be overridden.
* @param type the type of {@code Handler}
* @param the type of {@code Handler}
* @return an immutable collection of {@code Handler}s of the given type, descendants of this {@code Handler}
*/
default List getDescendants(Class type)
{
List handlers = new ArrayList<>();
for (Handler h : getHandlers())
{
if (type.isInstance(h))
{
@SuppressWarnings("unchecked")
T t = (T)h;
handlers.add(t);
}
if (h instanceof Container c)
handlers.addAll(c.getDescendants(type));
}
return handlers;
}
/**
* @param type the type of {@code Handler}
* @param the type of {@code Handler}
* @return the first {@code Handler} of the given type, descendants of this {@code Handler},
* or null if no such {@code Handler} exist
*/
default T getDescendant(Class type)
{
for (Handler h : getHandlers())
{
if (type.isInstance(h))
{
@SuppressWarnings("unchecked")
T t = (T)h;
return t;
}
if (h instanceof Container c)
{
T t = c.getDescendant(type);
if (t != null)
return t;
}
}
return null;
}
/**
* @param handler the child {@code Handler}
* @param type the type of {@code Handler}
* @param the type of {@code Handler}
* @return the {@code Handler.Container} of the given type, parent of the given {@code Handler}
*/
default T getContainer(Handler handler, Class type)
{
if (handler == null)
return null;
for (T container : getDescendants(type))
{
if (container.getDescendants(handler.getClass()).contains(handler))
return container;
}
return null;
}
}
/**
* A {@link Handler.Container} that wraps a single other {@code Handler}.
* @see Handler.Wrapper for an implementation of nested.
*/
interface Nested extends Container
{
Handler getHandler();
/**
* Set the nested handler.
* Implementations should check for loops, set the server and update any {@link ContainerLifeCycle} beans, all
* of which can be done by using the utility method {@link #updateHandler(Nested, Handler)}
* @param handler The handler to set.
*/
void setHandler(Handler handler);
/**
* Set the nested handler from a supplier. This allows for Handler type conversion.
* @param supplier A supplier of a Handler.
*/
default void setHandler(Supplier supplier)
{
setHandler(supplier.get());
}
@Override
default List getHandlers()
{
Handler h = getHandler();
if (h == null)
return Collections.emptyList();
return Collections.singletonList(h);
}
@Override
default void addHandler(Handler handler)
{
Handler existing = getHandler();
setHandler(handler);
if (existing != null && handler instanceof Container container)
container.addHandler(existing);
}
default void insertHandler(Handler.Nested handler)
{
Handler.Nested tail = handler;
while (tail.getHandler() instanceof Handler.Wrapper)
{
tail = (Handler.Wrapper)tail.getHandler();
}
if (tail.getHandler() != null)
throw new IllegalArgumentException("bad tail of inserted wrapper chain");
tail.setHandler(getHandler());
setHandler(handler);
}
/**
* Utility method to:
* - Check the server state and invocation type
* - Check for handler loops
* - Set the server on the handler
* - Update the beans on if the Nests is a {@link ContainerLifeCycle}
*
* @param nested The Nested implementation to update
* @param handler The handle to set
* @return The set handler.
*/
static Handler updateHandler(Nested nested, Handler handler)
{
// check state
Server server = nested.getServer();
if (server != null && server.isStarted() && handler != null &&
server.getInvocationType() != Invocable.combine(server.getInvocationType(), handler.getInvocationType()))
throw new IllegalArgumentException("Cannot change invocation type of started server");
// Check for loops.
if (handler == nested || (handler instanceof Handler.Container container &&
container.getDescendants().contains(nested)))
throw new IllegalStateException("setHandler loop");
if (handler != null && server != null)
handler.setServer(server);
if (nested instanceof org.eclipse.jetty.util.component.ContainerLifeCycle container)
container.updateBean(nested.getHandler(), handler);
return handler;
}
}
/**
* An abstract implementation of {@link Handler} that is a {@link ContainerLifeCycle}.
*/
abstract class Abstract extends ContainerLifeCycle implements Handler
{
private static final Logger LOG = LoggerFactory.getLogger(Abstract.class);
private final InvocationType _invocationType;
private Server _server;
public Abstract()
{
this(InvocationType.BLOCKING);
}
public Abstract(InvocationType type)
{
_invocationType = type;
}
@Override
public Server getServer()
{
return _server;
}
@Override
public void setServer(Server server)
{
if (_server == server)
return;
if (isStarted())
throw new IllegalStateException(getState());
_server = server;
}
@Override
public InvocationType getInvocationType()
{
return _invocationType;
}
@Override
protected void doStart() throws Exception
{
if (LOG.isDebugEnabled())
LOG.debug("starting {}", this);
if (_server == null)
LOG.warn("No Server set for {}", this);
super.doStart();
}
@Override
protected void doStop() throws Exception
{
if (LOG.isDebugEnabled())
LOG.debug("stopping {}", this);
super.doStop();
}
@Override
public void destroy()
{
if (isRunning())
throw new IllegalStateException(getState());
super.destroy();
}
}
/**
* A {@link Handler.Abstract} that implements {@link Handler.Container}.
*/
abstract class AbstractContainer extends Abstract implements Container
{
@Override
public List getDescendants(Class type)
{
List list = new ArrayList<>();
expandHandler(this, list, type);
return list;
}
@SuppressWarnings("unchecked")
private void expandHandler(Handler handler, List list, Class type)
{
if (!(handler instanceof Container container))
return;
for (Handler child : container.getHandlers())
{
if (type == null || type.isInstance(child))
list.add((H)child);
expandHandler(child, list, type);
}
}
@Override
public T getDescendant(Class type)
{
return findHandler(this, type);
}
@SuppressWarnings("unchecked")
private H findHandler(Handler handler, Class type)
{
if (!(handler instanceof Container container))
return null;
for (Handler child : container.getHandlers())
{
if (type == null || type.isInstance(child))
return (H)child;
H descendant = findHandler(child, type);
if (descendant != null)
return descendant;
}
return null;
}
@Override
public void setServer(Server server)
{
super.setServer(server);
for (Handler child : getHandlers())
{
child.setServer(server);
}
}
@Override
public InvocationType getInvocationType()
{
InvocationType invocationType = InvocationType.NON_BLOCKING;
for (Handler child : getHandlers())
invocationType = Invocable.combine(invocationType, child.getInvocationType());
return invocationType;
}
@SuppressWarnings("unchecked")
public static T findContainerOf(Handler.Container root, Class type, Handler handler)
{
if (root == null || handler == null)
return null;
List branches = (List)root.getDescendants(type);
if (branches != null)
{
for (Handler.Container container : branches)
{
List candidates = (List)container.getDescendants(handler.getClass());
if (candidates != null)
{
for (Handler c : candidates)
{
if (c == handler)
return (T)container;
}
}
}
}
return null;
}
}
/**
* An implementation of {@link Nested}, which is a {@link Handler.Container} that wraps a single other {@link Handler}.
*/
class Wrapper extends AbstractContainer implements Nested
{
private Handler _handler;
public Wrapper()
{
this(null);
}
public Wrapper(Handler handler)
{
_handler = handler == null ? null : Nested.updateHandler(this, handler);
}
public Handler getHandler()
{
return _handler;
}
public void setHandler(Handler handler)
{
_handler = Nested.updateHandler(this, handler);
}
@Override
public List getHandlers()
{
Handler next = getHandler();
return (next == null) ? Collections.emptyList() : Collections.singletonList(next);
}
@Override
public Request.Processor handle(Request request) throws Exception
{
Handler next = getHandler();
return next == null ? null : next.handle(request);
}
@Override
public InvocationType getInvocationType()
{
Handler next = getHandler();
return next == null ? InvocationType.NON_BLOCKING : next.getInvocationType();
}
}
/**
* A {@link Handler.Container} that contains a list of other {@code Handler}s.
*
* TODO this should be called List instead
*/
class Collection extends AbstractContainer
{
private volatile List _handlers = new ArrayList<>();
public Collection(Handler... handlers)
{
this(List.of(handlers));
}
public Collection(List handlers)
{
setHandlers(handlers);
}
@Override
public String toString()
{
return super.toString();
}
@Override
public Request.Processor handle(Request request) throws Exception
{
for (Handler h : _handlers)
{
Request.Processor processor = h.handle(request);
if (processor != null)
return processor;
}
return null;
}
@Override
public List getHandlers()
{
return _handlers;
}
public void setHandlers(Handler... handlers)
{
setHandlers(handlers.length == 0 ? null : List.of(handlers));
}
public void setHandlers(List handlers)
{
List newHandlers = newHandlers(handlers);
Server server = getServer();
InvocationType invocationType = server == null ? null : server.getInvocationType();
// Check for loops && InvocationType changes.
for (Handler handler : newHandlers)
{
if (handler == null)
continue;
if (handler == this || (handler instanceof Handler.Container container &&
container.getDescendants().contains(this)))
throw new IllegalStateException("setHandler loop");
invocationType = Invocable.combine(invocationType, handler.getInvocationType());
if (server != null && server.isStarted() &&
server.getInvocationType() != Invocable.combine(server.getInvocationType(), handler.getInvocationType()))
throw new IllegalArgumentException("Cannot change invocation type of started server");
if (server != null)
handler.setServer(server);
}
updateBeans(_handlers, handlers);
_handlers = newHandlers;
}
protected List newHandlers(List handlers)
{
return handlers == null ? List.of() : List.copyOf(handlers);
}
public void addHandler(Handler handler)
{
List list = new ArrayList<>(getHandlers());
list.add(handler);
setHandlers(list);
}
public void removeHandler(Handler handler)
{
List list = new ArrayList<>(getHandlers());
if (list.remove(handler))
setHandlers(list);
}
}
/**
* A {@link Handler} that itself implements {@link Request.Processor}
* and that returns itself from a call to {@link Handler#handle(Request)}.
* Subclasses only need to implement
* {@link #process(Request, Response, Callback)}.
*/
abstract class Processor extends Abstract implements Request.Processor
{
public Processor()
{
super();
}
public Processor(InvocationType type)
{
super(type);
}
@Override
public Request.Processor handle(Request request) throws Exception
{
return this;
}
public abstract static class Blocking extends Processor
{
public Blocking()
{
super(InvocationType.BLOCKING);
}
}
public abstract static class NonBlocking extends Processor
{
public NonBlocking()
{
super(InvocationType.NON_BLOCKING);
}
}
}
}