Skip to content

Commit

Permalink
Issue #5229 - WebSocket documentation.
Browse files Browse the repository at this point in the history
Updates after review.

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
  • Loading branch information
sbordet committed Aug 17, 2021
1 parent 68fa147 commit 9fb6f99
Show file tree
Hide file tree
Showing 6 changed files with 22 additions and 48 deletions.
Expand Up @@ -14,17 +14,17 @@
[[pg-server-websocket-configure-filter]]
==== Advanced `WebSocketUpgradeFilter` Configuration

`JavaxWebSocketServletContainerInitializer` installs the `WebSocketUpgradeFilter` to handle HTTP requests that upgrade to WebSocket.
The WebSocket ``ServletContainerInitializer``s, namely `JavaxWebSocketServletContainerInitializer` and `JettyWebSocketServletContainerInitializer`, install the `WebSocketUpgradeFilter` to handle HTTP requests that upgrade to WebSocket.

Typically, the `WebSocketUpgradeFilter` is not present in the `web.xml` configuration, and therefore `JavaxWebSocketServletContainerInitializer` creates a new `WebSocketUpgradeFilter` and installs it _before_ any other Filter declared in `web.xml`, under the default name of `"org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter"` and with path mapping `/*`.
Typically, the `WebSocketUpgradeFilter` is not present in the `web.xml` configuration, and therefore the WebSocket ``ServletContainerInitializer``s create a new `WebSocketUpgradeFilter` and install it _before_ any other Filter declared in `web.xml`, under the default name of `"org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter"` and with path mapping `/*`.

However, if the `WebSocketUpgradeFilter` is already present in `web.xml` under the default name, then `JavaxWebSocketServletContainerInitializer` will use that declared in `web.xml` instead of creating a new one.
However, if the `WebSocketUpgradeFilter` is already present in `web.xml` under the default name, then the ``ServletContainerInitializer``s will use that declared in `web.xml` instead of creating a new one.

This allows you to customize:

* The filter order; for example, by configuring the `CrossOriginFilter` (or other filters) for increased security or authentication _before_ the `WebSocketUpgradeFilter`.
* The `WebSocketUpgradeFilter` configuration via ``init-param``s, that affects all `Session` instances created by this filter.
* The `WebSocketUpgradeFilter` path mapping. Rather than the default mapping of `/*`, you can map the `WebSocketUpgradeFilter` to a more specific path such as `/ws/*`.
* The `WebSocketUpgradeFilter` path mapping. Rather than the default mapping of `+/*+`, you can map the `WebSocketUpgradeFilter` to a more specific path such as `+/ws/*+`.
* The possibility to have multiple ``WebSocketUpgradeFilter``s, mapped to different paths, each with its own configuration.

For example:
Expand Down
Expand Up @@ -86,7 +86,7 @@ When using the Jetty WebSocket APIs, WebSocket endpoints must always be explicit
There are two ways of configuring WebSocket endpoints when using the Jetty WebSocket APIs:

* xref:pg-server-websocket-jetty-endpoints-container[Using `JettyWebSocketServerContainer`], which is very similar to how WebSocket endpoints are configured using the standard `javax.websocket` APIs (see xref:pg-server-websocket-standard-endpoints[here]).
* xref:pg-server-websocket-jetty-endpoints-servlet[Using `JettyWebSocketServlet`], which may be simpler to configure.
* xref:pg-server-websocket-jetty-endpoints-servlet[Using `JettyWebSocketServlet`], which may be more straightforward to configure in `web.xml`.

[[pg-server-websocket-jetty-endpoints-container]]
====== Using `JettyWebSocketServerContainer`
Expand Down Expand Up @@ -124,14 +124,7 @@ An alternative way to register WebSocket endpoints using the Jetty WebSocket API

This method has the advantage that it does not install the `WebSocketUpgradeFilter` under the hoods, because the WebSocket upgrade is handled directly by your `JettyWebSocketServlet` subclass.

This may also have a performance benefit for non-WebSocket HTTP requests (as they will not pass through the `WebSocketUpgradeFilter`), and be simpler to configure (for example, it is easier to configure the `CrossOriginFilter`, see also xref:pg-server-websocket-configure-filter[this section] for more information).

