diff --git a/framework-platform/framework-platform.gradle b/framework-platform/framework-platform.gradle index 25979bcdf7c..28d1cf7b611 100644 --- a/framework-platform/framework-platform.gradle +++ b/framework-platform/framework-platform.gradle @@ -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") diff --git a/spring-websocket/spring-websocket.gradle b/spring-websocket/spring-websocket.gradle index 2250f5fdf38..c20652bb129 100644 --- a/spring-websocket/spring-websocket.gradle +++ b/spring-websocket/spring-websocket.gradle @@ -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") diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractStandardUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractStandardUpgradeStrategy.java deleted file mode 100644 index e19318ad296..00000000000 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractStandardUpgradeStrategy.java +++ /dev/null @@ -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 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 getSupportedExtensions(ServerHttpRequest request) { - List extensions = this.extensions; - if (extensions == null) { - HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest(); - extensions = getInstalledExtensions(getContainer(servletRequest)); - this.extensions = extensions; - } - return extensions; - } - - protected List getInstalledExtensions(WebSocketContainer container) { - List 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 selectedExtensions, - @Nullable Principal user, WebSocketHandler wsHandler, Map 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 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 selectedExtensions, Endpoint endpoint) - throws HandshakeFailureException; - -} diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractTyrusRequestUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractTyrusRequestUpgradeStrategy.java deleted file mode 100644 index 0dea79819f8..00000000000 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractTyrusRequestUpgradeStrategy.java +++ /dev/null @@ -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 Project Tyrus - */ -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 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 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 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; - -} diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/GlassFishRequestUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/GlassFishRequestUpgradeStrategy.java deleted file mode 100644 index 07ca46500e1..00000000000 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/GlassFishRequestUpgradeStrategy.java +++ /dev/null @@ -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); - } - } - -} diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/StandardWebSocketUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/StandardWebSocketUpgradeStrategy.java index 9cb810a0ce4..818fbe7730c 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/StandardWebSocketUpgradeStrategy.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/StandardWebSocketUpgradeStrategy.java @@ -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+. * - *

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. - * *

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 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 selectedExtensions, Endpoint endpoint) + public List getSupportedExtensions(ServerHttpRequest request) { + List extensions = this.extensions; + if (extensions == null) { + HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest(); + extensions = getInstalledExtensions(getContainer(servletRequest)); + this.extensions = extensions; + } + return extensions; + } + + protected List getInstalledExtensions(WebSocketContainer container) { + List 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 selectedExtensions, + @Nullable Principal user, WebSocketHandler wsHandler, Map 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 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 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(); } } diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/TomcatRequestUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/TomcatRequestUpgradeStrategy.java deleted file mode 100644 index 714918c07a1..00000000000 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/TomcatRequestUpgradeStrategy.java +++ /dev/null @@ -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). - * - *

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 pathParams) throws Exception { - - ((WsServerContainer) getContainer(request)).upgradeHttpToWebSocket( - request, response, endpointConfig, pathParams); - } - -} diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/UndertowRequestUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/UndertowRequestUpgradeStrategy.java deleted file mode 100644 index bb20bcafcaf..00000000000 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/UndertowRequestUpgradeStrategy.java +++ /dev/null @@ -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. - * - *

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 pathParams) throws Exception { - - ((ServerWebSocketContainer) getContainer(request)).doUpgrade( - request, response, endpointConfig, pathParams); - } - -} diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/WebLogicRequestUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/WebLogicRequestUpgradeStrategy.java deleted file mode 100644 index 23409116acc..00000000000 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/WebLogicRequestUpgradeStrategy.java +++ /dev/null @@ -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); - } - } - } - -} diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/WebSphereRequestUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/WebSphereRequestUpgradeStrategy.java deleted file mode 100644 index 47df6b7c859..00000000000 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/WebSphereRequestUpgradeStrategy.java +++ /dev/null @@ -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 pathParams) throws Exception { - - ServerContainer container = getContainer(request); - upgradeMethod.invoke(container, request, response, endpointConfig, pathParams); - } - -} diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/support/AbstractHandshakeHandler.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/support/AbstractHandshakeHandler.java index 6c05cfda70e..d489acd5d94 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/support/AbstractHandshakeHandler.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/support/AbstractHandshakeHandler.java @@ -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. * *

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. * *

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(); - } - } - } diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/support/DefaultHandshakeHandler.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/support/DefaultHandshakeHandler.java index 1ad972ab904..cc6d3cb95e3 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/support/DefaultHandshakeHandler.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/support/DefaultHandshakeHandler.java @@ -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) { diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/AbstractWebSocketIntegrationTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/AbstractWebSocketIntegrationTests.java index e762a0af22d..9d03d40ba04 100644 --- a/spring-websocket/src/test/java/org/springframework/web/socket/AbstractWebSocketIntegrationTests.java +++ b/spring-websocket/src/test/java/org/springframework/web/socket/AbstractWebSocketIntegrationTests.java @@ -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> upgradeStrategyConfigTypes = Map.of( - JettyWebSocketTestServer.class, JettyUpgradeStrategyConfig.class, - TomcatWebSocketTestServer.class, TomcatUpgradeStrategyConfig.class, - UndertowTestServer.class, UndertowUpgradeStrategyConfig.class); - static Stream 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()); } } diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/WebSocketExtensionTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/WebSocketExtensionTests.java index 2ab23568efb..f9419e641e4 100644 --- a/spring-websocket/src/test/java/org/springframework/web/socket/WebSocketExtensionTests.java +++ b/spring-websocket/src/test/java/org/springframework/web/socket/WebSocketExtensionTests.java @@ -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); - } } diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/WebSocketHandshakeTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/WebSocketHandshakeTests.java index 74990097e18..142a0c18f37 100644 --- a/spring-websocket/src/test/java/org/springframework/web/socket/WebSocketHandshakeTests.java +++ b/spring-websocket/src/test/java/org/springframework/web/socket/WebSocketHandshakeTests.java @@ -104,9 +104,9 @@ class WebSocketHandshakeTests extends AbstractWebSocketIntegrationTests { TestWebSocketHandler handler() { return new TestWebSocketHandler(); } - } + @SuppressWarnings("rawtypes") private static class TestWebSocketHandler extends AbstractWebSocketHandler { diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/messaging/StompWebSocketIntegrationTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/messaging/StompWebSocketIntegrationTests.java index 3db503998a9..f02301abfd9 100644 --- a/spring-websocket/src/test/java/org/springframework/web/socket/messaging/StompWebSocketIntegrationTests.java +++ b/spring-websocket/src/test/java/org/springframework/web/socket/messaging/StompWebSocketIntegrationTests.java @@ -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. diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/messaging/WebSocketStompClientIntegrationTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/messaging/WebSocketStompClientIntegrationTests.java index 3e464b329aa..506f90ded33 100644 --- a/spring-websocket/src/test/java/org/springframework/web/socket/messaging/WebSocketStompClientIntegrationTests.java +++ b/spring-websocket/src/test/java/org/springframework/web/socket/messaging/WebSocketStompClientIntegrationTests.java @@ -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("*"); diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/client/AbstractSockJsIntegrationTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/client/AbstractSockJsIntegrationTests.java index 6299ab45c7b..615baf82925 100644 --- a/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/client/AbstractSockJsIntegrationTests.java +++ b/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/client/AbstractSockJsIntegrationTests.java @@ -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(); diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/client/UndertowSockJsIntegrationTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/client/UndertowSockJsIntegrationTests.java index 54c9feb4cb7..57250ba77df 100644 --- a/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/client/UndertowSockJsIntegrationTests.java +++ b/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/client/UndertowSockJsIntegrationTests.java @@ -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(); - } - } - }