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[]