[IMPORTANT]
====
Do not mix the use of `JettyWebSocketServerContainer` with the use of `JettyWebSocketServlet`.
Using `JettyWebSocketServerContainer` will install the `WebSocketUpgradeFilter` under the hoods, which by default will intercepts all HTTP requests to upgrade to WebSocket, and your `JettyWebSocketServlet` subclasses will not be invoked.
====
This may also have a performance benefit for non-WebSocket HTTP requests (as they will not pass through the `WebSocketUpgradeFilter`), and be simpler to configure in `web.xml` (for example, it is easier to configure the `CrossOriginFilter`, see also xref:pg-server-websocket-configure-filter[this section] for more information).

For example:

Expand All @@ -148,8 +141,10 @@ include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/websocket/We
Note how in the call to `JettyWebSocketServletContainerInitializer.configure(\...)` the second parameter is `null`, because WebSocket endpoints are not created here, but instead by one (or more) `JettyWebSocketServlet` subclasses.
Yet the call is necessary to create other WebSocket implementation components that are necessary also when using `JettyWebSocketServlet` subclasses.

[[pg-server-websocket-jetty-upgrade]]
====== Upgrade to WebSocket
[TIP]
====
It is best to avoid mixing the use of `JettyWebSocketServerContainer` with the use of `JettyWebSocketServlet`, so that all your WebSocket endpoints are initialized by the same code in one place only.
====

An HTTP upgrade request to WebSocket that matches your `JettyWebSocketServlet` subclass path mapping (specified above via `ServletContextHandler.addServlet(...)`) arrives at the Servlet and is inspected to verify it is a valid upgrade to WebSocket.

Expand All @@ -159,3 +154,8 @@ From this point on, the communication happens with the WebSocket protocol, and H

If the HTTP request is not an upgrade to WebSocket, `JettyWebSocketServlet` delegates the processing to the superclass, `javax.servlet.HttpServlet`, which in turn invokes methods such as `doGet(...)` or `doPost(...)` depending on the HTTP method.
If your `JettyWebSocketServlet` subclass did not override the `doXYZ(...)` method corresponding to the HTTP request, a `405 Method Not Allowed` response is returned to the client.

Note that it is possible, although not recommended, to use both `JettyWebSocketServerContainer` and `JettyWebSocketServlet`.

