diff --git a/documentation/jetty-documentation/pom.xml b/documentation/jetty-documentation/pom.xml
index bc7e62058411..eb6358c97ed1 100644
--- a/documentation/jetty-documentation/pom.xml
+++ b/documentation/jetty-documentation/pom.xml
@@ -209,6 +209,7 @@
org.eclipse.jetty
jetty-slf4j-impl
${project.version}
+ runtime
org.eclipse.jetty.memcached
@@ -225,5 +226,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..bcd2ad62bfbd
--- /dev/null
+++ b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-filter.adoc
@@ -0,0 +1,82 @@
+//
+// ========================================================================
+// 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
+
+The `WebSocketUpgradeFilter` that handles the HTTP requests that upgrade to WebSocket is installed in these cases:
+
+* 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.
+
+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..d04e3fc84485
--- /dev/null
+++ b/documentation/jetty-documentation/src/main/asciidoc/programming-guide/server/websocket/server-websocket-jetty.adoc
@@ -0,0 +1,177 @@
+//
+// ========================================================================
+// 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 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:
+
+[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`.
+. 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`
+
+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`].
+
+[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 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`
+
+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, 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 (or even many different `JettyWebSocketServlet` subclasses).
+
+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`).
+
+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, your `JettyWebSocketServlet` subclass may be declared in code in this way:
+
+[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.
+
+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 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, as per the standard `HttpServlet` class implementation.
+
+[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.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
new file mode 100644
index 000000000000..3d69d07cdf7c
--- /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`.
+. 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`
+
+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-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`:
+
+[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 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, 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 2bc35d4b8683..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
@@ -12,6 +12,32 @@
//
[[pg-server-websocket]]
-=== WebSocket Server Libraries
+=== WebSocket Server
-TODO
+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:
+
+* 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..51b24422c22c
--- /dev/null
+++ b/documentation/jetty-documentation/src/main/java/org/eclipse/jetty/docs/programming/server/websocket/WebSocketServerDocs.java
@@ -0,0 +1,351 @@
+//
+// ========================================================================
+// 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.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;
+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.JettyWebSocketCreator;
+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, 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", (upgradeRequest, upgradeResponse) ->
+ 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, 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[]
+ }
+
+ @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[]
+ // 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", (upgradeRequest, 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-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)