From a2ac764f9c171c55e51ddeb83a6e9229e5455aee Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 7 Nov 2022 17:35:31 +0100 Subject: [PATCH] Reuse StandardWebSocketUpgradeStrategy as a base class for Tomcat etc Includes non-reflective instantiation of well-known strategy classes. See gh-29436 --- .../support/HandshakeWebSocketService.java | 108 ++++++---- .../upgrade/JettyRequestUpgradeStrategy.java | 4 +- .../ReactorNetty2RequestUpgradeStrategy.java | 2 +- .../ReactorNettyRequestUpgradeStrategy.java | 4 +- .../StandardWebSocketUpgradeStrategy.java | 199 ++++++++++++++++++ .../upgrade/TomcatRequestUpgradeStrategy.java | 162 +------------- .../UndertowRequestUpgradeStrategy.java | 2 +- .../jetty/JettyRequestUpgradeStrategy.java | 2 +- .../AbstractTyrusRequestUpgradeStrategy.java | 8 +- .../StandardWebSocketUpgradeStrategy.java | 21 +- .../TomcatRequestUpgradeStrategy.java | 54 +---- .../UndertowRequestUpgradeStrategy.java | 64 ++---- .../WebSphereRequestUpgradeStrategy.java | 47 +---- .../support/AbstractHandshakeHandler.java | 84 ++++---- 14 files changed, 379 insertions(+), 382 deletions(-) create mode 100644 spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/StandardWebSocketUpgradeStrategy.java diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/support/HandshakeWebSocketService.java b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/support/HandshakeWebSocketService.java index 567e5448eb13..0d20bfa5b767 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/support/HandshakeWebSocketService.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/support/HandshakeWebSocketService.java @@ -38,12 +38,17 @@ import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.MultiValueMap; -import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; import org.springframework.web.reactive.socket.HandshakeInfo; import org.springframework.web.reactive.socket.WebSocketHandler; import org.springframework.web.reactive.socket.server.RequestUpgradeStrategy; import org.springframework.web.reactive.socket.server.WebSocketService; +import org.springframework.web.reactive.socket.server.upgrade.JettyRequestUpgradeStrategy; +import org.springframework.web.reactive.socket.server.upgrade.ReactorNetty2RequestUpgradeStrategy; +import org.springframework.web.reactive.socket.server.upgrade.ReactorNettyRequestUpgradeStrategy; +import org.springframework.web.reactive.socket.server.upgrade.StandardWebSocketUpgradeStrategy; +import org.springframework.web.reactive.socket.server.upgrade.TomcatRequestUpgradeStrategy; +import org.springframework.web.reactive.socket.server.upgrade.UndertowRequestUpgradeStrategy; import org.springframework.web.server.MethodNotAllowedException; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebInputException; @@ -55,6 +60,7 @@ * also be explicitly configured. * * @author Rossen Stoyanchev + * @author Juergen Hoeller * @since 5.0 */ public class HandshakeWebSocketService implements WebSocketService, Lifecycle { @@ -66,28 +72,32 @@ public class HandshakeWebSocketService implements WebSocketService, Lifecycle { private static final Mono> EMPTY_ATTRIBUTES = Mono.just(Collections.emptyMap()); - private static final boolean tomcatPresent; + private static final boolean tomcatWsPresent; - private static final boolean jettyPresent; + private static final boolean jettyWsPresent; - private static final boolean undertowPresent; + private static final boolean undertowWsPresent; private static final boolean reactorNettyPresent; private static final boolean reactorNetty2Present; static { - ClassLoader loader = HandshakeWebSocketService.class.getClassLoader(); - tomcatPresent = ClassUtils.isPresent("org.apache.tomcat.websocket.server.WsHttpUpgradeHandler", loader); - jettyPresent = ClassUtils.isPresent("org.eclipse.jetty.websocket.server.JettyWebSocketServerContainer", loader); - undertowPresent = ClassUtils.isPresent("io.undertow.websockets.WebSocketProtocolHandshakeHandler", loader); - reactorNettyPresent = ClassUtils.isPresent("reactor.netty.http.server.HttpServerResponse", loader); - reactorNetty2Present = ClassUtils.isPresent("reactor.netty5.http.server.HttpServerResponse", loader); + ClassLoader classLoader = HandshakeWebSocketService.class.getClassLoader(); + tomcatWsPresent = ClassUtils.isPresent( + "org.apache.tomcat.websocket.server.WsHttpUpgradeHandler", classLoader); + jettyWsPresent = ClassUtils.isPresent( + "org.eclipse.jetty.websocket.server.JettyWebSocketServerContainer", classLoader); + undertowWsPresent = ClassUtils.isPresent( + "io.undertow.websockets.WebSocketProtocolHandshakeHandler", classLoader); + reactorNettyPresent = ClassUtils.isPresent( + "reactor.netty.http.server.HttpServerResponse", classLoader); + reactorNetty2Present = ClassUtils.isPresent( + "reactor.netty5.http.server.HttpServerResponse", classLoader); } - protected static final Log logger = LogFactory.getLog(HandshakeWebSocketService.class); - + private static final Log logger = LogFactory.getLog(HandshakeWebSocketService.class); private final RequestUpgradeStrategy upgradeStrategy; @@ -114,40 +124,6 @@ public HandshakeWebSocketService(RequestUpgradeStrategy upgradeStrategy) { this.upgradeStrategy = upgradeStrategy; } - static RequestUpgradeStrategy initUpgradeStrategy() { - String className; - if (tomcatPresent) { - className = "TomcatRequestUpgradeStrategy"; - } - else if (jettyPresent) { - className = "JettyRequestUpgradeStrategy"; - } - else if (undertowPresent) { - className = "UndertowRequestUpgradeStrategy"; - } - else if (reactorNettyPresent) { - // As late as possible (Reactor Netty commonly used for WebClient) - className = "ReactorNettyRequestUpgradeStrategy"; - } - else if (reactorNetty2Present) { - // As late as possible (Reactor Netty commonly used for WebClient) - className = "ReactorNetty2RequestUpgradeStrategy"; - } - else { - throw new IllegalStateException("No suitable default RequestUpgradeStrategy found"); - } - - try { - className = "org.springframework.web.reactive.socket.server.upgrade." + className; - Class clazz = ClassUtils.forName(className, HandshakeWebSocketService.class.getClassLoader()); - return (RequestUpgradeStrategy) ReflectionUtils.accessibleConstructor(clazz).newInstance(); - } - catch (Throwable ex) { - throw new IllegalStateException( - "Failed to instantiate RequestUpgradeStrategy: " + className, ex); - } - } - /** * Return the {@link RequestUpgradeStrategy} for WebSocket requests. @@ -292,4 +268,44 @@ private HandshakeInfo createHandshakeInfo(ServerWebExchange exchange, ServerHttp return new HandshakeInfo(uri, headers, cookies, principal, protocol, remoteAddress, attributes, logPrefix); } + + static RequestUpgradeStrategy initUpgradeStrategy() { + if (tomcatWsPresent) { + return new TomcatRequestUpgradeStrategy(); + } + else if (jettyWsPresent) { + return new JettyRequestUpgradeStrategy(); + } + else if (undertowWsPresent) { + return new UndertowRequestUpgradeStrategy(); + } + else if (reactorNettyPresent) { + // As late as possible (Reactor Netty commonly used for WebClient) + return ReactorNettyStrategyDelegate.forReactorNetty1(); + } + else if (reactorNetty2Present) { + // As late as possible (Reactor Netty commonly used for WebClient) + return ReactorNettyStrategyDelegate.forReactorNetty2(); + } + else { + // Let's assume Jakarta WebSocket API 2.1+ + return new StandardWebSocketUpgradeStrategy(); + } + } + + + /** + * Inner class to avoid a reachable dependency on Reactor Netty API. + */ + private static class ReactorNettyStrategyDelegate { + + public static RequestUpgradeStrategy forReactorNetty1() { + return new ReactorNettyRequestUpgradeStrategy(); + } + + public static RequestUpgradeStrategy forReactorNetty2() { + return new ReactorNetty2RequestUpgradeStrategy(); + } + } + } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/JettyRequestUpgradeStrategy.java b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/JettyRequestUpgradeStrategy.java index c95782934866..7f56ca7565db 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/JettyRequestUpgradeStrategy.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/JettyRequestUpgradeStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * 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. @@ -40,7 +40,7 @@ import org.springframework.web.server.ServerWebExchange; /** - * A {@link RequestUpgradeStrategy} for Jetty 11. + * A WebSocket {@code RequestUpgradeStrategy} for Jetty 11. * * @author Rossen Stoyanchev * @since 5.3.4 diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/ReactorNetty2RequestUpgradeStrategy.java b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/ReactorNetty2RequestUpgradeStrategy.java index 14623902a5fd..57c97e5b87a8 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/ReactorNetty2RequestUpgradeStrategy.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/ReactorNetty2RequestUpgradeStrategy.java @@ -35,7 +35,7 @@ import org.springframework.web.server.ServerWebExchange; /** - * A {@link RequestUpgradeStrategy} for use with Reactor Netty for Netty 5. + * A WebSocket {@code RequestUpgradeStrategy} for Reactor Netty for Netty 5. * *

This class is based on {@link ReactorNettyRequestUpgradeStrategy}. *\ diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/ReactorNettyRequestUpgradeStrategy.java b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/ReactorNettyRequestUpgradeStrategy.java index d8f98768a67d..a7ac18a564b4 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/ReactorNettyRequestUpgradeStrategy.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/ReactorNettyRequestUpgradeStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * 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. @@ -35,7 +35,7 @@ import org.springframework.web.server.ServerWebExchange; /** - * A {@link RequestUpgradeStrategy} for use with Reactor Netty. + * A WebSocket {@code RequestUpgradeStrategy} for Reactor Netty. * * @author Rossen Stoyanchev * @since 5.0 diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/StandardWebSocketUpgradeStrategy.java b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/StandardWebSocketUpgradeStrategy.java new file mode 100644 index 000000000000..d5341505e0b6 --- /dev/null +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/StandardWebSocketUpgradeStrategy.java @@ -0,0 +1,199 @@ +/* + * 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.reactive.socket.server.upgrade; + +import java.util.Collections; +import java.util.Map; +import java.util.function.Supplier; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.websocket.Endpoint; +import jakarta.websocket.server.ServerContainer; +import jakarta.websocket.server.ServerEndpointConfig; +import reactor.core.publisher.Mono; + +import org.springframework.core.io.buffer.DataBufferFactory; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpRequestDecorator; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.http.server.reactive.ServerHttpResponseDecorator; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.web.reactive.socket.HandshakeInfo; +import org.springframework.web.reactive.socket.WebSocketHandler; +import org.springframework.web.reactive.socket.adapter.ContextWebSocketHandler; +import org.springframework.web.reactive.socket.adapter.StandardWebSocketHandlerAdapter; +import org.springframework.web.reactive.socket.adapter.TomcatWebSocketSession; +import org.springframework.web.reactive.socket.server.RequestUpgradeStrategy; +import org.springframework.web.server.ServerWebExchange; + +/** + * 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. + * + * @author Juergen Hoeller + * @author Violeta Georgieva + * @author Rossen Stoyanchev + * @since 6.0 + * @see jakarta.websocket.server.ServerContainer#upgradeHttpToWebSocket + */ +public class StandardWebSocketUpgradeStrategy implements RequestUpgradeStrategy { + + private static final String SERVER_CONTAINER_ATTR = "jakarta.websocket.server.ServerContainer"; + + + @Nullable + private Long asyncSendTimeout; + + @Nullable + private Long maxSessionIdleTimeout; + + @Nullable + private Integer maxTextMessageBufferSize; + + @Nullable + private Integer maxBinaryMessageBufferSize; + + @Nullable + private ServerContainer serverContainer; + + + /** + * Exposes the underlying config option on + * {@link ServerContainer#setAsyncSendTimeout(long)}. + */ + public void setAsyncSendTimeout(Long timeoutInMillis) { + this.asyncSendTimeout = timeoutInMillis; + } + + @Nullable + public Long getAsyncSendTimeout() { + return this.asyncSendTimeout; + } + + /** + * Exposes the underlying config option on + * {@link ServerContainer#setDefaultMaxSessionIdleTimeout(long)}. + */ + public void setMaxSessionIdleTimeout(Long timeoutInMillis) { + this.maxSessionIdleTimeout = timeoutInMillis; + } + + @Nullable + public Long getMaxSessionIdleTimeout() { + return this.maxSessionIdleTimeout; + } + + /** + * Exposes the underlying config option on + * {@link ServerContainer#setDefaultMaxTextMessageBufferSize(int)}. + */ + public void setMaxTextMessageBufferSize(Integer bufferSize) { + this.maxTextMessageBufferSize = bufferSize; + } + + @Nullable + public Integer getMaxTextMessageBufferSize() { + return this.maxTextMessageBufferSize; + } + + /** + * Exposes the underlying config option on + * {@link ServerContainer#setDefaultMaxBinaryMessageBufferSize(int)}. + */ + public void setMaxBinaryMessageBufferSize(Integer bufferSize) { + this.maxBinaryMessageBufferSize = bufferSize; + } + + @Nullable + public Integer getMaxBinaryMessageBufferSize() { + return this.maxBinaryMessageBufferSize; + } + + @Override + public Mono upgrade(ServerWebExchange exchange, WebSocketHandler handler, + @Nullable String subProtocol, Supplier handshakeInfoFactory){ + + ServerHttpRequest request = exchange.getRequest(); + ServerHttpResponse response = exchange.getResponse(); + + HttpServletRequest servletRequest = ServerHttpRequestDecorator.getNativeRequest(request); + HttpServletResponse servletResponse = ServerHttpResponseDecorator.getNativeResponse(response); + + HandshakeInfo handshakeInfo = handshakeInfoFactory.get(); + DataBufferFactory bufferFactory = response.bufferFactory(); + + // Trigger WebFlux preCommit actions and upgrade + return exchange.getResponse().setComplete() + .then(Mono.deferContextual(contextView -> { + Endpoint endpoint = new StandardWebSocketHandlerAdapter( + ContextWebSocketHandler.decorate(handler, contextView), + session -> new TomcatWebSocketSession(session, handshakeInfo, bufferFactory)); + + String requestURI = servletRequest.getRequestURI(); + DefaultServerEndpointConfig config = new DefaultServerEndpointConfig(requestURI, endpoint); + config.setSubprotocols(subProtocol != null ? + Collections.singletonList(subProtocol) : Collections.emptyList()); + + try { + upgradeHttpToWebSocket(servletRequest, servletResponse, config, Collections.emptyMap()); + } + catch (Exception ex) { + return Mono.error(ex); + } + return Mono.empty(); + })); + } + + + 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) { + if (this.serverContainer == null) { + Object container = request.getServletContext().getAttribute(SERVER_CONTAINER_ATTR); + Assert.state(container instanceof ServerContainer, + "ServletContext attribute 'jakarta.websocket.server.ServerContainer' not found."); + this.serverContainer = (ServerContainer) container; + initServerContainer(this.serverContainer); + } + return this.serverContainer; + } + + private void initServerContainer(ServerContainer serverContainer) { + if (this.asyncSendTimeout != null) { + serverContainer.setAsyncSendTimeout(this.asyncSendTimeout); + } + if (this.maxSessionIdleTimeout != null) { + serverContainer.setDefaultMaxSessionIdleTimeout(this.maxSessionIdleTimeout); + } + if (this.maxTextMessageBufferSize != null) { + serverContainer.setDefaultMaxTextMessageBufferSize(this.maxTextMessageBufferSize); + } + if (this.maxBinaryMessageBufferSize != null) { + serverContainer.setDefaultMaxBinaryMessageBufferSize(this.maxBinaryMessageBufferSize); + } + } + +} diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/TomcatRequestUpgradeStrategy.java b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/TomcatRequestUpgradeStrategy.java index ce3a3719d60a..8837a4543f69 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/TomcatRequestUpgradeStrategy.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/TomcatRequestUpgradeStrategy.java @@ -16,171 +16,31 @@ package org.springframework.web.reactive.socket.server.upgrade; -import java.util.Collections; -import java.util.function.Supplier; +import java.util.Map; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import jakarta.websocket.Endpoint; -import jakarta.websocket.server.ServerContainer; +import jakarta.websocket.server.ServerEndpointConfig; import org.apache.tomcat.websocket.server.WsServerContainer; -import reactor.core.publisher.Mono; - -import org.springframework.core.io.buffer.DataBufferFactory; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpRequestDecorator; -import org.springframework.http.server.reactive.ServerHttpResponse; -import org.springframework.http.server.reactive.ServerHttpResponseDecorator; -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; -import org.springframework.web.reactive.socket.HandshakeInfo; -import org.springframework.web.reactive.socket.WebSocketHandler; -import org.springframework.web.reactive.socket.adapter.ContextWebSocketHandler; -import org.springframework.web.reactive.socket.adapter.StandardWebSocketHandlerAdapter; -import org.springframework.web.reactive.socket.adapter.TomcatWebSocketSession; -import org.springframework.web.reactive.socket.server.RequestUpgradeStrategy; -import org.springframework.web.server.ServerWebExchange; /** - * A {@link RequestUpgradeStrategy} for use with Tomcat. + * 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). * * @author Violeta Georgieva * @author Rossen Stoyanchev + * @author Juergen Hoeller * @since 5.0 + * @see org.apache.tomcat.websocket.server.WsServerContainer#upgradeHttpToWebSocket */ -public class TomcatRequestUpgradeStrategy implements RequestUpgradeStrategy { - - private static final String SERVER_CONTAINER_ATTR = "jakarta.websocket.server.ServerContainer"; - - - @Nullable - private Long asyncSendTimeout; - - @Nullable - private Long maxSessionIdleTimeout; - - @Nullable - private Integer maxTextMessageBufferSize; - - @Nullable - private Integer maxBinaryMessageBufferSize; - - @Nullable - private WsServerContainer serverContainer; - - - /** - * Exposes the underlying config option on - * {@link jakarta.websocket.server.ServerContainer#setAsyncSendTimeout(long)}. - */ - public void setAsyncSendTimeout(Long timeoutInMillis) { - this.asyncSendTimeout = timeoutInMillis; - } - - @Nullable - public Long getAsyncSendTimeout() { - return this.asyncSendTimeout; - } - - /** - * Exposes the underlying config option on - * {@link jakarta.websocket.server.ServerContainer#setDefaultMaxSessionIdleTimeout(long)}. - */ - public void setMaxSessionIdleTimeout(Long timeoutInMillis) { - this.maxSessionIdleTimeout = timeoutInMillis; - } - - @Nullable - public Long getMaxSessionIdleTimeout() { - return this.maxSessionIdleTimeout; - } - - /** - * Exposes the underlying config option on - * {@link jakarta.websocket.server.ServerContainer#setDefaultMaxTextMessageBufferSize(int)}. - */ - public void setMaxTextMessageBufferSize(Integer bufferSize) { - this.maxTextMessageBufferSize = bufferSize; - } - - @Nullable - public Integer getMaxTextMessageBufferSize() { - return this.maxTextMessageBufferSize; - } - - /** - * Exposes the underlying config option on - * {@link jakarta.websocket.server.ServerContainer#setDefaultMaxBinaryMessageBufferSize(int)}. - */ - public void setMaxBinaryMessageBufferSize(Integer bufferSize) { - this.maxBinaryMessageBufferSize = bufferSize; - } - - @Nullable - public Integer getMaxBinaryMessageBufferSize() { - return this.maxBinaryMessageBufferSize; - } +public class TomcatRequestUpgradeStrategy extends StandardWebSocketUpgradeStrategy { @Override - public Mono upgrade(ServerWebExchange exchange, WebSocketHandler handler, - @Nullable String subProtocol, Supplier handshakeInfoFactory){ - - ServerHttpRequest request = exchange.getRequest(); - ServerHttpResponse response = exchange.getResponse(); - - HttpServletRequest servletRequest = ServerHttpRequestDecorator.getNativeRequest(request); - HttpServletResponse servletResponse = ServerHttpResponseDecorator.getNativeResponse(response); - - HandshakeInfo handshakeInfo = handshakeInfoFactory.get(); - DataBufferFactory bufferFactory = response.bufferFactory(); - - // Trigger WebFlux preCommit actions and upgrade - return exchange.getResponse().setComplete() - .then(Mono.deferContextual(contextView -> { - Endpoint endpoint = new StandardWebSocketHandlerAdapter( - ContextWebSocketHandler.decorate(handler, contextView), - session -> new TomcatWebSocketSession(session, handshakeInfo, bufferFactory)); - - String requestURI = servletRequest.getRequestURI(); - DefaultServerEndpointConfig config = new DefaultServerEndpointConfig(requestURI, endpoint); - config.setSubprotocols(subProtocol != null ? - Collections.singletonList(subProtocol) : Collections.emptyList()); - - WsServerContainer container = getContainer(servletRequest); - try { - container.upgradeHttpToWebSocket(servletRequest, servletResponse, config, Collections.emptyMap()); - } - catch (Exception ex) { - return Mono.error(ex); - } - return Mono.empty(); - })); - } - - private WsServerContainer getContainer(HttpServletRequest request) { - if (this.serverContainer == null) { - Object container = request.getServletContext().getAttribute(SERVER_CONTAINER_ATTR); - Assert.state(container instanceof WsServerContainer, - "ServletContext attribute 'jakarta.websocket.server.ServerContainer' not found."); - this.serverContainer = (WsServerContainer) container; - initServerContainer(this.serverContainer); - } - return this.serverContainer; - } + protected void upgradeHttpToWebSocket(HttpServletRequest request, HttpServletResponse response, + ServerEndpointConfig endpointConfig, Map pathParams) throws Exception { - private void initServerContainer(ServerContainer serverContainer) { - if (this.asyncSendTimeout != null) { - serverContainer.setAsyncSendTimeout(this.asyncSendTimeout); - } - if (this.maxSessionIdleTimeout != null) { - serverContainer.setDefaultMaxSessionIdleTimeout(this.maxSessionIdleTimeout); - } - if (this.maxTextMessageBufferSize != null) { - serverContainer.setDefaultMaxTextMessageBufferSize(this.maxTextMessageBufferSize); - } - if (this.maxBinaryMessageBufferSize != null) { - serverContainer.setDefaultMaxBinaryMessageBufferSize(this.maxBinaryMessageBufferSize); - } + ((WsServerContainer) getContainer(request)).upgradeHttpToWebSocket( + request, response, endpointConfig, pathParams); } } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/UndertowRequestUpgradeStrategy.java b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/UndertowRequestUpgradeStrategy.java index 91e01527ee32..60f3742318b0 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/UndertowRequestUpgradeStrategy.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/socket/server/upgrade/UndertowRequestUpgradeStrategy.java @@ -42,7 +42,7 @@ import org.springframework.web.server.ServerWebExchange; /** - * A {@link RequestUpgradeStrategy} for use with Undertow. + * A WebSocket {@code RequestUpgradeStrategy} for Undertow. * * @author Violeta Georgieva * @author Rossen Stoyanchev diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/jetty/JettyRequestUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/jetty/JettyRequestUpgradeStrategy.java index 34e6b89f01a5..4343f6103c4f 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/jetty/JettyRequestUpgradeStrategy.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/jetty/JettyRequestUpgradeStrategy.java @@ -49,7 +49,7 @@ */ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy { - private static final String[] SUPPORTED_VERSIONS = new String[] { String.valueOf(13) }; + private static final String[] SUPPORTED_VERSIONS = new String[] {"13"}; @Override 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 index a02b18b82f7a..0dea79819f86 100644 --- 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 @@ -58,8 +58,6 @@ * A base class for {@code RequestUpgradeStrategy} implementations on top of * JSR-356 based servers which include Tyrus as their WebSocket engine. * - *

Works with Tyrus 1.11 (WebLogic 12.2.1) and Tyrus 1.12 (GlassFish 4.1.1). - * * @author Rossen Stoyanchev * @author Brian Clozel * @author Juergen Hoeller @@ -68,6 +66,10 @@ */ 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; @@ -111,7 +113,7 @@ private static Constructor getEndpointConstructor() { @Override public String[] getSupportedVersions() { - return StringUtils.tokenizeToStringArray(Version.getSupportedWireProtocolVersions(), ","); + return SUPPORTED_VERSIONS; } @Override 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 0ac70abe408c..9cb810a0ce46 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 @@ -24,6 +24,7 @@ import jakarta.servlet.http.HttpServletResponse; import jakarta.websocket.Endpoint; import jakarta.websocket.Extension; +import jakarta.websocket.server.ServerEndpointConfig; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; @@ -33,24 +34,32 @@ /** * 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 * "jakarta.websocket.server.ServerContainer" ServletContext attribute. * * @author Juergen Hoeller + * @author Rossen Stoyanchev * @since 6.0 * @see jakarta.websocket.server.ServerContainer#upgradeHttpToWebSocket */ public class StandardWebSocketUpgradeStrategy extends AbstractStandardUpgradeStrategy { + private static final String[] SUPPORTED_VERSIONS = new String[] {"13"}; + + @Override public String[] getSupportedVersions() { - return new String[] {"13"}; + return SUPPORTED_VERSIONS; } @Override - public void upgradeInternal(ServerHttpRequest request, ServerHttpResponse response, + protected void upgradeInternal(ServerHttpRequest request, ServerHttpResponse response, @Nullable String selectedProtocol, List selectedExtensions, Endpoint endpoint) throws HandshakeFailureException { @@ -66,7 +75,7 @@ public void upgradeInternal(ServerHttpRequest request, ServerHttpResponse respon endpointConfig.setExtensions(selectedExtensions); try { - getContainer(servletRequest).upgradeHttpToWebSocket(servletRequest, servletResponse, endpointConfig, pathParams); + upgradeHttpToWebSocket(servletRequest, servletResponse, endpointConfig, pathParams); } catch (Exception ex) { throw new HandshakeFailureException( @@ -74,4 +83,10 @@ public void upgradeInternal(ServerHttpRequest request, ServerHttpResponse respon } } + protected void upgradeHttpToWebSocket(HttpServletRequest request, HttpServletResponse response, + ServerEndpointConfig endpointConfig, Map pathParams) throws Exception { + + getContainer(request).upgradeHttpToWebSocket(request, response, endpointConfig, pathParams); + } + } 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 index 2344cf660c38..714918c07a16 100644 --- 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 @@ -16,24 +16,16 @@ package org.springframework.web.socket.server.standard; -import java.util.Collections; -import java.util.List; import java.util.Map; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import jakarta.websocket.Endpoint; -import jakarta.websocket.Extension; +import jakarta.websocket.server.ServerEndpointConfig; import org.apache.tomcat.websocket.server.WsServerContainer; -import org.springframework.http.server.ServerHttpRequest; -import org.springframework.http.server.ServerHttpResponse; -import org.springframework.lang.Nullable; -import org.springframework.web.socket.server.HandshakeFailureException; - /** - * A WebSocket {@code RequestUpgradeStrategy} for Apache Tomcat. Compatible with - * Tomcat 10 and higher. + * 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, @@ -41,44 +33,18 @@ * "jakarta.websocket.server.ServerContainer" ServletContext attribute. * * @author Rossen Stoyanchev + * @author Juergen Hoeller * @since 4.0 - * @see WsServerContainer#upgradeHttpToWebSocket + * @see org.apache.tomcat.websocket.server.WsServerContainer#upgradeHttpToWebSocket */ -public class TomcatRequestUpgradeStrategy extends AbstractStandardUpgradeStrategy { +public class TomcatRequestUpgradeStrategy extends StandardWebSocketUpgradeStrategy { @Override - public String[] getSupportedVersions() { - return new String[] {"13"}; - } - - @Override - public void upgradeInternal(ServerHttpRequest request, ServerHttpResponse response, - @Nullable String selectedProtocol, List selectedExtensions, Endpoint endpoint) - throws HandshakeFailureException { - - HttpServletRequest servletRequest = getHttpServletRequest(request); - HttpServletResponse servletResponse = getHttpServletResponse(response); + protected void upgradeHttpToWebSocket(HttpServletRequest request, HttpServletResponse response, + ServerEndpointConfig endpointConfig, Map pathParams) throws Exception { - StringBuffer requestUrl = servletRequest.getRequestURL(); - String path = servletRequest.getRequestURI(); // shouldn't matter - Map pathParams = Collections. emptyMap(); - - ServerEndpointRegistration endpointConfig = new ServerEndpointRegistration(path, endpoint); - endpointConfig.setSubprotocols(Collections.singletonList(selectedProtocol)); - endpointConfig.setExtensions(selectedExtensions); - - try { - getContainer(servletRequest).upgradeHttpToWebSocket(servletRequest, servletResponse, endpointConfig, pathParams); - } - catch (Exception ex) { - throw new HandshakeFailureException( - "Servlet request failed to upgrade to WebSocket: " + requestUrl, ex); - } - } - - @Override - public WsServerContainer getContainer(HttpServletRequest request) { - return (WsServerContainer) super.getContainer(request); + ((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 index 4ee43f18e509..bb20bcafcaf5 100644 --- 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * 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. @@ -16,79 +16,41 @@ package org.springframework.web.socket.server.standard; -import java.io.IOException; -import java.util.Collections; -import java.util.List; import java.util.Map; -import io.undertow.websockets.core.WebSocketVersion; import io.undertow.websockets.jsr.ServerWebSocketContainer; -import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import jakarta.websocket.Endpoint; -import jakarta.websocket.Extension; - -import org.springframework.http.server.ServerHttpRequest; -import org.springframework.http.server.ServerHttpResponse; -import org.springframework.lang.Nullable; -import org.springframework.web.socket.server.HandshakeFailureException; +import jakarta.websocket.server.ServerEndpointConfig; /** * A WebSocket {@code RequestUpgradeStrategy} for WildFly and its underlying * Undertow web server. Also compatible with embedded Undertow usage. * - *

Requires Undertow 1.3.5+ as of Spring Framework 5.0. + *

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 AbstractStandardUpgradeStrategy { +public class UndertowRequestUpgradeStrategy extends StandardWebSocketUpgradeStrategy { - private static final String[] VERSIONS = new String[] { - WebSocketVersion.V13.toHttpHeaderValue(), - WebSocketVersion.V08.toHttpHeaderValue(), - WebSocketVersion.V07.toHttpHeaderValue() - }; + private static final String[] SUPPORTED_VERSIONS = new String[] {"13", "8", "7"}; @Override public String[] getSupportedVersions() { - return VERSIONS; + return SUPPORTED_VERSIONS; } @Override - protected void upgradeInternal(ServerHttpRequest request, ServerHttpResponse response, - @Nullable String selectedProtocol, List selectedExtensions, Endpoint endpoint) - throws HandshakeFailureException { - - HttpServletRequest servletRequest = getHttpServletRequest(request); - HttpServletResponse servletResponse = getHttpServletResponse(response); - - StringBuffer requestUrl = servletRequest.getRequestURL(); - String path = servletRequest.getRequestURI(); // shouldn't matter - Map pathParams = Collections.emptyMap(); - - ServerEndpointRegistration endpointConfig = new ServerEndpointRegistration(path, endpoint); - endpointConfig.setSubprotocols(Collections.singletonList(selectedProtocol)); - endpointConfig.setExtensions(selectedExtensions); + protected void upgradeHttpToWebSocket(HttpServletRequest request, HttpServletResponse response, + ServerEndpointConfig endpointConfig, Map pathParams) throws Exception { - try { - getContainer(servletRequest).doUpgrade(servletRequest, servletResponse, endpointConfig, pathParams); - } - catch (ServletException ex) { - throw new HandshakeFailureException( - "Servlet request failed to upgrade to WebSocket: " + requestUrl, ex); - } - catch (IOException ex) { - throw new HandshakeFailureException( - "Response update failed during upgrade to WebSocket: " + requestUrl, ex); - } - } - - @Override - public ServerWebSocketContainer getContainer(HttpServletRequest request) { - return (ServerWebSocketContainer) super.getContainer(request); + ((ServerWebSocketContainer) getContainer(request)).doUpgrade( + request, response, endpointConfig, pathParams); } } 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 index 24014d476775..47df6b7c8598 100644 --- 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * 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. @@ -17,22 +17,13 @@ package org.springframework.web.socket.server.standard; import java.lang.reflect.Method; -import java.util.Collections; -import java.util.List; import java.util.Map; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import jakarta.websocket.Endpoint; -import jakarta.websocket.Extension; import jakarta.websocket.server.ServerContainer; import jakarta.websocket.server.ServerEndpointConfig; -import org.springframework.http.server.ServerHttpRequest; -import org.springframework.http.server.ServerHttpResponse; -import org.springframework.lang.Nullable; -import org.springframework.web.socket.server.HandshakeFailureException; - /** * WebSphere support for upgrading an {@link HttpServletRequest} during a * WebSocket handshake. To modify properties of the underlying @@ -41,12 +32,11 @@ * Java configuration, access the container instance through the * "javax.websocket.server.ServerContainer" ServletContext attribute. * - *

Tested with WAS Liberty beta (August 2015) for the upcoming 8.5.5.7 release. - * * @author Rossen Stoyanchev + * @author Juergen Hoeller * @since 4.2.1 */ -public class WebSphereRequestUpgradeStrategy extends AbstractStandardUpgradeStrategy { +public class WebSphereRequestUpgradeStrategy extends StandardWebSocketUpgradeStrategy { private static final Method upgradeMethod; @@ -64,34 +54,11 @@ public class WebSphereRequestUpgradeStrategy extends AbstractStandardUpgradeStra @Override - public String[] getSupportedVersions() { - return new String[] {"13"}; - } - - @Override - public void upgradeInternal(ServerHttpRequest httpRequest, ServerHttpResponse httpResponse, - @Nullable String selectedProtocol, List selectedExtensions, Endpoint endpoint) - throws HandshakeFailureException { - - HttpServletRequest request = getHttpServletRequest(httpRequest); - HttpServletResponse response = getHttpServletResponse(httpResponse); + protected void upgradeHttpToWebSocket(HttpServletRequest request, HttpServletResponse response, + ServerEndpointConfig endpointConfig, Map pathParams) throws Exception { - StringBuffer requestUrl = request.getRequestURL(); - String path = request.getRequestURI(); // shouldn't matter - Map pathParams = Collections. emptyMap(); - - ServerEndpointRegistration endpointConfig = new ServerEndpointRegistration(path, endpoint); - endpointConfig.setSubprotocols(Collections.singletonList(selectedProtocol)); - endpointConfig.setExtensions(selectedExtensions); - - try { - ServerContainer container = getContainer(request); - upgradeMethod.invoke(container, request, response, endpointConfig, pathParams); - } - catch (Exception ex) { - throw new HandshakeFailureException( - "Servlet request failed to upgrade to WebSocket for " + requestUrl, ex); - } + 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 ead6f06852e1..527054eb755f 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.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; -import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; import org.springframework.web.socket.SubProtocolCapable; import org.springframework.web.socket.WebSocketExtension; @@ -47,7 +46,13 @@ 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. @@ -129,42 +134,6 @@ protected AbstractHandshakeHandler(RequestUpgradeStrategy requestUpgradeStrategy } - private static RequestUpgradeStrategy initRequestUpgradeStrategy() { - String className; - if (tomcatWsPresent) { - className = "org.springframework.web.socket.server.standard.TomcatRequestUpgradeStrategy"; - } - else if (jettyWsPresent) { - className = "org.springframework.web.socket.server.jetty.JettyRequestUpgradeStrategy"; - } - else if (undertowWsPresent) { - className = "org.springframework.web.socket.server.standard.UndertowRequestUpgradeStrategy"; - } - else if (glassfishWsPresent) { - className = "org.springframework.web.socket.server.standard.GlassFishRequestUpgradeStrategy"; - } - else if (weblogicWsPresent) { - className = "org.springframework.web.socket.server.standard.WebLogicRequestUpgradeStrategy"; - } - else if (websphereWsPresent) { - className = "org.springframework.web.socket.server.standard.WebSphereRequestUpgradeStrategy"; - } - else { - // Let's assume Jakarta WebSocket API 2.1+ - return new StandardWebSocketUpgradeStrategy(); - } - - try { - Class clazz = ClassUtils.forName(className, AbstractHandshakeHandler.class.getClassLoader()); - return (RequestUpgradeStrategy) ReflectionUtils.accessibleConstructor(clazz).newInstance(); - } - catch (Exception ex) { - throw new IllegalStateException( - "Failed to instantiate RequestUpgradeStrategy: " + className, ex); - } - } - - /** * Return the {@link RequestUpgradeStrategy} for WebSocket requests. */ @@ -425,4 +394,45 @@ protected Principal determineUser( 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(); + } + } + }