Drop server-specific RequestUpgradeStrategy implementations

StandardWebSocketUpgradeStrategy is the common replacement on Tomcat, Undertow and all EE servers. JettyRequestUpgradeStrategy remains the preferred choice on Jetty.

Closes gh-33744
This commit is contained in:
Juergen Hoeller 2024-12-03 19:08:22 +01:00
parent 342369355d
commit 3f3341bb1d
19 changed files with 130 additions and 1085 deletions

View File

@ -123,7 +123,6 @@ dependencies {
api("org.freemarker:freemarker:2.3.33")
api("org.glassfish.external:opendmk_jmxremote_optional_jar:1.0-b01-ea")
api("org.glassfish:jakarta.el:4.0.2")
api("org.glassfish.tyrus:tyrus-container-servlet:2.1.3")
api("org.graalvm.sdk:graal-sdk:22.3.1")
api("org.hamcrest:hamcrest:2.2")
api("org.hibernate:hibernate-core:7.0.0.Beta2")

View File

@ -12,19 +12,11 @@ dependencies {
optional("jakarta.servlet:jakarta.servlet-api")
optional("jakarta.websocket:jakarta.websocket-api")
optional("jakarta.websocket:jakarta.websocket-client-api")
optional("org.apache.tomcat:tomcat-websocket") {
exclude group: "org.apache.tomcat", module: "tomcat-servlet-api"
exclude group: "org.apache.tomcat", module: "tomcat-websocket-api"
}
optional("org.eclipse.jetty.ee10:jetty-ee10-webapp") {
exclude group: "jakarta.servlet", module: "jakarta.servlet-api"
}
optional("org.eclipse.jetty.websocket:jetty-websocket-jetty-api")
optional("org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-jakarta-server")
optional("org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-jetty-server") {
exclude group: "jakarta.servlet", module: "jakarta.servlet-api"
}
optional("org.glassfish.tyrus:tyrus-container-servlet")
optional("org.eclipse.jetty.websocket:jetty-websocket-jetty-api")
optional("org.eclipse.jetty:jetty-client")
testImplementation(testFixtures(project(":spring-core")))
testImplementation(testFixtures(project(":spring-web")))
testImplementation("io.projectreactor.netty:reactor-netty-http")

View File

@ -1,143 +0,0 @@
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.server.standard;
import java.net.InetSocketAddress;
import java.security.Principal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.websocket.Endpoint;
import jakarta.websocket.Extension;
import jakarta.websocket.WebSocketContainer;
import jakarta.websocket.server.ServerContainer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.socket.WebSocketExtension;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.adapter.standard.StandardToWebSocketExtensionAdapter;
import org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter;
import org.springframework.web.socket.adapter.standard.StandardWebSocketSession;
import org.springframework.web.socket.adapter.standard.WebSocketToStandardExtensionAdapter;
import org.springframework.web.socket.server.HandshakeFailureException;
import org.springframework.web.socket.server.RequestUpgradeStrategy;
/**
* A base class for {@link RequestUpgradeStrategy} implementations that build
* on the standard WebSocket API for Java (JSR-356).
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public abstract class AbstractStandardUpgradeStrategy implements RequestUpgradeStrategy {
protected final Log logger = LogFactory.getLog(getClass());
@Nullable
private volatile List<WebSocketExtension> extensions;
protected ServerContainer getContainer(HttpServletRequest request) {
ServletContext servletContext = request.getServletContext();
String attrName = "jakarta.websocket.server.ServerContainer";
ServerContainer container = (ServerContainer) servletContext.getAttribute(attrName);
Assert.notNull(container, "No 'jakarta.websocket.server.ServerContainer' ServletContext attribute. " +
"Are you running in a Servlet container that supports JSR-356?");
return container;
}
protected final HttpServletRequest getHttpServletRequest(ServerHttpRequest request) {
Assert.isInstanceOf(ServletServerHttpRequest.class, request, "ServletServerHttpRequest required");
return ((ServletServerHttpRequest) request).getServletRequest();
}
protected final HttpServletResponse getHttpServletResponse(ServerHttpResponse response) {
Assert.isInstanceOf(ServletServerHttpResponse.class, response, "ServletServerHttpResponse required");
return ((ServletServerHttpResponse) response).getServletResponse();
}
@Override
public List<WebSocketExtension> getSupportedExtensions(ServerHttpRequest request) {
List<WebSocketExtension> extensions = this.extensions;
if (extensions == null) {
HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
extensions = getInstalledExtensions(getContainer(servletRequest));
this.extensions = extensions;
}
return extensions;
}
protected List<WebSocketExtension> getInstalledExtensions(WebSocketContainer container) {
List<WebSocketExtension> result = new ArrayList<>();
for (Extension extension : container.getInstalledExtensions()) {
result.add(new StandardToWebSocketExtensionAdapter(extension));
}
return result;
}
@Override
public void upgrade(ServerHttpRequest request, ServerHttpResponse response,
@Nullable String selectedProtocol, List<WebSocketExtension> selectedExtensions,
@Nullable Principal user, WebSocketHandler wsHandler, Map<String, Object> attrs)
throws HandshakeFailureException {
HttpHeaders headers = request.getHeaders();
InetSocketAddress localAddr = null;
try {
localAddr = request.getLocalAddress();
}
catch (Exception ex) {
// Ignore
}
InetSocketAddress remoteAddr = null;
try {
remoteAddr = request.getRemoteAddress();
}
catch (Exception ex) {
// Ignore
}
StandardWebSocketSession session = new StandardWebSocketSession(headers, attrs, localAddr, remoteAddr, user);
StandardWebSocketHandlerAdapter endpoint = new StandardWebSocketHandlerAdapter(wsHandler, session);
List<Extension> extensions = new ArrayList<>();
for (WebSocketExtension extension : selectedExtensions) {
extensions.add(new WebSocketToStandardExtensionAdapter(extension));
}
upgradeInternal(request, response, selectedProtocol, extensions, endpoint);
}
protected abstract void upgradeInternal(ServerHttpRequest request, ServerHttpResponse response,
@Nullable String selectedProtocol, List<Extension> selectedExtensions, Endpoint endpoint)
throws HandshakeFailureException;
}

View File

@ -1,248 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.server.standard;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.websocket.DeploymentException;
import jakarta.websocket.Endpoint;
import jakarta.websocket.EndpointConfig;
import jakarta.websocket.Extension;
import jakarta.websocket.WebSocketContainer;
import org.glassfish.tyrus.core.ComponentProviderService;
import org.glassfish.tyrus.core.RequestContext;
import org.glassfish.tyrus.core.TyrusEndpointWrapper;
import org.glassfish.tyrus.core.TyrusUpgradeResponse;
import org.glassfish.tyrus.core.TyrusWebSocketEngine;
import org.glassfish.tyrus.core.Version;
import org.glassfish.tyrus.server.TyrusServerContainer;
import org.glassfish.tyrus.spi.WebSocketEngine.UpgradeInfo;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.WebSocketExtension;
import org.springframework.web.socket.server.HandshakeFailureException;
import static org.glassfish.tyrus.spi.WebSocketEngine.UpgradeStatus.SUCCESS;
/**
* A base class for {@code RequestUpgradeStrategy} implementations on top of
* JSR-356 based servers which include Tyrus as their WebSocket engine.
*
* @author Rossen Stoyanchev
* @author Brian Clozel
* @author Juergen Hoeller
* @since 4.1
* @see <a href="https://tyrus.java.net/">Project Tyrus</a>
*/
public abstract class AbstractTyrusRequestUpgradeStrategy extends AbstractStandardUpgradeStrategy {
private static final String[] SUPPORTED_VERSIONS =
StringUtils.tokenizeToStringArray(Version.getSupportedWireProtocolVersions(), ",");
private static final Random random = new Random();
private static final Constructor<?> constructor;
private static final boolean constructorWithBooleanArgument;
private static final Method registerMethod;
private static final Method unRegisterMethod;
static {
try {
constructor = getEndpointConstructor();
int parameterCount = constructor.getParameterCount();
constructorWithBooleanArgument = (parameterCount == 10);
if (!constructorWithBooleanArgument && parameterCount != 9) {
throw new IllegalStateException("Expected TyrusEndpointWrapper constructor with 9 or 10 arguments");
}
registerMethod = TyrusWebSocketEngine.class.getDeclaredMethod("register", TyrusEndpointWrapper.class);
unRegisterMethod = TyrusWebSocketEngine.class.getDeclaredMethod("unregister", TyrusEndpointWrapper.class);
ReflectionUtils.makeAccessible(registerMethod);
}
catch (Exception ex) {
throw new IllegalStateException("No compatible Tyrus version found", ex);
}
}
private static Constructor<?> getEndpointConstructor() {
for (Constructor<?> current : TyrusEndpointWrapper.class.getConstructors()) {
Class<?>[] types = current.getParameterTypes();
if (Endpoint.class == types[0] && EndpointConfig.class == types[1]) {
return current;
}
}
throw new IllegalStateException("No compatible Tyrus version found");
}
private final ComponentProviderService componentProvider = ComponentProviderService.create();
@Override
public String[] getSupportedVersions() {
return SUPPORTED_VERSIONS;
}
@Override
protected List<WebSocketExtension> getInstalledExtensions(WebSocketContainer container) {
try {
return super.getInstalledExtensions(container);
}
catch (UnsupportedOperationException ex) {
return new ArrayList<>(0);
}
}
@Override
public void upgradeInternal(ServerHttpRequest request, ServerHttpResponse response,
@Nullable String selectedProtocol, List<Extension> extensions, Endpoint endpoint)
throws HandshakeFailureException {
HttpServletRequest servletRequest = getHttpServletRequest(request);
HttpServletResponse servletResponse = getHttpServletResponse(response);
TyrusServerContainer serverContainer = (TyrusServerContainer) getContainer(servletRequest);
TyrusWebSocketEngine engine = (TyrusWebSocketEngine) serverContainer.getWebSocketEngine();
Object tyrusEndpoint = null;
boolean success;
try {
// Shouldn't matter for processing but must be unique
String path = "/" + random.nextLong();
tyrusEndpoint = createTyrusEndpoint(endpoint, path, selectedProtocol, extensions, serverContainer, engine);
register(engine, tyrusEndpoint);
HttpHeaders headers = request.getHeaders();
RequestContext requestContext = createRequestContext(servletRequest, path, headers);
TyrusUpgradeResponse upgradeResponse = new TyrusUpgradeResponse();
UpgradeInfo upgradeInfo = engine.upgrade(requestContext, upgradeResponse);
success = SUCCESS.equals(upgradeInfo.getStatus());
if (success) {
if (logger.isTraceEnabled()) {
logger.trace("Successful request upgrade: " + upgradeResponse.getHeaders());
}
handleSuccess(servletRequest, servletResponse, upgradeInfo, upgradeResponse);
}
}
catch (Exception ex) {
unregisterTyrusEndpoint(engine, tyrusEndpoint);
throw new HandshakeFailureException("Error during handshake: " + request.getURI(), ex);
}
unregisterTyrusEndpoint(engine, tyrusEndpoint);
if (!success) {
throw new HandshakeFailureException("Unexpected handshake failure: " + request.getURI());
}
}
private Object createTyrusEndpoint(Endpoint endpoint, String endpointPath, @Nullable String protocol,
List<Extension> extensions, WebSocketContainer container, TyrusWebSocketEngine engine)
throws DeploymentException {
ServerEndpointRegistration endpointConfig = new ServerEndpointRegistration(endpointPath, endpoint);
endpointConfig.setSubprotocols(Collections.singletonList(protocol));
endpointConfig.setExtensions(extensions);
return createEndpoint(endpointConfig, this.componentProvider, container, engine);
}
private RequestContext createRequestContext(HttpServletRequest request, String endpointPath, HttpHeaders headers) {
RequestContext context =
RequestContext.Builder.create()
.requestURI(URI.create(endpointPath))
.userPrincipal(request.getUserPrincipal())
.secure(request.isSecure())
.remoteAddr(request.getRemoteAddr())
.build();
headers.forEach((header, value) -> context.getHeaders().put(header, value));
return context;
}
private void unregisterTyrusEndpoint(TyrusWebSocketEngine engine, @Nullable Object tyrusEndpoint) {
if (tyrusEndpoint != null) {
try {
unregister(engine, tyrusEndpoint);
}
catch (Throwable ex) {
// ignore
}
}
}
private Object createEndpoint(ServerEndpointRegistration registration, ComponentProviderService provider,
WebSocketContainer container, TyrusWebSocketEngine engine) throws DeploymentException {
DirectFieldAccessor accessor = new DirectFieldAccessor(engine);
Object sessionListener = accessor.getPropertyValue("sessionListener");
Object clusterContext = accessor.getPropertyValue("clusterContext");
try {
if (constructorWithBooleanArgument) {
// Tyrus 1.11+
return constructor.newInstance(registration.getEndpoint(), registration, provider, container,
"/", registration.getConfigurator(), sessionListener, clusterContext, null, Boolean.TRUE);
}
else {
return constructor.newInstance(registration.getEndpoint(), registration, provider, container,
"/", registration.getConfigurator(), sessionListener, clusterContext, null);
}
}
catch (Exception ex) {
throw new HandshakeFailureException("Failed to register " + registration, ex);
}
}
private void register(TyrusWebSocketEngine engine, Object endpoint) {
try {
registerMethod.invoke(engine, endpoint);
}
catch (Exception ex) {
throw new HandshakeFailureException("Failed to register " + endpoint, ex);
}
}
private void unregister(TyrusWebSocketEngine engine, Object endpoint) {
try {
unRegisterMethod.invoke(engine, endpoint);
}
catch (Exception ex) {
throw new HandshakeFailureException("Failed to unregister " + endpoint, ex);
}
}
protected abstract void handleSuccess(HttpServletRequest request, HttpServletResponse response,
UpgradeInfo upgradeInfo, TyrusUpgradeResponse upgradeResponse) throws IOException, ServletException;
}

View File

@ -1,81 +0,0 @@
/*
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.server.standard;
import java.io.IOException;
import java.lang.reflect.Constructor;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.glassfish.tyrus.core.TyrusUpgradeResponse;
import org.glassfish.tyrus.core.Utils;
import org.glassfish.tyrus.servlet.TyrusHttpUpgradeHandler;
import org.glassfish.tyrus.spi.WebSocketEngine.UpgradeInfo;
import org.glassfish.tyrus.spi.Writer;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.socket.server.HandshakeFailureException;
/**
* A WebSocket {@code RequestUpgradeStrategy} for Oracle's GlassFish 4.1 and higher.
*
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @author Michael Irwin
* @since 4.0
*/
public class GlassFishRequestUpgradeStrategy extends AbstractTyrusRequestUpgradeStrategy {
private static final Constructor<?> constructor;
static {
try {
ClassLoader classLoader = GlassFishRequestUpgradeStrategy.class.getClassLoader();
Class<?> type = classLoader.loadClass("org.glassfish.tyrus.servlet.TyrusServletWriter");
constructor = type.getDeclaredConstructor(TyrusHttpUpgradeHandler.class);
ReflectionUtils.makeAccessible(constructor);
}
catch (Exception ex) {
throw new IllegalStateException("No compatible Tyrus version found", ex);
}
}
@Override
protected void handleSuccess(HttpServletRequest request, HttpServletResponse response,
UpgradeInfo upgradeInfo, TyrusUpgradeResponse upgradeResponse) throws IOException, ServletException {
TyrusHttpUpgradeHandler handler = request.upgrade(TyrusHttpUpgradeHandler.class);
Writer servletWriter = newServletWriter(handler);
handler.preInit(upgradeInfo, servletWriter, request.getUserPrincipal() != null);
response.setStatus(upgradeResponse.getStatus());
upgradeResponse.getHeaders().forEach((key, value) -> response.addHeader(key, Utils.getHeaderFromList(value)));
response.flushBuffer();
}
private Writer newServletWriter(TyrusHttpUpgradeHandler handler) {
try {
return (Writer) constructor.newInstance(handler);
}
catch (Exception ex) {
throw new HandshakeFailureException("Failed to instantiate TyrusServletWriter", ex);
}
}
}

View File

@ -16,28 +16,39 @@
package org.springframework.web.socket.server.standard;
import java.net.InetSocketAddress;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.websocket.Endpoint;
import jakarta.websocket.Extension;
import jakarta.websocket.server.ServerEndpointConfig;
import jakarta.websocket.WebSocketContainer;
import jakarta.websocket.server.ServerContainer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.socket.WebSocketExtension;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.adapter.standard.StandardToWebSocketExtensionAdapter;
import org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter;
import org.springframework.web.socket.adapter.standard.StandardWebSocketSession;
import org.springframework.web.socket.adapter.standard.WebSocketToStandardExtensionAdapter;
import org.springframework.web.socket.server.HandshakeFailureException;
import org.springframework.web.socket.server.RequestUpgradeStrategy;
/**
* A WebSocket {@code RequestUpgradeStrategy} for the Jakarta WebSocket API 2.1+.
*
* <p>This strategy serves as a fallback if no specific server has been detected.
* It can also be used with Jakarta EE 10 level servers such as Tomcat 10.1 and
* Undertow 2.3 directly, relying on their built-in Jakarta WebSocket 2.1 support.
*
* <p>To modify properties of the underlying {@link jakarta.websocket.server.ServerContainer}
* you can use {@link ServletServerContainerFactoryBean} in XML configuration or,
* when using Java configuration, access the container instance through the
@ -48,10 +59,13 @@ import org.springframework.web.socket.server.HandshakeFailureException;
* @since 6.0
* @see jakarta.websocket.server.ServerContainer#upgradeHttpToWebSocket
*/
public class StandardWebSocketUpgradeStrategy extends AbstractStandardUpgradeStrategy {
public class StandardWebSocketUpgradeStrategy implements RequestUpgradeStrategy {
private static final String[] SUPPORTED_VERSIONS = new String[] {"13"};
@Nullable
private volatile List<WebSocketExtension> extensions;
@Override
public String[] getSupportedVersions() {
@ -59,10 +73,55 @@ public class StandardWebSocketUpgradeStrategy extends AbstractStandardUpgradeStr
}
@Override
protected void upgradeInternal(ServerHttpRequest request, ServerHttpResponse response,
@Nullable String selectedProtocol, List<Extension> selectedExtensions, Endpoint endpoint)
public List<WebSocketExtension> getSupportedExtensions(ServerHttpRequest request) {
List<WebSocketExtension> extensions = this.extensions;
if (extensions == null) {
HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
extensions = getInstalledExtensions(getContainer(servletRequest));
this.extensions = extensions;
}
return extensions;
}
protected List<WebSocketExtension> getInstalledExtensions(WebSocketContainer container) {
List<WebSocketExtension> result = new ArrayList<>();
for (Extension extension : container.getInstalledExtensions()) {
result.add(new StandardToWebSocketExtensionAdapter(extension));
}
return result;
}
@Override
public void upgrade(ServerHttpRequest request, ServerHttpResponse response,
@Nullable String selectedProtocol, List<WebSocketExtension> selectedExtensions,
@Nullable Principal user, WebSocketHandler wsHandler, Map<String, Object> attrs)
throws HandshakeFailureException {
HttpHeaders headers = request.getHeaders();
InetSocketAddress localAddr = null;
try {
localAddr = request.getLocalAddress();
}
catch (Exception ex) {
// Ignore
}
InetSocketAddress remoteAddr = null;
try {
remoteAddr = request.getRemoteAddress();
}
catch (Exception ex) {
// Ignore
}
StandardWebSocketSession session = new StandardWebSocketSession(headers, attrs, localAddr, remoteAddr, user);
StandardWebSocketHandlerAdapter endpoint = new StandardWebSocketHandlerAdapter(wsHandler, session);
List<Extension> extensions = new ArrayList<>();
for (WebSocketExtension extension : selectedExtensions) {
extensions.add(new WebSocketToStandardExtensionAdapter(extension));
}
HttpServletRequest servletRequest = getHttpServletRequest(request);
HttpServletResponse servletResponse = getHttpServletResponse(response);
@ -72,21 +131,35 @@ public class StandardWebSocketUpgradeStrategy extends AbstractStandardUpgradeStr
ServerEndpointRegistration endpointConfig = new ServerEndpointRegistration(path, endpoint);
endpointConfig.setSubprotocols(Collections.singletonList(selectedProtocol));
endpointConfig.setExtensions(selectedExtensions);
endpointConfig.setExtensions(extensions);
try {
upgradeHttpToWebSocket(servletRequest, servletResponse, endpointConfig, pathParams);
getContainer(servletRequest).upgradeHttpToWebSocket(
servletRequest, servletResponse, endpointConfig, pathParams);
}
catch (Exception ex) {
throw new HandshakeFailureException(
"Servlet request failed to upgrade to WebSocket: " + requestUrl, ex);
throw new HandshakeFailureException("Servlet request failed to upgrade to WebSocket: " + requestUrl, ex);
}
}
protected void upgradeHttpToWebSocket(HttpServletRequest request, HttpServletResponse response,
ServerEndpointConfig endpointConfig, Map<String,String> pathParams) throws Exception {
getContainer(request).upgradeHttpToWebSocket(request, response, endpointConfig, pathParams);
protected ServerContainer getContainer(HttpServletRequest request) {
ServletContext servletContext = request.getServletContext();
String attrName = "jakarta.websocket.server.ServerContainer";
ServerContainer container = (ServerContainer) servletContext.getAttribute(attrName);
Assert.notNull(container, "No 'jakarta.websocket.server.ServerContainer' ServletContext attribute. " +
"Are you running in a Servlet container that supports JSR-356?");
return container;
}
protected final HttpServletRequest getHttpServletRequest(ServerHttpRequest request) {
Assert.isInstanceOf(ServletServerHttpRequest.class, request, "ServletServerHttpRequest required");
return ((ServletServerHttpRequest) request).getServletRequest();
}
protected final HttpServletResponse getHttpServletResponse(ServerHttpResponse response) {
Assert.isInstanceOf(ServletServerHttpResponse.class, response, "ServletServerHttpResponse required");
return ((ServletServerHttpResponse) response).getServletResponse();
}
}

View File

@ -1,50 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.server.standard;
import java.util.Map;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.websocket.server.ServerEndpointConfig;
import org.apache.tomcat.websocket.server.WsServerContainer;
/**
* A WebSocket {@code RequestUpgradeStrategy} for Apache Tomcat. Compatible with Tomcat 10
* and higher, in particular with Tomcat 10.0 (not based on Jakarta WebSocket 2.1 yet).
*
* <p>To modify properties of the underlying {@link jakarta.websocket.server.ServerContainer}
* you can use {@link ServletServerContainerFactoryBean} in XML configuration or,
* when using Java configuration, access the container instance through the
* "jakarta.websocket.server.ServerContainer" ServletContext attribute.
*
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 4.0
* @see org.apache.tomcat.websocket.server.WsServerContainer#upgradeHttpToWebSocket
*/
public class TomcatRequestUpgradeStrategy extends StandardWebSocketUpgradeStrategy {
@Override
protected void upgradeHttpToWebSocket(HttpServletRequest request, HttpServletResponse response,
ServerEndpointConfig endpointConfig, Map<String, String> pathParams) throws Exception {
((WsServerContainer) getContainer(request)).upgradeHttpToWebSocket(
request, response, endpointConfig, pathParams);
}
}

View File

@ -1,56 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.server.standard;
import java.util.Map;
import io.undertow.websockets.jsr.ServerWebSocketContainer;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.websocket.server.ServerEndpointConfig;
/**
* A WebSocket {@code RequestUpgradeStrategy} for WildFly and its underlying
* Undertow web server. Also compatible with embedded Undertow usage.
*
* <p>Designed for Undertow 2.2, also compatible with Undertow 2.3
* (which implements Jakarta WebSocket 2.1 as well).
*
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 4.0.1
* @see io.undertow.websockets.jsr.ServerWebSocketContainer#doUpgrade
*/
public class UndertowRequestUpgradeStrategy extends StandardWebSocketUpgradeStrategy {
private static final String[] SUPPORTED_VERSIONS = new String[] {"13", "8", "7"};
@Override
public String[] getSupportedVersions() {
return SUPPORTED_VERSIONS;
}
@Override
protected void upgradeHttpToWebSocket(HttpServletRequest request, HttpServletResponse response,
ServerEndpointConfig endpointConfig, Map<String, String> pathParams) throws Exception {
((ServerWebSocketContainer) getContainer(request)).doUpgrade(
request, response, endpointConfig, pathParams);
}
}

View File

@ -1,244 +0,0 @@
/*
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.server.standard;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import jakarta.servlet.AsyncContext;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletRequestWrapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.glassfish.tyrus.core.TyrusUpgradeResponse;
import org.glassfish.tyrus.core.Utils;
import org.glassfish.tyrus.spi.Connection;
import org.glassfish.tyrus.spi.WebSocketEngine.UpgradeInfo;
import org.glassfish.tyrus.spi.Writer;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.socket.server.HandshakeFailureException;
/**
* A WebSocket {@code RequestUpgradeStrategy} for Oracle's WebLogic.
* Supports 12.1.3 as well as 12.2.1, as of Spring Framework 4.2.3.
*
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 4.1
*/
public class WebLogicRequestUpgradeStrategy extends AbstractTyrusRequestUpgradeStrategy {
private static final TyrusMuxableWebSocketHelper webSocketHelper = new TyrusMuxableWebSocketHelper();
private static final WebLogicServletWriterHelper servletWriterHelper = new WebLogicServletWriterHelper();
private static final Connection.CloseListener noOpCloseListener = (reason -> {});
@Override
protected void handleSuccess(HttpServletRequest request, HttpServletResponse response,
UpgradeInfo upgradeInfo, TyrusUpgradeResponse upgradeResponse) throws IOException, ServletException {
response.setStatus(upgradeResponse.getStatus());
upgradeResponse.getHeaders().forEach((key, value) -> response.addHeader(key, Utils.getHeaderFromList(value)));
AsyncContext asyncContext = request.startAsync();
asyncContext.setTimeout(-1L);
Object nativeRequest = getNativeRequest(request);
BeanWrapper beanWrapper = new BeanWrapperImpl(nativeRequest);
Object httpSocket = beanWrapper.getPropertyValue("connection.connectionHandler.rawConnection");
Object webSocket = webSocketHelper.newInstance(request, httpSocket);
webSocketHelper.upgrade(webSocket, httpSocket, request.getServletContext());
response.flushBuffer();
boolean isProtected = request.getUserPrincipal() != null;
Writer servletWriter = servletWriterHelper.newInstance(webSocket, isProtected);
Connection connection = upgradeInfo.createConnection(servletWriter, noOpCloseListener);
new BeanWrapperImpl(webSocket).setPropertyValue("connection", connection);
new BeanWrapperImpl(servletWriter).setPropertyValue("connection", connection);
webSocketHelper.registerForReadEvent(webSocket);
}
private static Class<?> type(String className) throws ClassNotFoundException {
return WebLogicRequestUpgradeStrategy.class.getClassLoader().loadClass(className);
}
private static Method method(String className, String method, Class<?>... paramTypes)
throws ClassNotFoundException, NoSuchMethodException {
return type(className).getDeclaredMethod(method, paramTypes);
}
private static Object getNativeRequest(ServletRequest request) {
while (request instanceof ServletRequestWrapper wrapper) {
request = wrapper.getRequest();
}
return request;
}
/**
* Helps to create and invoke {@code weblogic.servlet.internal.MuxableSocketHTTP}.
*/
private static class TyrusMuxableWebSocketHelper {
private static final Class<?> type;
private static final Constructor<?> constructor;
private static final SubjectHelper subjectHelper;
private static final Method upgradeMethod;
private static final Method readEventMethod;
static {
try {
type = type("weblogic.websocket.tyrus.TyrusMuxableWebSocket");
constructor = type.getDeclaredConstructor(
type("weblogic.servlet.internal.MuxableSocketHTTP"),
type("weblogic.websocket.tyrus.CoherenceServletFilterService"),
type("weblogic.servlet.spi.SubjectHandle"));
subjectHelper = new SubjectHelper();
upgradeMethod = type.getMethod("upgrade", type("weblogic.socket.MuxableSocket"), ServletContext.class);
readEventMethod = type.getMethod("registerForReadEvent");
}
catch (Exception ex) {
throw new IllegalStateException("No compatible WebSocket version found", ex);
}
}
private Object newInstance(HttpServletRequest request, @Nullable Object httpSocket) {
try {
Object[] args = new Object[] {httpSocket, null, subjectHelper.getSubject(request)};
return constructor.newInstance(args);
}
catch (Exception ex) {
throw new HandshakeFailureException("Failed to create TyrusMuxableWebSocket", ex);
}
}
private void upgrade(Object webSocket, @Nullable Object httpSocket, ServletContext servletContext) {
try {
upgradeMethod.invoke(webSocket, httpSocket, servletContext);
}
catch (Exception ex) {
throw new HandshakeFailureException("Failed to upgrade TyrusMuxableWebSocket", ex);
}
}
private void registerForReadEvent(Object webSocket) {
try {
readEventMethod.invoke(webSocket);
}
catch (Exception ex) {
throw new HandshakeFailureException("Failed to register WebSocket for read event", ex);
}
}
}
private static class SubjectHelper {
private final Method securityContextMethod;
private final Method currentUserMethod;
private final Method providerMethod;
private final Method anonymousSubjectMethod;
public SubjectHelper() {
try {
String className = "weblogic.servlet.internal.WebAppServletContext";
this.securityContextMethod = method(className, "getSecurityContext");
className = "weblogic.servlet.security.internal.SecurityModule";
this.currentUserMethod = method(className, "getCurrentUser",
type("weblogic.servlet.security.internal.ServletSecurityContext"),
HttpServletRequest.class);
className = "weblogic.servlet.security.internal.WebAppSecurity";
this.providerMethod = method(className, "getProvider");
this.anonymousSubjectMethod = this.providerMethod.getReturnType().getDeclaredMethod("getAnonymousSubject");
}
catch (Exception ex) {
throw new IllegalStateException("No compatible WebSocket version found", ex);
}
}
public Object getSubject(HttpServletRequest request) {
try {
ServletContext servletContext = request.getServletContext();
Object securityContext = this.securityContextMethod.invoke(servletContext);
Object subject = this.currentUserMethod.invoke(null, securityContext, request);
if (subject == null) {
Object securityProvider = this.providerMethod.invoke(null);
subject = this.anonymousSubjectMethod.invoke(securityProvider);
}
return subject;
}
catch (Exception ex) {
throw new HandshakeFailureException("Failed to obtain SubjectHandle", ex);
}
}
}
/**
* Helps to create and invoke {@code weblogic.websocket.tyrus.TyrusServletWriter}.
*/
private static class WebLogicServletWriterHelper {
private static final Constructor<?> constructor;
static {
try {
Class<?> writerType = type("weblogic.websocket.tyrus.TyrusServletWriter");
Class<?> listenerType = type("weblogic.websocket.tyrus.TyrusServletWriter$CloseListener");
Class<?> webSocketType = TyrusMuxableWebSocketHelper.type;
constructor = writerType.getDeclaredConstructor(webSocketType, listenerType, boolean.class);
ReflectionUtils.makeAccessible(constructor);
}
catch (Exception ex) {
throw new IllegalStateException("No compatible WebSocket version found", ex);
}
}
private Writer newInstance(Object webSocket, boolean isProtected) {
try {
return (Writer) constructor.newInstance(webSocket, null, isProtected);
}
catch (Exception ex) {
throw new HandshakeFailureException("Failed to create TyrusServletWriter", ex);
}
}
}
}

View File

@ -1,64 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.server.standard;
import java.lang.reflect.Method;
import java.util.Map;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.websocket.server.ServerContainer;
import jakarta.websocket.server.ServerEndpointConfig;
/**
* WebSphere support for upgrading an {@link HttpServletRequest} during a
* WebSocket handshake. To modify properties of the underlying
* {@link jakarta.websocket.server.ServerContainer} you can use
* {@link ServletServerContainerFactoryBean} in XML configuration or, when using
* Java configuration, access the container instance through the
* "javax.websocket.server.ServerContainer" ServletContext attribute.
*
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 4.2.1
*/
public class WebSphereRequestUpgradeStrategy extends StandardWebSocketUpgradeStrategy {
private static final Method upgradeMethod;
static {
ClassLoader loader = WebSphereRequestUpgradeStrategy.class.getClassLoader();
try {
Class<?> type = loader.loadClass("com.ibm.websphere.wsoc.WsWsocServerContainer");
upgradeMethod = type.getMethod("doUpgrade", HttpServletRequest.class,
HttpServletResponse.class, ServerEndpointConfig.class, Map.class);
}
catch (Exception ex) {
throw new IllegalStateException("No compatible WebSphere version found", ex);
}
}
@Override
protected void upgradeHttpToWebSocket(HttpServletRequest request, HttpServletResponse response,
ServerEndpointConfig endpointConfig, Map<String, String> pathParams) throws Exception {
ServerContainer container = getContainer(request);
upgradeMethod.invoke(container, request, response, endpointConfig, pathParams);
}
}

View File

@ -37,7 +37,6 @@ import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.SubProtocolCapable;
import org.springframework.web.socket.WebSocketExtension;
@ -47,65 +46,27 @@ import org.springframework.web.socket.handler.WebSocketHandlerDecorator;
import org.springframework.web.socket.server.HandshakeFailureException;
import org.springframework.web.socket.server.HandshakeHandler;
import org.springframework.web.socket.server.RequestUpgradeStrategy;
import org.springframework.web.socket.server.jetty.JettyRequestUpgradeStrategy;
import org.springframework.web.socket.server.standard.GlassFishRequestUpgradeStrategy;
import org.springframework.web.socket.server.standard.StandardWebSocketUpgradeStrategy;
import org.springframework.web.socket.server.standard.TomcatRequestUpgradeStrategy;
import org.springframework.web.socket.server.standard.UndertowRequestUpgradeStrategy;
import org.springframework.web.socket.server.standard.WebLogicRequestUpgradeStrategy;
import org.springframework.web.socket.server.standard.WebSphereRequestUpgradeStrategy;
/**
* A base class for {@link HandshakeHandler} implementations, independent of the Servlet API.
*
* <p>Performs initial validation of the WebSocket handshake request - possibly rejecting it
* through the appropriate HTTP status code - while also allowing its subclasses to override
* various parts of the negotiation process (for example, origin validation, sub-protocol negotiation,
* extensions negotiation, etc).
* various parts of the negotiation process: for example, origin validation, sub-protocol
* negotiation, extensions negotiation, etc.
*
* <p>If the negotiation succeeds, the actual upgrade is delegated to a server-specific
* {@link org.springframework.web.socket.server.RequestUpgradeStrategy}, which will update
* the response as necessary and initialize the WebSocket.
* the response as necessary and initialize the WebSocket. As of 7.0, this class uses
* {@link StandardWebSocketUpgradeStrategy} unless explicitly configured.
*
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 4.2
* @see org.springframework.web.socket.server.jetty.JettyRequestUpgradeStrategy
* @see org.springframework.web.socket.server.standard.TomcatRequestUpgradeStrategy
* @see org.springframework.web.socket.server.standard.UndertowRequestUpgradeStrategy
* @see org.springframework.web.socket.server.standard.GlassFishRequestUpgradeStrategy
*/
public abstract class AbstractHandshakeHandler implements HandshakeHandler, Lifecycle {
private static final boolean tomcatWsPresent;
private static final boolean jettyWsPresent;
private static final boolean undertowWsPresent;
private static final boolean glassfishWsPresent;
private static final boolean weblogicWsPresent;
private static final boolean websphereWsPresent;
static {
ClassLoader classLoader = AbstractHandshakeHandler.class.getClassLoader();
tomcatWsPresent = ClassUtils.isPresent(
"org.apache.tomcat.websocket.server.WsHttpUpgradeHandler", classLoader);
jettyWsPresent = ClassUtils.isPresent(
"org.eclipse.jetty.ee10.websocket.server.JettyWebSocketServerContainer", classLoader);
undertowWsPresent = ClassUtils.isPresent(
"io.undertow.websockets.jsr.ServerWebSocketContainer", classLoader);
glassfishWsPresent = ClassUtils.isPresent(
"org.glassfish.tyrus.servlet.TyrusHttpUpgradeHandler", classLoader);
weblogicWsPresent = ClassUtils.isPresent(
"weblogic.websocket.tyrus.TyrusServletWriter", classLoader);
websphereWsPresent = ClassUtils.isPresent(
"com.ibm.websphere.wsoc.WsWsocServerContainer", classLoader);
}
protected final Log logger = LogFactory.getLog(getClass());
private final RequestUpgradeStrategy requestUpgradeStrategy;
@ -116,12 +77,10 @@ public abstract class AbstractHandshakeHandler implements HandshakeHandler, Life
/**
* Default constructor that auto-detects and instantiates a
* {@link RequestUpgradeStrategy} suitable for the runtime container.
* @throws IllegalStateException if no {@link RequestUpgradeStrategy} can be found.
* Default constructor that uses {@link StandardWebSocketUpgradeStrategy}.
*/
protected AbstractHandshakeHandler() {
this(initRequestUpgradeStrategy());
this(new StandardWebSocketUpgradeStrategy());
}
/**
@ -394,45 +353,4 @@ public abstract class AbstractHandshakeHandler implements HandshakeHandler, Life
return request.getPrincipal();
}
private static RequestUpgradeStrategy initRequestUpgradeStrategy() {
if (tomcatWsPresent) {
return new TomcatRequestUpgradeStrategy();
}
else if (jettyWsPresent) {
return new JettyRequestUpgradeStrategy();
}
else if (undertowWsPresent) {
return new UndertowRequestUpgradeStrategy();
}
else if (glassfishWsPresent) {
return TyrusStrategyDelegate.forGlassFish();
}
else if (weblogicWsPresent) {
return TyrusStrategyDelegate.forWebLogic();
}
else if (websphereWsPresent) {
return new WebSphereRequestUpgradeStrategy();
}
else {
// Let's assume Jakarta WebSocket API 2.1+
return new StandardWebSocketUpgradeStrategy();
}
}
/**
* Inner class to avoid a reachable dependency on Tyrus API.
*/
private static class TyrusStrategyDelegate {
public static RequestUpgradeStrategy forGlassFish() {
return new GlassFishRequestUpgradeStrategy();
}
public static RequestUpgradeStrategy forWebLogic() {
return new WebLogicRequestUpgradeStrategy();
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,13 +18,17 @@ package org.springframework.web.socket.server.support;
import jakarta.servlet.ServletContext;
import org.springframework.util.ClassUtils;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.socket.server.RequestUpgradeStrategy;
import org.springframework.web.socket.server.jetty.JettyRequestUpgradeStrategy;
import org.springframework.web.socket.server.standard.StandardWebSocketUpgradeStrategy;
/**
* A default {@link org.springframework.web.socket.server.HandshakeHandler} implementation,
* extending {@link AbstractHandshakeHandler} with Servlet-specific initialization support.
* See {@link AbstractHandshakeHandler}'s javadoc for details on supported servers etc.
* As of 7.0, this class prefers {@link JettyRequestUpgradeStrategy} when Jetty WebSocket
* is available on the classpath, using {@link StandardWebSocketUpgradeStrategy} otherwise.
*
* @author Rossen Stoyanchev
* @author Juergen Hoeller
@ -32,7 +36,13 @@ import org.springframework.web.socket.server.RequestUpgradeStrategy;
*/
public class DefaultHandshakeHandler extends AbstractHandshakeHandler implements ServletContextAware {
private static final boolean jettyWsPresent = ClassUtils.isPresent(
"org.eclipse.jetty.ee10.websocket.server.JettyWebSocketServerContainer",
DefaultHandshakeHandler.class.getClassLoader());
public DefaultHandshakeHandler() {
super(jettyWsPresent ? new JettyRequestUpgradeStrategy() : new StandardWebSocketUpgradeStrategy());
}
public DefaultHandshakeHandler(RequestUpgradeStrategy requestUpgradeStrategy) {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,7 +20,6 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Stream;
@ -34,16 +33,12 @@ import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.context.Lifecycle;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.socket.client.WebSocketClient;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.server.RequestUpgradeStrategy;
import org.springframework.web.socket.server.jetty.JettyRequestUpgradeStrategy;
import org.springframework.web.socket.server.standard.TomcatRequestUpgradeStrategy;
import org.springframework.web.socket.server.standard.UndertowRequestUpgradeStrategy;
import org.springframework.web.socket.server.standard.StandardWebSocketUpgradeStrategy;
import org.springframework.web.socket.server.support.DefaultHandshakeHandler;
import static org.junit.jupiter.api.Named.named;
@ -57,11 +52,6 @@ import static org.junit.jupiter.params.provider.Arguments.arguments;
*/
public abstract class AbstractWebSocketIntegrationTests {
private static final Map<Class<?>, Class<?>> upgradeStrategyConfigTypes = Map.of(
JettyWebSocketTestServer.class, JettyUpgradeStrategyConfig.class,
TomcatWebSocketTestServer.class, TomcatUpgradeStrategyConfig.class,
UndertowTestServer.class, UndertowUpgradeStrategyConfig.class);
static Stream<Arguments> argumentsFactory() {
return Stream.of(
arguments(named("Jetty", new JettyWebSocketTestServer()), named("Standard", new StandardWebSocketClient())),
@ -104,7 +94,8 @@ public abstract class AbstractWebSocketIntegrationTests {
this.wac = new AnnotationConfigWebApplicationContext();
this.wac.register(getAnnotatedConfigClasses());
this.wac.register(upgradeStrategyConfigTypes.get(this.server.getClass()));
this.wac.register(this.server instanceof JettyWebSocketTestServer ? JettyHandshakeHandler.class :
StandardHandshakeHandler.class);
if (this.webSocketClient instanceof Lifecycle) {
((Lifecycle) this.webSocketClient).start();
@ -164,46 +155,18 @@ public abstract class AbstractWebSocketIntegrationTests {
}
abstract static class AbstractRequestUpgradeStrategyConfig {
private static class JettyHandshakeHandler extends DefaultHandshakeHandler {
@Bean
public DefaultHandshakeHandler handshakeHandler() {
return new DefaultHandshakeHandler(requestUpgradeStrategy());
}
public abstract RequestUpgradeStrategy requestUpgradeStrategy();
}
@Configuration(proxyBeanMethods = false)
static class JettyUpgradeStrategyConfig extends AbstractRequestUpgradeStrategyConfig {
@Override
@Bean
public RequestUpgradeStrategy requestUpgradeStrategy() {
return new JettyRequestUpgradeStrategy();
public JettyHandshakeHandler() {
super(new JettyRequestUpgradeStrategy());
}
}
@Configuration(proxyBeanMethods = false)
static class TomcatUpgradeStrategyConfig extends AbstractRequestUpgradeStrategyConfig {
private static class StandardHandshakeHandler extends DefaultHandshakeHandler {
@Override
@Bean
public RequestUpgradeStrategy requestUpgradeStrategy() {
return new TomcatRequestUpgradeStrategy();
}
}
@Configuration(proxyBeanMethods = false)
static class UndertowUpgradeStrategyConfig extends AbstractRequestUpgradeStrategyConfig {
@Override
@Bean
public RequestUpgradeStrategy requestUpgradeStrategy() {
return new UndertowRequestUpgradeStrategy();
public StandardHandshakeHandler() {
super(new StandardWebSocketUpgradeStrategy());
}
}

View File

@ -18,15 +18,13 @@ package org.springframework.web.socket;
import java.util.List;
import org.glassfish.tyrus.core.TyrusExtension;
import org.junit.jupiter.api.Test;
import org.springframework.web.socket.adapter.standard.StandardToWebSocketExtensionAdapter;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test fixture for {@link WebSocketExtension}
* Test fixture for {@link WebSocketExtension}.
*
* @author Brian Clozel
*/
class WebSocketExtensionTests {
@ -54,11 +52,4 @@ class WebSocketExtensionTests {
.containsExactly("x-foo-extension", "x-bar-extension");
}
@Test // gh-26449
public void equality() {
WebSocketExtension ext1 = new WebSocketExtension("myExtension");
WebSocketExtension ext2 = new StandardToWebSocketExtensionAdapter(new TyrusExtension("myExtension"));
assertThat(ext1).isEqualTo(ext2);
}
}

View File

@ -104,9 +104,9 @@ class WebSocketHandshakeTests extends AbstractWebSocketIntegrationTests {
TestWebSocketHandler handler() {
return new TestWebSocketHandler();
}
}
@SuppressWarnings("rawtypes")
private static class TestWebSocketHandler extends AbstractWebSocketHandler {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -44,7 +44,7 @@ import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurationSupport;
import org.springframework.web.socket.server.RequestUpgradeStrategy;
import org.springframework.web.socket.server.standard.TomcatRequestUpgradeStrategy;
import org.springframework.web.socket.server.standard.StandardWebSocketUpgradeStrategy;
import org.springframework.web.socket.server.support.DefaultHandshakeHandler;
import static org.assertj.core.api.Assertions.assertThat;
@ -144,7 +144,7 @@ class WebSocketStompClientIntegrationTests {
@Override
protected void registerStompEndpoints(StompEndpointRegistry registry) {
// Can't rely on classpath detection
RequestUpgradeStrategy upgradeStrategy = new TomcatRequestUpgradeStrategy();
RequestUpgradeStrategy upgradeStrategy = new StandardWebSocketUpgradeStrategy();
registry.addEndpoint("/stomp")
.setHandshakeHandler(new DefaultHandshakeHandler(upgradeStrategy))
.setAllowedOrigins("*");

View File

@ -62,6 +62,7 @@ import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry
import org.springframework.web.socket.handler.TextWebSocketHandler;
import org.springframework.web.socket.server.HandshakeHandler;
import org.springframework.web.socket.server.RequestUpgradeStrategy;
import org.springframework.web.socket.server.standard.StandardWebSocketUpgradeStrategy;
import org.springframework.web.socket.server.support.DefaultHandshakeHandler;
import static org.assertj.core.api.Assertions.assertThat;
@ -143,7 +144,9 @@ abstract class AbstractSockJsIntegrationTests {
}
protected abstract Class<?> upgradeStrategyConfigClass();
protected Class<?> upgradeStrategyConfigClass() {
return StandardWebSocketUpgradeStrategy.class;
}
protected abstract WebSocketTestServer createWebSocketTestServer();

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,24 +18,15 @@ package org.springframework.web.socket.sockjs.client;
import java.io.IOException;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.UndertowTestServer;
import org.springframework.web.socket.WebSocketTestServer;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.server.RequestUpgradeStrategy;
import org.springframework.web.socket.server.standard.UndertowRequestUpgradeStrategy;
/**
* @author Brian Clozel
*/
class UndertowSockJsIntegrationTests extends AbstractSockJsIntegrationTests {
@Override
protected Class<?> upgradeStrategyConfigClass() {
return UndertowTestConfig.class;
}
@Override
protected WebSocketTestServer createWebSocketTestServer() {
return new UndertowTestServer();
@ -56,13 +47,4 @@ class UndertowSockJsIntegrationTests extends AbstractSockJsIntegrationTests {
}
}
@Configuration(proxyBeanMethods = false)
static class UndertowTestConfig {
@Bean
RequestUpgradeStrategy upgradeStrategy() {
return new UndertowRequestUpgradeStrategy();
}
}
}