org.eclipse.jetty.websocket.server.WebSocketUpgradeHandler Maven / Gradle / Ivy
//
// ========================================================================
// Copyright (c) 1995 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.websocket.server;
import java.util.function.Consumer;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.thread.Invocable;
import org.eclipse.jetty.websocket.api.WebSocketContainer;
import org.eclipse.jetty.websocket.core.WebSocketComponents;
import org.eclipse.jetty.websocket.core.server.WebSocketMappings;
import org.eclipse.jetty.websocket.core.server.WebSocketServerComponents;
/**
* A {@link Handler} that may perform the upgrade from HTTP to WebSocket.
* The upgrade is performed only if the request matches all the requisites
* necessary for the upgrade (which vary upon the HTTP protocol version),
* otherwise the request handling is forwarded to the {@link Handler} child
* of this {@link Handler}.
* {@code WebSocketUpgradeHandler} may be a {@link #getDescendant(Class)
* descendant} of a {@link ContextHandler}, typically as a direct child, but
* possibly also further down the {@link Handler}'s tree, to enable WebSocket
* upgrades for that {@link ContextHandler} only.
* {@code WebSocketUpgradeHandler} may be a {@link #getDescendant(Class)
* descendant} of the {@link Server}, typically as a direct child, but
* possibly also further down the {@link Handler}'s tree, to enable WebSocket
* upgrades for possibly multiple {@link ContextHandler}s.
* Typical usage:
* {@code
* Server server = ...;
*
* ContextHandler context = new ContextHandler("/app");
*
* // Create the WebSocketUpgradeHandler.
* WebSocketUpgradeHandler wsHandler = WebSocketUpgradeHandler.from(server, context, container ->
* {
* // Map upgrade requests to "/app/ws" to an echo WebSocket endpoint.
* container.addMapping("/ws", (upgradeRequest, upgradeResponse, callback) -> new EchoEndPoint());
* });
*
* // Link WebSocketUpgradeHandler as a child of ContextHandler.
* context.setHandler(wsHandler);
*
* server.setHandler(context);
* server.start();
* }
* A {@link WebSocketUpgradeHandler} is associated with a {@link ServerWebSocketContainer}
* that is exported as a request context attribute and can be retrieved in this way:
*
{@code
* public boolean process(Request request)
* {
* // Retrieve the WebSocket container from the context attributes.
* ServerWebSocketContainer container = (ServerWebSocketContainer)request.getContext().getAttribute(WebSocketContainer.class.getName());
* }
* }
*/
public class WebSocketUpgradeHandler extends Handler.Wrapper
{
/**
* Creates a new {@code WebSocketUpgradeHandler}.
* The {@code WebSocketUpgradeHandler} is not yet linked to the given
* {@link ContextHandler}, therefore the caller code must ensure that
* the returned {@code WebSocketUpgradeHandler} is a descendant of the
* given {@link ContextHandler}.
*
* @param server the {@link Server} object used to lookup common WebSocket components
* @param context the {@link ContextHandler} ancestor of the returned {@code WebSocketUpgradeHandler}
* @return a new {@code WebSocketUpgradeHandler}
* @see #from(Server, ContextHandler, Consumer)
*/
public static WebSocketUpgradeHandler from(Server server, ContextHandler context)
{
return from(server, context, null);
}
/**
* Creates a new {@code WebSocketUpgradeHandler}.
* The {@code WebSocketUpgradeHandler} is not yet linked to the given
* {@link ContextHandler}, therefore the caller code must ensure that
* the returned {@code WebSocketUpgradeHandler} is a descendant of the
* given {@link ContextHandler}.
* The configurator parameter is invoked every time this {@code WebSocketUpgradeHandler}
* is started, so that the endpoint configuration (removed during the
* stopping phase) can be re-applied upon restart.
*
* @param server the {@link Server} object used to lookup common WebSocket components
* @param context the {@link ContextHandler} ancestor of the returned {@code WebSocketUpgradeHandler}
* @param configurator a {@link Consumer} that is called to allow the {@link ServerWebSocketContainer} to
* be configured during the starting phase of the {@code WebSocketUpgradeHandler}
* @return a new {@code WebSocketUpgradeHandler}
*/
public static WebSocketUpgradeHandler from(Server server, ContextHandler context, Consumer configurator)
{
WebSocketComponents components = WebSocketServerComponents.ensureWebSocketComponents(server, context);
WebSocketMappings mappings = new WebSocketMappings(components);
ServerWebSocketContainer container = new ServerWebSocketContainer(mappings);
container.addBean(mappings);
WebSocketUpgradeHandler wsHandler = new WebSocketUpgradeHandler(container, configurator);
context.getContext().setAttribute(WebSocketContainer.class.getName(), container);
return wsHandler;
}
/**
* Creates a new {@code WebSocketUpgradeHandler}.
* The {@code WebSocketUpgradeHandler} is not yet linked to the given
* {@link Server}, therefore the caller code must ensure that
* the returned {@code WebSocketUpgradeHandler} is a descendant of the
* given {@link Server}.
*
* @param server the {@link Server} object used to lookup common WebSocket components
* @return a new {@code WebSocketUpgradeHandler}
* @see #from(Server, Consumer)
*/
public static WebSocketUpgradeHandler from(Server server)
{
return from(server, container -> {});
}
/**
* Creates a new {@code WebSocketUpgradeHandler}.
* The {@code WebSocketUpgradeHandler} is not yet linked to the given
* {@link Server}, therefore the caller code must ensure that
* the returned {@code WebSocketUpgradeHandler} is a descendant of the
* given {@link Server}.
* The configurator parameter is invoked every time this {@code WebSocketUpgradeHandler}
* is started, so that the endpoint configuration (removed during the
* stopping phase) can be re-applied upon restart.
*
* @param server the {@link Server} object used to lookup common WebSocket components
* @param configurator a {@link Consumer} that is called to allow the {@link ServerWebSocketContainer} to
* be configured during the starting phase of the {@code WebSocketUpgradeHandler}
* @return a new {@code WebSocketUpgradeHandler}
*/
public static WebSocketUpgradeHandler from(Server server, Consumer configurator)
{
WebSocketComponents components = WebSocketServerComponents.ensureWebSocketComponents(server);
WebSocketMappings mappings = new WebSocketMappings(components);
ServerWebSocketContainer container = new ServerWebSocketContainer(mappings);
WebSocketUpgradeHandler wsHandler = new WebSocketUpgradeHandler(container, configurator);
server.getContext().setAttribute(WebSocketContainer.class.getName(), container);
return wsHandler;
}
private final ServerWebSocketContainer _container;
private final Consumer _configurator;
/**
* Creates a new {@code WebSocketUpgradeHandler} with the given {@link ServerWebSocketContainer}.
*
* @param container the {@link ServerWebSocketContainer} of this {@code WebSocketUpgradeHandler}
*/
public WebSocketUpgradeHandler(ServerWebSocketContainer container)
{
this(container, null);
}
/**
* Creates a new {@code WebSocketUpgradeHandler} with the given {@link ServerWebSocketContainer}
* and the given configurator.
* The configurator is invoked every time this {@code WebSocketUpgradeHandler} is started,
* see {@link #from(Server, ContextHandler, Consumer)}.
*
* @param container the {@link ServerWebSocketContainer} of this {@code WebSocketUpgradeHandler}
* @param configurator a {@link Consumer} that is called to allow the {@link ServerWebSocketContainer} to
* be configured during the starting phase of the {@code WebSocketUpgradeHandler}
*/
public WebSocketUpgradeHandler(ServerWebSocketContainer container, Consumer configurator)
{
_container = container;
_configurator = configurator;
addManaged(container);
}
/**
* Configures the {@link ServerWebSocketContainer} associated with this
* {@code WebSocketUpgradeHandler}.
* This configuration is applied immediately and lost after a restart.
*
* @param configurator the configuration code
* @return this {@code WebSocketUpgradeHandler}
* @deprecated use {@link #from(Server, ContextHandler, Consumer)},
* {@link #from(Server, Consumer)} or {@link #getServerWebSocketContainer()}
*/
@Deprecated
public WebSocketUpgradeHandler configure(Consumer configurator)
{
configurator.accept(_container);
return this;
}
public ServerWebSocketContainer getServerWebSocketContainer()
{
return _container;
}
@Override
protected void doStart() throws Exception
{
if (_configurator != null)
_configurator.accept(_container);
super.doStart();
}
@Override
public boolean handle(Request request, Response response, Callback callback) throws Exception
{
if (handle(_container, request, response, callback))
return true;
return super.handle(request, response, callback);
}
protected boolean handle(ServerWebSocketContainer container, Request request, Response response, Callback callback)
{
try
{
return container.handle(request, response, callback);
}
catch (Throwable x)
{
Response.writeError(request, response, callback, x);
return true;
}
}
@Override
public InvocationType getInvocationType()
{
if (isDynamic())
return InvocationType.BLOCKING;
Handler handler = getHandler();
InvocationType handlerInvocationType = handler == null ? InvocationType.NON_BLOCKING : handler.getInvocationType();
return Invocable.combine(handlerInvocationType, _container.getInvocationType());
}
}