Using `JettyWebSocketServerContainer` will install the `WebSocketUpgradeFilter` under the hoods, which by default will intercepts all HTTP requests to upgrade to WebSocket.
However, as explained in xref:pg-server-websocket-standard-upgrade[this section], if `WebSocketUpgradeFilter` does not find a matching WebSocket endpoint for the request URI path, then the HTTP request is passed to the Filter chain of your web application and may arrive to your `JettyWebSocketServlet` subclass, where it would be processed and possibly result in a WebSocket upgrade.
Expand Up @@ -135,7 +135,7 @@ Refer to the xref:pg-server-websocket-configure-filter[advanced `WebSocketUpgrad

With the default configuration, every HTTP request flows first through the `WebSocketUpgradeFilter`.

If the HTTP request is an upgrade to WebSocket, `WebSocketUpgradeFilter` performs the upgrade and does not invoke any other Filter or Servlet.
If the HTTP request is a valid upgrade to WebSocket, then `WebSocketUpgradeFilter` tries to find a matching WebSocket endpoint for the request URI path; if the match is found, `WebSocketUpgradeFilter` performs the upgrade and does not invoke any other Filter or Servlet.
From this point on, the communication happens with the WebSocket protocol, and HTTP components such as Filters and Servlets are not relevant anymore.

If the HTTP request is not an upgrade to WebSocket, `WebSocketUpgradeFilter` passes the request to the Filter chain of your web application, and eventually the request arrives to a Servlet to be processed (otherwise a `404 Not Found` response is returned to client).
If the HTTP request is not an upgrade to WebSocket, or `WebSocketUpgradeFilter` did not find a matching WebSocket endpoint for the request URI path, then the request is passed to the Filter chain of your web application, and eventually the request arrives to a Servlet to be processed (otherwise a `404 Not Found` response is returned to client).
Expand Up @@ -14,24 +14,6 @@
[[pg-server-websocket]]
=== WebSocket Server

// JettyWebSocketServletContainerInitializer.configure(ServletContextHandler context, Configurator configurator); is one entry point.

// JettyWebSocketServlet.configure(JettyWebSocketServletFactory factory); another entry point.

// TODO: why they expose different interfaces? Configurator exposes JettyWebSocketServerContainer, the servlet exposes JettyWebSocketServletFactory => historical, live with it.

// WSUFilter is automatically added lazily when context is started or when calling addMapping().
// Talk about WSUF but in the context of the upgrade, not much as an API to use, only init-params.
// WSUF init-params will however configure both Jetty and Javax containers.
// WSUF has a specific name in web.xml if you want to provide a WSUF that has a different config, e.g. different mapping than /*, or different init-params.

// For Javax I can pass in an HttpClient via ServletContext attribute.
// Also describe the XML file for HttpClient => needed to avoid class loader issues?

// WSUF and Servlet are typically mutually exclusive.
// However, WSUF could be mapped to /foo and servlet to /bar; OR
// WSUF has a mapping for /ep1 and Servlet has a mapping for /ep2, so an upgrade request enters WSUF, finds no mapping, forwards, lands to Servlet where the mapping is found.

Jetty provides two API implementations of the WebSocket protocol:

* An implementation for the standard `javax.websocket` APIs provided by link:https://www.jcp.org/en/jsr/detail?id=356[JSR 356], described in xref:pg-server-websocket-standard[this section].
Expand Down
Expand Up @@ -14,7 +14,6 @@
package org.eclipse.jetty.docs.programming.server.websocket;

import java.util.List;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.websocket.DeploymentException;
Expand All @@ -27,8 +26,6 @@
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer;
import org.eclipse.jetty.websocket.server.JettyServerUpgradeRequest;
import org.eclipse.jetty.websocket.server.JettyServerUpgradeResponse;
import org.eclipse.jetty.websocket.server.JettyWebSocketServerContainer;
import org.eclipse.jetty.websocket.server.JettyWebSocketServlet;
import org.eclipse.jetty.websocket.server.JettyWebSocketServletFactory;
Expand Down Expand Up @@ -137,7 +134,7 @@ public void standardContainerAndEndpoints() throws Exception
server.setHandler(handler);

// Setup the ServerContainer and the WebSocket endpoints for this web application context.
JavaxWebSocketServletContainerInitializer.configure(handler, (ServletContext context, ServerContainer container) ->
JavaxWebSocketServletContainerInitializer.configure(handler, (servletContext, container) ->
{
// Configure the ServerContainer.
container.setDefaultMaxTextMessageBufferSize(128 * 1024);
Expand Down Expand Up @@ -216,7 +213,7 @@ public void init() throws ServletException
container.addMapping("/ws/myURI", MyJettyWebSocketEndPoint.class);

// Advanced registration of your WebSocket endpoints.
container.addMapping("/ws/myOtherURI", (JettyServerUpgradeRequest request, JettyServerUpgradeResponse response) ->
container.addMapping("/ws/myOtherURI", (upgradeRequest, upgradeResponse) ->
new MyOtherJettyWebSocketEndPoint()
);
}
Expand All @@ -234,7 +231,7 @@ public void jettyContainerAndEndpoints() throws Exception
server.setHandler(handler);

// Setup the JettyWebSocketServerContainer and the WebSocket endpoints for this web application context.
JettyWebSocketServletContainerInitializer.configure(handler, (ServletContext context, JettyWebSocketServerContainer container) ->
JettyWebSocketServletContainerInitializer.configure(handler, (servletContext, container) ->
{
// Configure the ServerContainer.
container.setMaxTextMessageSize(128 * 1024);
Expand Down Expand Up @@ -290,7 +287,7 @@ protected void configure(JettyWebSocketServletFactory factory)
factory.setMaxTextMessageSize(1048576);

// Add the WebSocket endpoint.
factory.addMapping("/ws/someURI", (JettyServerUpgradeRequest upgradeRequest, JettyServerUpgradeResponse upgradeResponse) ->
factory.addMapping("/ws/someURI", (upgradeRequest, upgradeResponse) ->
{
// Possibly inspect the upgrade request and modify the upgrade response.

Expand Down
Expand Up @@ -149,11 +149,6 @@ protected void configure(JettyWebSocketServletFactory factory)
@WebSocket
public static class EchoSocket
{
public EchoSocket()
{
System.err.println("this = " + this);
}

@OnWebSocketMessage
public void onMessage(Session session, String message) throws Exception
{
Expand Down Expand Up @@ -196,7 +191,7 @@ public void websocketProvidedByServer() throws Exception
WebAppTester.WebApp app2 = webAppTester.createWebApp("/echo");
app2.addConfiguration(new JettyWebSocketConfiguration());
app2.createWebInf();
// app2.copyClass(EchoServlet.class);
app2.copyClass(EchoServlet.class);
app2.copyClass(EchoSocket.class);
app2.deploy();
});
Expand Down

0 comments on commit 9fb6f99

Please sign in to comment.