Skip to content

Fix #340 - Clarify extension handling #530

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion api/client/src/main/java/jakarta/websocket/ClientEndpoint.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2019 Oracle and/or its affiliates and others.
* Copyright (c) 2018, 2025 Oracle and/or its affiliates and others.
* All rights reserved.
*
* This program and the accompanying materials are made available under the
Expand Down Expand Up @@ -78,4 +78,14 @@
* annotation.
*/
public Class<? extends ClientEndpointConfig.Configurator> configurator() default ClientEndpointConfig.Configurator.class;

/**
* The list of extension configurations that the client would like to use if supported by both the client container
* and the server container. Extensions will be used in the order they are listed. The same extension may appear
* multiple times with different configurations. Where an extension is listed with multiple configurations, the
* configurations must be provided in preference order.
*
* @return The list of extensions the client would like to use
*/
ExtensionConfig[] extensionConfigs() default {};
}
72 changes: 72 additions & 0 deletions api/client/src/main/java/jakarta/websocket/ExtensionConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright (c) 2025 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package jakarta.websocket;

/**
* Defines the configuration of an extension for an endpoint.
* <p>
* For a client endpoint, this represents an extension configuration that the client would like to use if supported by
* both the client container and the server container. Extensions will be used in the order they are listed. The same
* extension may appear multiple times with different configurations. Where an extension is listed with multiple
* configurations, the configurations must be provided in preference order.
* <p>
* For a server endpoint, this represents an extension configuration that the server is prepared to accept if the client
* requests it and the server container supports it. If the extension name is prefaced with {@code &lt;}, it represents
* an extension that the server does not wish to accept regardless of whether the client requests it and/or the server
* container supports it.
*/
public @interface ExtensionConfig {

/**
* The name of the extension.
* <p>
* For server endpoints only, the name may be prefaced by a single {@code &lt;} character to signal any client
* requests for that extension should be rejected regardless of whether the server container supports the extension
* or not.
*
* @return the name of the extension
*/
String name();

/**
* The parameters associated with this configuration.
* <p>
* If a parameter associated with the extension is not defined, it means that any value is acceptable to the
* endpoint for that parameter.
*
* @return the parameters associated with this configuration, if any
*/
Parameter[] parameters() default {};

@interface Parameter {

/**
* The name of the parameter.
*
* @return the parameter name
*/
String name();

/**
* The value of the parameter.
* <p>
* Parameters that have not been set or have not had an explicit value defined will return an empty string.
*
* @return the parameter value
*/
String value() default "";
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2024 Oracle and/or its affiliates and others.
* Copyright (c) 2018, 2025 Oracle and/or its affiliates and others.
* All rights reserved.
*
* This program and the accompanying materials are made available under the
Expand All @@ -23,6 +23,7 @@
import java.lang.annotation.Target;
import jakarta.websocket.Decoder;
import jakarta.websocket.Encoder;
import jakarta.websocket.ExtensionConfig;

/**
* This class level annotation declares that the class it decorates is a web socket endpoint that will be deployed and
Expand Down Expand Up @@ -108,4 +109,15 @@
* annotation.
*/
public Class<? extends ServerEndpointConfig.Configurator> configurator() default ServerEndpointConfig.Configurator.class;

/**
* The list of extension configurations that the server is prepared to accept if the client requests one and the
* server container supports it. If the extension name is prefaced with {@code &lt;}, it represents an extension
* that the server does not wish to accept regardless of whether the client requests it and/or the server container
* supports it. Listing the same extension with and without the {@code &lt;} prefix is not valid and will trigger an
* error on deployment.
*
* @return the list of extensions configurations the server is prepared to accept
*/
ExtensionConfig[] extensionConfigs() default {};
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ public interface ServerEndpointConfig extends EndpointConfig {
List<String> getSubprotocols();

/**
* Return the websocket extensions configured.
* Return the websocket extensions configured. If non-empty, the extensions available to WebSocket clients will be
* the intersection of the configured extensions and those supported by the server. If this list is empty, all
* extensions supported by the server will be available to clients.
*
* @return the list of extensions, the empty list if none.
*/
Expand Down Expand Up @@ -137,15 +139,20 @@ public String getNegotiatedSubprotocol(List<String> supported, List<String> requ
}

/**
* Return the ordered list of extensions that t server endpoint will support given the requested extension list
* passed in, the empty list if none. See <a href="http://tools.ietf.org/html/rfc6455#section-9.1">Negotiating
* Extensions</a>
* Return the ordered list of extensions that the server endpoint will support given the requested extension
* list passed in, the empty list if none. See
* <a href="http://tools.ietf.org/html/rfc6455#section-9.1">Negotiating Extensions</a>
*
* <p>
* The default platform implementation of this method returns a list containing all of the requested extensions
* passed to this method that it supports, using the order in the requested extensions, the empty list if none.
* passed to this method less any extensions where the extension is not named in the installed extensions passed
* to this method. The results are in the order they appeared in the requested extensions.
*
* @param installed the installed extensions on the implementation.
* @param installed the extensions supported by the server ({@link ServerContainer#getInstalledExtensions()})
* unless the extensions defined for the endpoint ({@link ServerEndpointConfig#getExtensions()}) is a
* non-empty list, in which case this is the intersection (based solely on extension name) of the
* extensions defined for the endpoint and those supported by the server less any extensions defined
* for the endpoint where the name is prefixed by `&lt;`
* @param requested the requested extensions, in the order they were requested by the client
* @return the list of extensions negotiated, the empty list if none.
*/
Expand Down Expand Up @@ -330,6 +337,7 @@ public ServerEndpointConfig.Builder subprotocols(List<String> subprotocols) {
* Sets the extensions to use in the configuration.
*
* @param extensions the extensions to use.
*
* @return this builder instance.
*/
public ServerEndpointConfig.Builder extensions(List<Extension> extensions) {
Expand Down
90 changes: 82 additions & 8 deletions spec/src/main/asciidoc/WebSocket.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -593,10 +593,44 @@ match [WSC-3.1.2-1].
[[extension-modification]]
==== Extension Modification

In the opening handshake, the client supplies a list of extensions that
it would like to use. The default server configuration selects from
those extensions the ones it supports, and places them in the same order
as requested by the client [WSC-3.1.3-1].
The starting point to determine the extensions and parameters the default server
configuration must return in the opening handshake response is the list of
extensions and parameters providing by the client in the opening handshake. The
order in which extensions are presented in the opening handshake must be
maintained as the order determines the order in which the extensions will
process WebSocket messages.

Any extension whose name is returned from `ServerEndpointConfig.getExtensions()`
where the name is prefixed with a `<`, must be excluded from the opening
handshake response.

If `ServerContainer.getInstalledExtensions()` (excluding extensions with names
prefaced with `<`) is a non-empty list, any extension whose name is not
included in the list of extensions returned by
`ServerContainer.getInstalledExtensions()` must be excluded from the opening
handshake response.

The client may include the same extension multiple times. This signifies that
different configurations of the extension are acceptable with the variations
listed in preference order from most preferred to least preferred. For each
remaining extension, the server selects its preferred configuration from the
configurations provided by the client. If allowed by the extension's
specification, the server may add or remove extension parameters to/from the
client provided configuration.

The server includes its chosen configuration for each requested extension in the
opening handshake response. If none of the requested configurations for an
extension are acceptable to the server, the extension is not included in the
opening handshake response. The opening handshake response is the final,
authoritative extension configuration for the WebSocket session. [WSC-3.1.3-1].

Determination of acceptable parameters is extension implementation specific.

Extensions are always optional in the WebSocket protocol. If a server wishes to
require that an extension is present, it may provide a custom
`ServerEndpointConfig.Configurator`, override `getNegotiatedExtensions` and fail
the handshake, e.g. by throwing an `Exception`, if the required extension(s)
is(are) not present.

[[origin-check]]
==== Origin Check
Expand Down Expand Up @@ -673,10 +707,29 @@ formulates [WSC-3.2.1-1].
[[extensions]]
==== Extensions

The default client configuration must use the developer provided list of
extensions to send, in order of preference, the extensions, including
parameters, that it would like to use in the opening handshake it
formulates [WSC-3.2.2-1].
The starting point to determine the extensions and parameters the default client
configuration must request in the opening handshake request is the developer
provided list of extensions and parameters for the endpoint. The order in which
extensions are specified must be maintained as the order determines the order in
which the extensions will process WebSocket messages.

Any extension whose name is not included in the list of extensions returned by
`WebSocketContainer.getInstalledExtensions()` must be excluded from the opening
handshake request. Implementation and configuration of supported extensions is
container specific.

The developer provided list may include an extension multiple times. This
signifies that different configurations of the extension are acceptable with the
variations listed in preference order from most preferred to least preferred.

The default client configuration must use the remaining extensions, in order of
preference and including parameters, in the opening handshake it formulates
[WSC-3.2.2-1].

Extensions are always optional in the WebSocket protocol. If a client wishes to
require that an extension is present, it may examine the handshake response via
`ClientEndpointConfig.Configurator.afterResponse()` and fail the handshake, e.g.
by throwing an `Exception`, if the required extension(s) is(are) not present.

[[sslcontext]]
==== SSLContext
Expand Down Expand Up @@ -825,6 +878,16 @@ the endpoint [WSC-4.1.5-1]. The developer may use this technique to
share state across all instances of the endpoint in addition to
customizing the opening handshake.

==== extensionConfigs

The optional extensionConfigs attribute allows the developer to indicate which
WebSocket extensions this endpoint is prepared to accept if the extension is
requested by the client and it is supported by the server container. If an
extension name is prefaced with `<` that indicates that the extension must not
be used regardless of whether the client requests it and/or the server container
supports it. Listing the same extension with and without the `<` prefix is not
valid and will trigger a `DeploymentException` on deployment.

[[clientendpoint]]
=== @ClientEndpoint

Expand Down Expand Up @@ -885,6 +948,17 @@ implementation must use this list in the opening handshake to negotiate
the desired subprotocol to use for the connection it establishes
[WSC-4.2.4-1].

==== extensionConfigs

The optional extensionConfigs attribute allows the developer to indicate which
WebSocket extensions this endpoint would like to request if the extension is
supported by the client and server containers and permitted by the server
endpoint. Extensions should be listed in the order they should be used. The same
extension may appear multiple times with different configurations. Where an
extension is listed with multiple configurations, the configurations must be
provided in preference order. Extensions should be listed in preference order
and the same extension may appear multiple times with different configurations.

[[pathparam]]
=== @PathParam

Expand Down