From 68fa1475d1e1f11176e1d5a6514ad2c9b8e26215 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Thu, 6 May 2021 11:58:16 +0200 Subject: [PATCH 1/4] Issue #5229 - WebSocket documentation. WebSocket server documentation. Signed-off-by: Simone Bordet --- documentation/jetty-documentation/pom.xml | 11 + .../websocket/server-websocket-filter.adoc | 79 +++++ .../websocket/server-websocket-jetty.adoc | 161 +++++++++ .../websocket/server-websocket-standard.adoc | 141 ++++++++ .../server/websocket/server-websocket.adoc | 49 ++- .../server/websocket/WebSocketServerDocs.java | 318 ++++++++++++++++++ .../core/server/WebSocketMappings.java | 15 +- .../tests/JettyClientClassLoaderTest.java | 7 +- .../servlet/WebSocketUpgradeFilter.java | 8 +- 9 files changed, 774 insertions(+), 15 deletions(-) create mode 100644 documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-filter.adoc create mode 100644 documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-jetty.adoc create mode 100644 documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-standard.adoc create mode 100644 documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/server/websocket/WebSocketServerDocs.java diff --git a/documentation/jetty-documentation/pom.xml b/documentation/jetty-documentation/pom.xml index 48dd43ee6834..ec0ed28deb16 100644 --- a/documentation/jetty-documentation/pom.xml +++ b/documentation/jetty-documentation/pom.xml @@ -210,6 +210,7 @@ org.eclipse.jetty jetty-slf4j-impl ${project.version} + runtime org.eclipse.jetty.memcached @@ -226,5 +227,15 @@ websocket-jetty-client ${project.version} + + org.eclipse.jetty.websocket + websocket-javax-server + ${project.version} + + + org.eclipse.jetty.websocket + websocket-jetty-server + ${project.version} + diff --git a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-filter.adoc b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-filter.adoc new file mode 100644 index 000000000000..7c33f71aa001 --- /dev/null +++ b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-filter.adoc @@ -0,0 +1,79 @@ +// +// ======================================================================== +// Copyright (c) 1995-2021 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 +// ======================================================================== +// + +[[pg-server-websocket-configure-filter]] +==== Advanced `WebSocketUpgradeFilter` Configuration + +`JavaxWebSocketServletContainerInitializer` installs 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 `/*`. + +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. + +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 possibility to have multiple ``WebSocketUpgradeFilter``s, mapped to different paths, each with its own configuration. + +For example: + +[source,xml,subs=verbatim] +---- + + + + My WebSocket WebApp + + + + cross-origin + org.eclipse.jetty.servlets.CrossOriginFilter + true + + + cross-origin + /* + + + + + + org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter + org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter + + + maxTextMessageSize + 1048576 + + true + + + org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter + + /ws/* + + + +---- +<1> The `CrossOriginFilter` is the first to protect against link:https://owasp.org/www-community/attacks/csrf[cross-site request forgery attacks]. +<2> The configuration for the _default_ `WebSocketUpgradeFilter`. +<3> Note the use of the _default_ `WebSocketUpgradeFilter` name. +<4> Specific configuration for `WebSocketUpgradeFilter` parameters. +<5> Use a more specific path mapping for `WebSocketUpgradeFilter`. + +Note that using a more specific path mapping for WebSocket requests is also beneficial to the performance of normal HTTP requests: they do not go through the `WebSocketUpgradeFilter` (as they will not match its path mapping), saving the cost of analyzing them to see whether they are WebSocket upgrade requests or not. diff --git a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-jetty.adoc b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-jetty.adoc new file mode 100644 index 000000000000..0c936edb547b --- /dev/null +++ b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-jetty.adoc @@ -0,0 +1,161 @@ +// +// ======================================================================== +// Copyright (c) 1995-2021 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 +// ======================================================================== +// + +[[pg-server-websocket-jetty]] +==== Jetty APIs Implementation + +When you write a WebSocket application using the Jetty WebSocket APIs, your code typically need to depend on just the APIs to compile your application. +However, at runtime you need to have the implementation of the Jetty WebSocket APIs in your class-path (or module-path). + +Jetty's WebSocket APIs are provided by the following Maven artifact: + +[source,xml,subs=normal] +---- + + org.eclipse.jetty.websocket + websocket-jetty-api + {version} + +---- + +Jetty's implementation of the Jetty WebSocket APIs is provided by the following Maven artifact (and its transitive dependencies): + +[source,xml,subs=normal] +---- + + org.eclipse.jetty.websocket + websocket-jetty-server + {version} + +---- + +[NOTE] +==== +The `websocket-jetty-api` artifact and the `websocket-jetty-server` artifact (and its transitive dependencies) should be present in the server class-path (or module-path), and never in the web application's `/WEB-INF/lib` directory. +==== + +To configure correctly your WebSocket application based on the Jetty WebSocket APIs, you need two steps: + +. Make sure that Jetty xref:pg-server-websocket-jetty-container[sets up] an instance of `JettyWebSocketServerContainer`, so that WebSocket applications can use it to register xref:pg-websocket-endpoints[WebSocket endpoints] that implement your application logic. +. Use the `JettyWebSocketServerContainer` APIs to xref:pg-server-websocket-jetty-endpoints[register your WebSocket endpoints]. + +[[pg-server-websocket-jetty-container]] +===== Setting up `JettyWebSocketServerContainer` + +Jetty sets up a `JettyWebSocketServerContainer` instance using `JettyWebSocketServletContainerInitializer`. + +When you deploy web applications using xref:pg-server-http-handler-use-webapp-context[`WebAppContext`], then `JettyWebSocketServletContainerInitializer` is automatically discovered and initialized by Jetty when the web application starts, so that it sets up the `JettyWebSocketServerContainer`. +In this way, you do not need to write any additional code: + +[source,java,indent=0] +---- +include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/websocket/WebSocketServerDocs.java[tags=standardContainerWebAppContext] +---- + +On the other hand, when you deploy web applications using xref:pg-server-http-handler-use-servlet-context[`ServletContextHandler`], you have to write the code to ensure that the `JettyWebSocketServletContainerInitializer` is initialized, so that it sets up the `JettyWebSocketServerContainer`: + +[source,java,indent=0] +---- +include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/websocket/WebSocketServerDocs.java[tags=jettyContainerServletContextHandler] +---- + +Calling `JettyWebSocketServletContainerInitializer.configure(\...)` must be done _before_ the `ServletContextHandler` is started, and configures the Jetty WebSocket implementation for that web application context. + +[[pg-server-websocket-jetty-endpoints]] +===== Configuring Endpoints + +Once you have xref:pg-server-websocket-jetty-container[setup] the `JettyWebSocketServerContainer`, you can configure your xref:pg-websocket-endpoints[WebSocket endpoints]. + +Differently from the xref:pg-server-websocket-standard-endpoints[configuration of standard WebSocket endpoints], WebSocket endpoint classes may be annotated with Jetty WebSocket API annotations, or extend the `org.eclipse.jetty.websocket.api.WebSocketListener` interface, but they are not automatically discovered, not even when deploying web applications using xref:pg-server-http-handler-use-webapp-context[`WebAppContext`]. + +[NOTE] +==== +When using the Jetty WebSocket APIs, WebSocket endpoints must always be explicitly configured. +==== + +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. + +[[pg-server-websocket-jetty-endpoints-container]] +====== Using `JettyWebSocketServerContainer` + +To register WebSocket endpoints using the Jetty WebSocket APIs you need to access the `JettyWebSocketServerContainer` APIs. + +The `JettyWebSocketServerContainer` instance is stored in the `ServletContext`, so it can be retrieved when the `ServletContext` is initialized, either from a `ServletContextListener` or from a `HttpServlet`: + +[source,java,indent=0] +---- +include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/websocket/WebSocketServerDocs.java[tags=jettyEndpointsInitialization] +---- + +[source,java,indent=0] +---- +include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/websocket/WebSocketServerDocs.java[tags=jettyWebSocketInitializerServlet] +---- + +You can also use this variant to set up the `JettyWebSocketServerContainer` and configure the WebSocket endpoints in one step: + +[source,java,indent=0] +---- +include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/websocket/WebSocketServerDocs.java[tags=jettyContainerAndEndpoints] +---- + +When the `ServletContextHandler` is started, the `Configurator` lambda (the second parameter passed to `JettyWebSocketServletContainerInitializer.configure(\...)`) is invoked and allows you to explicitly configure the WebSocket endpoints using the Jetty WebSocket APIs provided by `JettyWebSocketServerContainer`. + +Under the hood, `JettyWebSocketServerContainer` installs the `org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter`, which is the component that intercepts HTTP requests to upgrade to WebSocket, described in xref:pg-server-websocket-standard-upgrade[this section]. +For more information about the `WebSocketUpgradeFilter` see also xref:pg-server-websocket-configure-filter[this section]. + +[[pg-server-websocket-jetty-endpoints-servlet]] +====== Using `JettyWebSocketServlet` + +An alternative way to register WebSocket endpoints using the Jetty WebSocket APIs is to use a `JettyWebSocketServlet` subclass. + +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. +==== + +For example: + +[source,java,indent=0] +---- +include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/websocket/WebSocketServerDocs.java[tags=jettyWebSocketServletMain] +---- + +[source,java,indent=0] +---- +include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/websocket/WebSocketServerDocs.java[tags=jettyWebSocketServlet] +---- + +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 + +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. + +If the HTTP request is an upgrade to WebSocket, `JettyWebSocketServlet` calls `configure(JettyWebSocketServletFactory factory)` that you have overridden in your subclass, so that your application can instantiate and return the WebSocket endpoint. +After having obtained the WebSocket endpoint, `JettyWebSocketServlet` performs the WebSocket upgrade. +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, `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. diff --git a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-standard.adoc b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-standard.adoc new file mode 100644 index 000000000000..7f82e598345a --- /dev/null +++ b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-standard.adoc @@ -0,0 +1,141 @@ +// +// ======================================================================== +// Copyright (c) 1995-2021 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 +// ======================================================================== +// + +[[pg-server-websocket-standard]] +==== Standard APIs Implementation + +When you write a WebSocket application using the standard `javax.websocket` APIs, your code typically need to depend on just the APIs to compile your application. +However, at runtime you need to have an implementation of the standard APIs in your class-path (or module-path). + +The standard `javax.websocket` APIs are provided by the following Maven artifact: + +[source,xml,subs=normal] +---- + + javax.websocket + javax.websocket-api + 1.1 + +---- + +However, the artifact above lacks a proper JPMS `module-info.class` file, and therefore it is a little more difficult to use if you want to use of JPMS for your application. + +If you want to use JPMS for your application, you can use this Maven artifact instead: + +[source,xml,subs=normal] +---- + + org.eclipse.jetty.toolchain + jetty-javax-websocket-api + 1.1.2 + +---- + +This artifact is nothing more than the `javax.websocket:javax.websocket-api:1.1` artifact repackaged with a proper `module-info.class` file. + +At runtime, you also need an implementation of the standard `javax.websocket` APIs. + +Jetty's implementation of the standard `javax.websocket` APIs is provided by the following Maven artifact (and its transitive dependencies): + +[source,xml,subs=normal] +---- + + org.eclipse.jetty.websocket + websocket-javax-server + {version} + +---- + +[NOTE] +==== +The `javax.websocket-api` artifact and the `websocket-javax-server` artifact (and its transitive dependencies) should be present in the server class-path (or module-path), and never in the web application's `/WEB-INF/lib` directory. +==== + +To configure correctly your WebSocket application based on the standard `javax.websocket` APIs, you need two steps: + +. Make sure that Jetty xref:pg-server-websocket-standard-container[sets up] an instance of `javax.websocket.server.ServerContainer`, so that WebSocket applications can use it to register xref:pg-websocket-endpoints[WebSocket endpoints] that implement your application logic. +. Use the `ServerContainer` APIs to xref:pg-server-websocket-standard-endpoints[register your WebSocket endpoints]. + +[[pg-server-websocket-standard-container]] +===== Setting Up `ServerContainer` + +Jetty sets up a `ServerContainer` instance using `JavaxWebSocketServletContainerInitializer`. + +When you deploy web applications using xref:pg-server-http-handler-use-webapp-context[`WebAppContext`], then `JavaxWebSocketServletContainerInitializer` is automatically discovered and initialized by Jetty when the web application starts, so that it sets up the `ServerContainer`. +In this way, you do not need to write any additional code: + +[source,java,indent=0] +---- +include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/websocket/WebSocketServerDocs.java[tags=standardContainerWebAppContext] +---- + +On the other hand, when you deploy web applications using xref:pg-server-http-handler-use-servlet-context[`ServletContextHandler`], you have to write the code to ensure that the `JavaxWebSocketServletContainerInitializer` is initialized, so that it sets up the `ServerContainer`: + +[source,java,indent=0] +---- +include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/websocket/WebSocketServerDocs.java[tags=standardContainerServletContextHandler] +---- + +Calling `JavaxWebSocketServletContainerInitializer.configure(\...)` must be done _before_ the `ServletContextHandler` is started, and configures the `javax.websocket` implementation for that web application context. + +[[pg-server-websocket-standard-endpoints]] +===== Configuring Endpoints + +Once you have xref:pg-server-websocket-standard-container[setup] the `ServerContainer`, you can configure your xref:pg-websocket-endpoints[WebSocket endpoints]. + +The WebSocket endpoints classes may be either annotated with the standard `javax.websocket` annotations, extend the `javax.websocket.Endpoint` abstract class, or implement the `javax.websocket.server.ServerApplicationConfig` interface. + +When you deploy web applications using xref:pg-server-http-handler-use-webapp-context[`WebAppContext`], then annotated WebSocket endpoint classes are automatically discovered and registered. +In this way, you do not need to write any additional code; you just need to ensure that your WebSocket endpoint classes are present in the web application's `/WEB-INF/classes` directory, or in a `*.jar` file in `/WEB-INF/lib`. + +On the other hand, when you deploy web applications using xref:pg-server-http-handler-use-servlet-context[`ServletContextHandler`], or you need to perform more advanced configuration of the `ServerContainer` or of the WebSocket endpoints, you need to access the `ServerContainer` APIs. + +The `ServerContainer` instance is stored as a `ServletContext` attribute, so it can be retrieved when the `ServletContext` is initialized, either from a `ServletContextListener` or from a `HttpServlet`: + +[source,java,indent=0] +---- +include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/websocket/WebSocketServerDocs.java[tags=standardEndpointsInitialization] +---- + +[source,java,indent=0] +---- +include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/websocket/WebSocketServerDocs.java[tags=standardWebSocketInitializerServlet] +---- + +When you deploy web applications using xref:pg-server-http-handler-use-servlet-context[`ServletContextHandler`], you can also use this variant to set up the `ServerContainer` and configure the WebSocket endpoints in one step: + +[source,java,indent=0] +---- +include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/websocket/WebSocketServerDocs.java[tags=standardContainerAndEndpoints] +---- + +When the `ServletContextHandler` is started, the `Configurator` lambda (the second parameter passed to `JavaxWebSocketServletContainerInitializer.configure(\...)`) is invoked and allows you to explicitly configure the WebSocket endpoints using the standard APIs provided by `ServerContainer`. + +[[pg-server-websocket-standard-upgrade]] +====== Upgrade to WebSocket + +Under the hood, `JavaxWebSocketServletContainerInitializer` installs the `org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter`, which is the component that intercepts HTTP requests to upgrade to WebSocket, and performs the upgrade from the HTTP protocol to the WebSocket protocol. + +[NOTE] +==== +The `WebSocketUpgradeFilter` is installed under the filter name corresponding to its class name (that is, the string `"org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter"`) and with a filter mapping of `/*`. + +Refer to the xref:pg-server-websocket-configure-filter[advanced `WebSocketUpgradeFilter` configuration section] for more information. +==== + +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. +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). diff --git a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket.adoc b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket.adoc index 2bc35d4b8683..ee1ba801becc 100644 --- a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket.adoc +++ b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket.adoc @@ -12,6 +12,51 @@ // [[pg-server-websocket]] -=== WebSocket Server Libraries +=== WebSocket Server -TODO +// 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]. +* An implementation for Jetty-specific WebSocket APIs, described in xref:pg-server-websocket-jetty[this section]. + +Using the standard `javax.websocket` APIs allows your applications to depend only on standard APIs, and your applications may be deployed in any compliant WebSocket Container that supports JSR 356. + +The standard APIs provide few features that are not present in the Jetty WebSocket APIs: + +* Encoders and Decoders for automatic conversion of text or binary messages to objects. +* `Reader` and `InputStream` for simple, blocking, message streaming. +* Simple URI template matching. + +On the other hand, the Jetty WebSocket APIs are more efficient and offer greater and more fine-grained control, and provide features that are not present in the standard APIs: + +* `MessageSink` for advanced, asynchronous, message streaming with backpressure. +* Suspend/resume to control backpressure. +* Remote socket address (IP address and port) information. +* WebSocket upgrade handling via Filter or Servlet. +* Advanced URI matching with Servlet WebSocket upgrade. +* Control of the idle timeout. +* Configuration of the network buffer capacity. + +If your application needs specific features that are not provided by the standard APIs, the Jetty WebSocket APIs may provide such features -- and if they do not, you may ask for these features by submitting an issue to the Jetty Project without waiting for the standard process to approve them. + +include::server-websocket-standard.adoc[] +include::server-websocket-jetty.adoc[] +include::server-websocket-filter.adoc[] diff --git a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/server/websocket/WebSocketServerDocs.java b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/server/websocket/WebSocketServerDocs.java new file mode 100644 index 000000000000..3f9d66484fd4 --- /dev/null +++ b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/server/websocket/WebSocketServerDocs.java @@ -0,0 +1,318 @@ +// +// ======================================================================== +// Copyright (c) 1995-2021 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.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; +import javax.websocket.server.ServerContainer; +import javax.websocket.server.ServerEndpoint; +import javax.websocket.server.ServerEndpointConfig; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; +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; +import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer; + +@SuppressWarnings("unused") +public class WebSocketServerDocs +{ + public void standardContainerWebAppContext() throws Exception + { + // tag::standardContainerWebAppContext[] + // Create a Server with a ServerConnector listening on port 8080. + Server server = new Server(8080); + + // Create a WebAppContext with the given context path. + WebAppContext handler = new WebAppContext("/path/to/webapp", "/ctx"); + server.setHandler(handler); + + // Starting the Server will start the WebAppContext. + server.start(); + // end::standardContainerWebAppContext[] + } + + public void standardContainerServletContextHandler() throws Exception + { + // tag::standardContainerServletContextHandler[] + // Create a Server with a ServerConnector listening on port 8080. + Server server = new Server(8080); + + // Create a ServletContextHandler with the given context path. + ServletContextHandler handler = new ServletContextHandler(server, "/ctx"); + server.setHandler(handler); + + // Ensure that JavaxWebSocketServletContainerInitializer is initialized, + // to setup the ServerContainer for this web application context. + JavaxWebSocketServletContainerInitializer.configure(handler, null); + + // Starting the Server will start the ServletContextHandler. + server.start(); + // end::standardContainerServletContextHandler[] + } + + public void standardEndpointsInitialization() throws Exception + { + // tag::standardEndpointsInitialization[] + // Create a Server with a ServerConnector listening on port 8080. + Server server = new Server(8080); + + // Create a ServletContextHandler with the given context path. + ServletContextHandler handler = new ServletContextHandler(server, "/ctx"); + server.setHandler(handler); + + // Ensure that JavaxWebSocketServletContainerInitializer is initialized, + // to setup the ServerContainer for this web application context. + JavaxWebSocketServletContainerInitializer.configure(handler, null); + + // Add a WebSocket-initializer Servlet to register WebSocket endpoints. + handler.addServlet(MyJavaxWebSocketInitializerServlet.class, "/*"); + + // Starting the Server will start the ServletContextHandler. + server.start(); + // end::standardEndpointsInitialization[] + } + + @SuppressWarnings("InnerClassMayBeStatic") + // tag::standardWebSocketInitializerServlet[] + public class MyJavaxWebSocketInitializerServlet extends HttpServlet + { + @Override + public void init() throws ServletException + { + try + { + // Retrieve the ServerContainer from the ServletContext attributes. + ServerContainer container = (ServerContainer)getServletContext().getAttribute(ServerContainer.class.getName()); + + // Configure the ServerContainer. + container.setDefaultMaxTextMessageBufferSize(128 * 1024); + + // Simple registration of your WebSocket endpoints. + container.addEndpoint(MyJavaxWebSocketEndPoint.class); + + // Advanced registration of your WebSocket endpoints. + container.addEndpoint( + ServerEndpointConfig.Builder.create(MyJavaxWebSocketEndPoint.class, "/ws") + .subprotocols(List.of("my-ws-protocol")) + .build() + ); + } + catch (DeploymentException x) + { + throw new ServletException(x); + } + } + } + // end::standardWebSocketInitializerServlet[] + + public void standardContainerAndEndpoints() throws Exception + { + // tag::standardContainerAndEndpoints[] + // Create a Server with a ServerConnector listening on port 8080. + Server server = new Server(8080); + + // Create a ServletContextHandler with the given context path. + ServletContextHandler handler = new ServletContextHandler(server, "/ctx"); + server.setHandler(handler); + + // Setup the ServerContainer and the WebSocket endpoints for this web application context. + JavaxWebSocketServletContainerInitializer.configure(handler, (ServletContext context, ServerContainer container) -> + { + // Configure the ServerContainer. + container.setDefaultMaxTextMessageBufferSize(128 * 1024); + + // Simple registration of your WebSocket endpoints. + container.addEndpoint(MyJavaxWebSocketEndPoint.class); + + // Advanced registration of your WebSocket endpoints. + container.addEndpoint( + ServerEndpointConfig.Builder.create(MyJavaxWebSocketEndPoint.class, "/ws") + .subprotocols(List.of("my-ws-protocol")) + .build() + ); + }); + + // Starting the Server will start the ServletContextHandler. + server.start(); + // end::standardContainerAndEndpoints[] + } + + public void jettyContainerServletContextHandler() throws Exception + { + // tag::jettyContainerServletContextHandler[] + // Create a Server with a ServerConnector listening on port 8080. + Server server = new Server(8080); + + // Create a ServletContextHandler with the given context path. + ServletContextHandler handler = new ServletContextHandler(server, "/ctx"); + server.setHandler(handler); + + // Ensure that JettyWebSocketServletContainerInitializer is initialized, + // to setup the JettyWebSocketServerContainer for this web application context. + JettyWebSocketServletContainerInitializer.configure(handler, null); + + // Starting the Server will start the ServletContextHandler. + server.start(); + // end::jettyContainerServletContextHandler[] + } + + public void jettyEndpointsInitialization() throws Exception + { + // tag::jettyEndpointsInitialization[] + // Create a Server with a ServerConnector listening on port 8080. + Server server = new Server(8080); + + // Create a ServletContextHandler with the given context path. + ServletContextHandler handler = new ServletContextHandler(server, "/ctx"); + server.setHandler(handler); + + // Ensure that JettyWebSocketServletContainerInitializer is initialized, + // to setup the JettyWebSocketServerContainer for this web application context. + JettyWebSocketServletContainerInitializer.configure(handler, null); + + // Add a WebSocket-initializer Servlet to register WebSocket endpoints. + handler.addServlet(MyJettyWebSocketInitializerServlet.class, "/*"); + + // Starting the Server will start the ServletContextHandler. + server.start(); + // end::jettyEndpointsInitialization[] + } + + @SuppressWarnings("InnerClassMayBeStatic") + // tag::jettyWebSocketInitializerServlet[] + public class MyJettyWebSocketInitializerServlet extends HttpServlet + { + @Override + public void init() throws ServletException + { + // Retrieve the JettyWebSocketServerContainer. + JettyWebSocketServerContainer container = JettyWebSocketServerContainer.getContainer(getServletContext()); + + // Configure the JettyWebSocketServerContainer. + container.setMaxTextMessageSize(128 * 1024); + + // Simple registration of your WebSocket endpoints. + container.addMapping("/ws/myURI", MyJettyWebSocketEndPoint.class); + + // Advanced registration of your WebSocket endpoints. + container.addMapping("/ws/myOtherURI", (JettyServerUpgradeRequest request, JettyServerUpgradeResponse response) -> + new MyOtherJettyWebSocketEndPoint() + ); + } + } + // end::jettyWebSocketInitializerServlet[] + + public void jettyContainerAndEndpoints() throws Exception + { + // tag::jettyContainerAndEndpoints[] + // Create a Server with a ServerConnector listening on port 8080. + Server server = new Server(8080); + + // Create a ServletContextHandler with the given context path. + ServletContextHandler handler = new ServletContextHandler(server, "/ctx"); + server.setHandler(handler); + + // Setup the JettyWebSocketServerContainer and the WebSocket endpoints for this web application context. + JettyWebSocketServletContainerInitializer.configure(handler, (ServletContext context, JettyWebSocketServerContainer container) -> + { + // Configure the ServerContainer. + container.setMaxTextMessageSize(128 * 1024); + + // Add your WebSocket endpoint(s) to the JettyWebSocketServerContainer. + container.addMapping("/ws/myURI", MyJettyWebSocketEndPoint.class); + + // Use JettyWebSocketCreator to have more control on the WebSocket endpoint creation. + container.addMapping("/ws/myOtherURI", (upgradeRequest, upgradeResponse) -> + { + // Possibly inspect the upgrade request and modify the upgrade response. + upgradeResponse.setAcceptedSubProtocol("my-ws-protocol"); + + // Create the new WebSocket endpoint. + return new MyOtherJettyWebSocketEndPoint(); + }); + }); + + // Starting the Server will start the ServletContextHandler. + server.start(); + // end::jettyContainerAndEndpoints[] + } + + public void jettyWebSocketServletMain() throws Exception + { + // tag::jettyWebSocketServletMain[] + // Create a Server with a ServerConnector listening on port 8080. + Server server = new Server(8080); + + // Create a ServletContextHandler with the given context path. + ServletContextHandler handler = new ServletContextHandler(server, "/ctx"); + server.setHandler(handler); + + // Setup the JettyWebSocketServerContainer to initialize WebSocket components. + JettyWebSocketServletContainerInitializer.configure(handler, null); + + // Add your WebSocketServlet subclass to the ServletContextHandler. + handler.addServlet(MyJettyWebSocketServlet.class, "/ws/*"); + + // Starting the Server will start the ServletContextHandler. + server.start(); + // end::jettyWebSocketServletMain[] + } + + @SuppressWarnings("InnerClassMayBeStatic") + // tag::jettyWebSocketServlet[] + public class MyJettyWebSocketServlet extends JettyWebSocketServlet + { + @Override + protected void configure(JettyWebSocketServletFactory factory) + { + // At most 1 MiB text messages. + factory.setMaxTextMessageSize(1048576); + + // Add the WebSocket endpoint. + factory.addMapping("/ws/someURI", (JettyServerUpgradeRequest upgradeRequest, JettyServerUpgradeResponse upgradeResponse) -> + { + // Possibly inspect the upgrade request and modify the upgrade response. + + // Create the new WebSocket endpoint. + return new MyJettyWebSocketEndPoint(); + }); + } + } + // end::jettyWebSocketServlet[] + + @ServerEndpoint("/ws") + private static class MyJavaxWebSocketEndPoint + { + } + + @WebSocket + private static class MyJettyWebSocketEndPoint + { + } + + @WebSocket + private static class MyOtherJettyWebSocketEndPoint + { + } +} diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/WebSocketMappings.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/WebSocketMappings.java index 7f1a6be3df44..dc72d911fb30 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/WebSocketMappings.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/WebSocketMappings.java @@ -26,6 +26,7 @@ import org.eclipse.jetty.http.pathmap.ServletPathSpec; import org.eclipse.jetty.http.pathmap.UriTemplatePathSpec; import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.component.Dumpable; import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.websocket.core.Configuration; @@ -223,17 +224,15 @@ public WebSocketNegotiator getMatchedNegotiator(String target, Consumer { - // Store PathSpec resource mapping as request attribute, for WebSocketCreator - // implementors to use later if they wish + // Store PathSpec resource mapping as request attribute, + // for WebSocketCreator implementors to use later if they wish. request.setAttribute(PathSpec.class.getName(), pathSpec); }); diff --git a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyClientClassLoaderTest.java b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyClientClassLoaderTest.java index 9ed2161edf26..288b5a63b764 100644 --- a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyClientClassLoaderTest.java +++ b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyClientClassLoaderTest.java @@ -149,6 +149,11 @@ 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 { @@ -191,7 +196,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(); }); diff --git a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketUpgradeFilter.java b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketUpgradeFilter.java index 810049d3f5d1..e945491746e2 100644 --- a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketUpgradeFilter.java +++ b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketUpgradeFilter.java @@ -143,7 +143,7 @@ public void lifeCycleStopping(LifeCycle event) } private final Configuration.ConfigurationCustomizer defaultCustomizer = new Configuration.ConfigurationCustomizer(); - private WebSocketMappings mapping; + private WebSocketMappings mappings; @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException @@ -151,7 +151,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha HttpServletRequest httpreq = (HttpServletRequest)request; HttpServletResponse httpresp = (HttpServletResponse)response; - if (mapping.upgrade(httpreq, httpresp, defaultCustomizer)) + if (mappings.upgrade(httpreq, httpresp, defaultCustomizer)) return; // If we reach this point, it means we had an incoming request to upgrade @@ -173,13 +173,13 @@ public String dump() @Override public void dump(Appendable out, String indent) throws IOException { - Dumpable.dumpObjects(out, indent, this, mapping); + Dumpable.dumpObjects(out, indent, this, mappings); } @Override public void init(FilterConfig config) throws ServletException { - mapping = WebSocketMappings.ensureMappings(config.getServletContext()); + mappings = WebSocketMappings.ensureMappings(config.getServletContext()); String max = config.getInitParameter("idleTimeout"); if (max == null) From 9fb6f990417f8f8cf1b7d6310f1484485d0a85cf Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Tue, 17 Aug 2021 11:11:29 +0200 Subject: [PATCH 2/4] Issue #5229 - WebSocket documentation. Updates after review. Signed-off-by: Simone Bordet --- .../websocket/server-websocket-filter.adoc | 8 +++---- .../websocket/server-websocket-jetty.adoc | 22 +++++++++---------- .../websocket/server-websocket-standard.adoc | 4 ++-- .../server/websocket/server-websocket.adoc | 18 --------------- .../server/websocket/WebSocketServerDocs.java | 11 ++++------ .../tests/JettyClientClassLoaderTest.java | 7 +----- 6 files changed, 22 insertions(+), 48 deletions(-) diff --git a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-filter.adoc b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-filter.adoc index 7c33f71aa001..09bab9e5c417 100644 --- a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-filter.adoc +++ b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-filter.adoc @@ -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: diff --git a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-jetty.adoc b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-jetty.adoc index 0c936edb547b..425b71d16239 100644 --- a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-jetty.adoc +++ b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-jetty.adoc @@ -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` @@ -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: @@ -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. @@ -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. diff --git a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-standard.adoc b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-standard.adoc index 7f82e598345a..8aba3c2d34d4 100644 --- a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-standard.adoc +++ b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-standard.adoc @@ -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). diff --git a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket.adoc b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket.adoc index ee1ba801becc..02dc9e344514 100644 --- a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket.adoc +++ b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket.adoc @@ -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]. diff --git a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/server/websocket/WebSocketServerDocs.java b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/server/websocket/WebSocketServerDocs.java index 3f9d66484fd4..5da1c8d353ef 100644 --- a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/server/websocket/WebSocketServerDocs.java +++ b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/server/websocket/WebSocketServerDocs.java @@ -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; @@ -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; @@ -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); @@ -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() ); } @@ -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); @@ -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. diff --git a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyClientClassLoaderTest.java b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyClientClassLoaderTest.java index 288b5a63b764..9ed2161edf26 100644 --- a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyClientClassLoaderTest.java +++ b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyClientClassLoaderTest.java @@ -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 { @@ -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(); }); From cd75747e674bd5e47c60fc1c9b96818800853ddc Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Mon, 6 Sep 2021 15:51:26 +0200 Subject: [PATCH 3/4] Issue #5229 - WebSocket documentation. Updates after review. Signed-off-by: Simone Bordet --- .../websocket/server-websocket-filter.adoc | 7 ++- .../websocket/server-websocket-jetty.adoc | 62 ++++++++++++------- .../websocket/server-websocket-standard.adoc | 6 +- .../server/websocket/WebSocketServerDocs.java | 36 +++++++++++ 4 files changed, 83 insertions(+), 28 deletions(-) diff --git a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-filter.adoc b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-filter.adoc index 09bab9e5c417..bcd2ad62bfbd 100644 --- a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-filter.adoc +++ b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-filter.adoc @@ -14,9 +14,12 @@ [[pg-server-websocket-configure-filter]] ==== Advanced `WebSocketUpgradeFilter` Configuration -The WebSocket ``ServletContainerInitializer``s, namely `JavaxWebSocketServletContainerInitializer` and `JettyWebSocketServletContainerInitializer`, install the `WebSocketUpgradeFilter` to handle HTTP requests that upgrade to WebSocket. +The `WebSocketUpgradeFilter` that handles the HTTP requests that upgrade to WebSocket is installed in these cases: -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 `/*`. +* Either by the `JavaxWebSocketServletContainerInitializer`, as described in xref:pg-server-websocket-standard[this section]. +* Or by a call to `JettyWebSocketServerContainer.addMapping(\...)`, as described in xref:pg-server-websocket-jetty[this section]. + +Typically, the `WebSocketUpgradeFilter` is not present in the `web.xml` configuration, and therefore the mechanisms above 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 the ``ServletContainerInitializer``s will use that declared in `web.xml` instead of creating a new one. diff --git a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-jetty.adoc b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-jetty.adoc index 425b71d16239..d04e3fc84485 100644 --- a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-jetty.adoc +++ b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-jetty.adoc @@ -14,8 +14,8 @@ [[pg-server-websocket-jetty]] ==== Jetty APIs Implementation -When you write a WebSocket application using the Jetty WebSocket APIs, your code typically need to depend on just the APIs to compile your application. -However, at runtime you need to have the implementation of the Jetty WebSocket APIs in your class-path (or module-path). +When you write a WebSocket application using the Jetty WebSocket APIs, your code typically need to depend on just the Jetty WebSocket APIs to compile your application. +However, at runtime you need to have the _implementation_ of the Jetty WebSocket APIs in your class-path (or module-path). Jetty's WebSocket APIs are provided by the following Maven artifact: @@ -46,8 +46,8 @@ The `websocket-jetty-api` artifact and the `websocket-jetty-server` artifact (an To configure correctly your WebSocket application based on the Jetty WebSocket APIs, you need two steps: -. Make sure that Jetty xref:pg-server-websocket-jetty-container[sets up] an instance of `JettyWebSocketServerContainer`, so that WebSocket applications can use it to register xref:pg-websocket-endpoints[WebSocket endpoints] that implement your application logic. -. Use the `JettyWebSocketServerContainer` APIs to xref:pg-server-websocket-jetty-endpoints[register your WebSocket endpoints]. +. Make sure that Jetty xref:pg-server-websocket-jetty-container[sets up] an instance of `JettyWebSocketServerContainer`. +. Use the `JettyWebSocketServerContainer` APIs in your applications to xref:pg-server-websocket-jetty-endpoints[register your WebSocket endpoints] that implement your application logic. [[pg-server-websocket-jetty-container]] ===== Setting up `JettyWebSocketServerContainer` @@ -78,15 +78,15 @@ Once you have xref:pg-server-websocket-jetty-container[setup] the `JettyWebSocke Differently from the xref:pg-server-websocket-standard-endpoints[configuration of standard WebSocket endpoints], WebSocket endpoint classes may be annotated with Jetty WebSocket API annotations, or extend the `org.eclipse.jetty.websocket.api.WebSocketListener` interface, but they are not automatically discovered, not even when deploying web applications using xref:pg-server-http-handler-use-webapp-context[`WebAppContext`]. -[NOTE] +[IMPORTANT] ==== When using the Jetty WebSocket APIs, WebSocket endpoints must always be explicitly configured. ==== 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 more straightforward to configure in `web.xml`. +* xref:pg-server-websocket-jetty-endpoints-container[Using `JettyWebSocketServerContainer`], which is very similar to how WebSocket endpoints are configured when using the xref:pg-server-websocket-standard-endpoints[standard `javax.websocket` APIs], but also provides APIs to perform a direct, programmatic, WebSocket upgrade. +* xref:pg-server-websocket-jetty-endpoints-servlet[Using `JettyWebSocketServlet`], which may configured in `web.xml`, rather than in Java code. [[pg-server-websocket-jetty-endpoints-container]] ====== Using `JettyWebSocketServerContainer` @@ -114,19 +114,35 @@ include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/websocket/We When the `ServletContextHandler` is started, the `Configurator` lambda (the second parameter passed to `JettyWebSocketServletContainerInitializer.configure(\...)`) is invoked and allows you to explicitly configure the WebSocket endpoints using the Jetty WebSocket APIs provided by `JettyWebSocketServerContainer`. -Under the hood, `JettyWebSocketServerContainer` installs the `org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter`, which is the component that intercepts HTTP requests to upgrade to WebSocket, described in xref:pg-server-websocket-standard-upgrade[this section]. +Under the hood, the call to `JettyWebSocketServerContainer.addMapping(\...)` installs the `org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter`, which is the component that intercepts HTTP requests to upgrade to WebSocket, described in xref:pg-server-websocket-standard-upgrade[this section]. For more information about the `WebSocketUpgradeFilter` see also xref:pg-server-websocket-configure-filter[this section]. +One last alternative to register your WebSocket endpoints is to use a programmatic WebSocket upgrade via `JettyWebSocketServerContainer.upgrade(\...)`, which allows you to use a standard `HttpServlet` subclass (rather than a `JettyWebSocketServlet` as explained in xref:pg-server-websocket-jetty-endpoints-servlet[this section]) to perform a direct WebSocket upgrade when your application logic demands so: + +[source,java,indent=0] +---- +include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/websocket/WebSocketServerDocs.java[tags=jettyContainerServletContextHandler] +---- + +[source,java,indent=0] +---- +include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/websocket/WebSocketServerDocs.java[tags=jettyContainerUpgrade] +---- + +When using `JettyWebSocketServerContainer.upgrade(\...)`, the `WebSocketUpgradeFilter` is not installed, since the WebSocket upgrade is performed programmatically. + [[pg-server-websocket-jetty-endpoints-servlet]] ====== Using `JettyWebSocketServlet` -An alternative way to register WebSocket endpoints using the Jetty WebSocket APIs is to use a `JettyWebSocketServlet` subclass. +An alternative way to register WebSocket endpoints using the Jetty WebSocket APIs is to use a `JettyWebSocketServlet` subclass (or even many different `JettyWebSocketServlet` subclasses). -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 method has the advantage that it does not install the `WebSocketUpgradeFilter` under the hood, 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`). -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). +Your `JettyWebSocketServlet` subclass may be declared and configured either in code or in `web.xml`. +Declaring your `JettyWebSocketServlet` subclass explicitly in code or in `web.xml` also simplifies the declaration and configuration of other web components such as other Servlets and/or Filters (for example, it is easier to configure the `CrossOriginFilter`, see also xref:pg-server-websocket-configure-filter[this section] for more information). -For example: +For example, your `JettyWebSocketServlet` subclass may be declared in code in this way: [source,java,indent=0] ---- @@ -141,21 +157,21 @@ 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. -[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. +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 whether it is a valid upgrade to WebSocket. -If the HTTP request is an upgrade to WebSocket, `JettyWebSocketServlet` calls `configure(JettyWebSocketServletFactory factory)` that you have overridden in your subclass, so that your application can instantiate and return the WebSocket endpoint. +If the HTTP request is a valid upgrade to WebSocket, `JettyWebSocketServlet` calls `configure(JettyWebSocketServletFactory factory)` that you have overridden in your subclass, so that your application can instantiate and return the WebSocket endpoint. After having obtained the WebSocket endpoint, `JettyWebSocketServlet` performs the WebSocket upgrade. 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, `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. +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, as per the standard `HttpServlet` class implementation. -Note that it is possible, although not recommended, to use both `JettyWebSocketServerContainer` and `JettyWebSocketServlet`. +[NOTE] +==== +It is possible to use both `JettyWebSocketServerContainer` and `JettyWebSocketServlet`. + +However, it is typically 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. +==== -Using `JettyWebSocketServerContainer` will install the `WebSocketUpgradeFilter` under the hoods, which by default will intercepts all HTTP requests to upgrade to WebSocket. +Using `JettyWebSocketServerContainer.addMapping(\...)` will install the `WebSocketUpgradeFilter` under the hood, 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. diff --git a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-standard.adoc b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-standard.adoc index 8aba3c2d34d4..90ce7280c3aa 100644 --- a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-standard.adoc +++ b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-standard.adoc @@ -63,8 +63,8 @@ The `javax.websocket-api` artifact and the `websocket-javax-server` artifact (an To configure correctly your WebSocket application based on the standard `javax.websocket` APIs, you need two steps: -. Make sure that Jetty xref:pg-server-websocket-standard-container[sets up] an instance of `javax.websocket.server.ServerContainer`, so that WebSocket applications can use it to register xref:pg-websocket-endpoints[WebSocket endpoints] that implement your application logic. -. Use the `ServerContainer` APIs to xref:pg-server-websocket-standard-endpoints[register your WebSocket endpoints]. +. Make sure that Jetty xref:pg-server-websocket-standard-container[sets up] an instance of `javax.websocket.server.ServerContainer`. +. Use the `ServerContainer` APIs in your applications to xref:pg-server-websocket-standard-endpoints[register your WebSocket endpoints] that implement your application logic. [[pg-server-websocket-standard-container]] ===== Setting Up `ServerContainer` @@ -98,7 +98,7 @@ The WebSocket endpoints classes may be either annotated with the standard `javax When you deploy web applications using xref:pg-server-http-handler-use-webapp-context[`WebAppContext`], then annotated WebSocket endpoint classes are automatically discovered and registered. In this way, you do not need to write any additional code; you just need to ensure that your WebSocket endpoint classes are present in the web application's `/WEB-INF/classes` directory, or in a `*.jar` file in `/WEB-INF/lib`. -On the other hand, when you deploy web applications using xref:pg-server-http-handler-use-servlet-context[`ServletContextHandler`], or you need to perform more advanced configuration of the `ServerContainer` or of the WebSocket endpoints, you need to access the `ServerContainer` APIs. +On the other hand, when you deploy web applications using xref:pg-server-http-handler-use-webapp-context[`WebAppContext`] but you need to perform more advanced configuration of the `ServerContainer` or of the WebSocket endpoints, or when you deploy web applications using xref:pg-server-http-handler-use-servlet-context[`ServletContextHandler`], you need to access the `ServerContainer` APIs. The `ServerContainer` instance is stored as a `ServletContext` attribute, so it can be retrieved when the `ServletContext` is initialized, either from a `ServletContextListener` or from a `HttpServlet`: diff --git a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/server/websocket/WebSocketServerDocs.java b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/server/websocket/WebSocketServerDocs.java index 5da1c8d353ef..51b24422c22c 100644 --- a/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/server/websocket/WebSocketServerDocs.java +++ b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/server/websocket/WebSocketServerDocs.java @@ -13,9 +13,12 @@ package org.eclipse.jetty.docs.programming.server.websocket; +import java.io.IOException; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import javax.websocket.DeploymentException; import javax.websocket.server.ServerContainer; import javax.websocket.server.ServerEndpoint; @@ -26,6 +29,7 @@ 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.JettyWebSocketCreator; import org.eclipse.jetty.websocket.server.JettyWebSocketServerContainer; import org.eclipse.jetty.websocket.server.JettyWebSocketServlet; import org.eclipse.jetty.websocket.server.JettyWebSocketServletFactory; @@ -255,6 +259,38 @@ public void jettyContainerAndEndpoints() throws Exception // end::jettyContainerAndEndpoints[] } + @SuppressWarnings("InnerClassMayBeStatic") + // tag::jettyContainerUpgrade[] + public class ProgrammaticWebSocketUpgradeServlet extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException + { + if (requiresWebSocketUpgrade(request)) + { + // Retrieve the JettyWebSocketServerContainer. + JettyWebSocketServerContainer container = JettyWebSocketServerContainer.getContainer(getServletContext()); + + // Use a JettyWebSocketCreator to inspect the upgrade request, + // possibly modify the upgrade response, and create the WebSocket endpoint. + JettyWebSocketCreator creator = (upgradeRequest, upgradeResponse) -> new MyJettyWebSocketEndPoint(); + + // Perform the direct WebSocket upgrade. + container.upgrade(creator, request, response); + } + else + { + // Normal handling of the HTTP request/response. + } + } + } + // end::jettyContainerUpgrade[] + + private boolean requiresWebSocketUpgrade(HttpServletRequest request) + { + return false; + } + public void jettyWebSocketServletMain() throws Exception { // tag::jettyWebSocketServletMain[] From d92c91c0c8ca9a1c202d891543fe4e318318ca22 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Tue, 7 Sep 2021 10:17:26 +0200 Subject: [PATCH 4/4] Issue #5229 - WebSocket documentation. Updates after review. Signed-off-by: Simone Bordet --- .../server/websocket/server-websocket-standard.adoc | 2 +- .../programming-guide/server/websocket/server-websocket.adoc | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-standard.adoc b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-standard.adoc index 90ce7280c3aa..3d69d07cdf7c 100644 --- a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-standard.adoc +++ b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-standard.adoc @@ -64,7 +64,7 @@ The `javax.websocket-api` artifact and the `websocket-javax-server` artifact (an To configure correctly your WebSocket application based on the standard `javax.websocket` APIs, you need two steps: . Make sure that Jetty xref:pg-server-websocket-standard-container[sets up] an instance of `javax.websocket.server.ServerContainer`. -. Use the `ServerContainer` APIs in your applications to xref:pg-server-websocket-standard-endpoints[register your WebSocket endpoints] that implement your application logic. +. xref:pg-server-websocket-standard-endpoints[Configure] the WebSocket endpoints that implement your application logic, either by annotating their classes with the standard `javax.websocket` annotations, or by using the `ServerContainer` APIs to register them in your code. [[pg-server-websocket-standard-container]] ===== Setting Up `ServerContainer` diff --git a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket.adoc b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket.adoc index 02dc9e344514..6db4ab470669 100644 --- a/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket.adoc +++ b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket.adoc @@ -29,7 +29,6 @@ The standard APIs provide few features that are not present in the Jetty WebSock On the other hand, the Jetty WebSocket APIs are more efficient and offer greater and more fine-grained control, and provide features that are not present in the standard APIs: -* `MessageSink` for advanced, asynchronous, message streaming with backpressure. * Suspend/resume to control backpressure. * Remote socket address (IP address and port) information. * WebSocket upgrade handling via Filter or Servlet.