11 changed files with 1006 additions and 72 deletions
@ -0,0 +1,147 @@
@@ -0,0 +1,147 @@
|
||||
/* |
||||
* Copyright 2002-2021 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.adapter; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
import java.nio.charset.StandardCharsets; |
||||
import java.util.function.Function; |
||||
|
||||
import org.eclipse.jetty.websocket.api.Session; |
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; |
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; |
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; |
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; |
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket; |
||||
import org.eclipse.jetty.websocket.api.extensions.Frame; |
||||
|
||||
import org.springframework.core.io.buffer.DataBuffer; |
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.web.reactive.socket.CloseStatus; |
||||
import org.springframework.web.reactive.socket.WebSocketHandler; |
||||
import org.springframework.web.reactive.socket.WebSocketMessage; |
||||
import org.springframework.web.reactive.socket.WebSocketMessage.Type; |
||||
import org.springframework.web.reactive.socket.WebSocketSession; |
||||
|
||||
/** |
||||
* Identical to {@link JettyWebSocketHandlerAdapter}, only excluding the |
||||
* {@code onWebSocketFrame} method, since the {@link Frame} argument has moved |
||||
* to a different package in Jetty 10. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 5.3.4 |
||||
*/ |
||||
@WebSocket |
||||
public class Jetty10WebSocketHandlerAdapter { |
||||
|
||||
private static final ByteBuffer EMPTY_PAYLOAD = ByteBuffer.wrap(new byte[0]); |
||||
|
||||
|
||||
private final WebSocketHandler delegateHandler; |
||||
|
||||
private final Function<Session, JettyWebSocketSession> sessionFactory; |
||||
|
||||
@Nullable |
||||
private JettyWebSocketSession delegateSession; |
||||
|
||||
|
||||
public Jetty10WebSocketHandlerAdapter(WebSocketHandler handler, |
||||
Function<Session, JettyWebSocketSession> sessionFactory) { |
||||
|
||||
Assert.notNull(handler, "WebSocketHandler is required"); |
||||
Assert.notNull(sessionFactory, "'sessionFactory' is required"); |
||||
this.delegateHandler = handler; |
||||
this.sessionFactory = sessionFactory; |
||||
} |
||||
|
||||
|
||||
@OnWebSocketConnect |
||||
public void onWebSocketConnect(Session session) { |
||||
this.delegateSession = this.sessionFactory.apply(session); |
||||
this.delegateHandler.handle(this.delegateSession) |
||||
.checkpoint(session.getUpgradeRequest().getRequestURI() + " [JettyWebSocketHandlerAdapter]") |
||||
.subscribe(this.delegateSession); |
||||
} |
||||
|
||||
@OnWebSocketMessage |
||||
public void onWebSocketText(String message) { |
||||
if (this.delegateSession != null) { |
||||
WebSocketMessage webSocketMessage = toMessage(Type.TEXT, message); |
||||
this.delegateSession.handleMessage(webSocketMessage.getType(), webSocketMessage); |
||||
} |
||||
} |
||||
|
||||
@OnWebSocketMessage |
||||
public void onWebSocketBinary(byte[] message, int offset, int length) { |
||||
if (this.delegateSession != null) { |
||||
ByteBuffer buffer = ByteBuffer.wrap(message, offset, length); |
||||
WebSocketMessage webSocketMessage = toMessage(Type.BINARY, buffer); |
||||
this.delegateSession.handleMessage(webSocketMessage.getType(), webSocketMessage); |
||||
} |
||||
} |
||||
|
||||
// TODO: onWebSocketFrame can't be declared without compiling against Jetty 10
|
||||
// Jetty 10: org.eclipse.jetty.websocket.api.Frame
|
||||
// Jetty 9: org.eclipse.jetty.websocket.api.extensions.Frame
|
||||
|
||||
// @OnWebSocketFrame
|
||||
// public void onWebSocketFrame(Frame frame) {
|
||||
// if (this.delegateSession != null) {
|
||||
// if (OpCode.PONG == frame.getOpCode()) {
|
||||
// ByteBuffer buffer = (frame.getPayload() != null ? frame.getPayload() : EMPTY_PAYLOAD);
|
||||
// WebSocketMessage webSocketMessage = toMessage(Type.PONG, buffer);
|
||||
// this.delegateSession.handleMessage(webSocketMessage.getType(), webSocketMessage);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
private <T> WebSocketMessage toMessage(Type type, T message) { |
||||
WebSocketSession session = this.delegateSession; |
||||
Assert.state(session != null, "Cannot create message without a session"); |
||||
if (Type.TEXT.equals(type)) { |
||||
byte[] bytes = ((String) message).getBytes(StandardCharsets.UTF_8); |
||||
DataBuffer buffer = session.bufferFactory().wrap(bytes); |
||||
return new WebSocketMessage(Type.TEXT, buffer); |
||||
} |
||||
else if (Type.BINARY.equals(type)) { |
||||
DataBuffer buffer = session.bufferFactory().wrap((ByteBuffer) message); |
||||
return new WebSocketMessage(Type.BINARY, buffer); |
||||
} |
||||
else if (Type.PONG.equals(type)) { |
||||
DataBuffer buffer = session.bufferFactory().wrap((ByteBuffer) message); |
||||
return new WebSocketMessage(Type.PONG, buffer); |
||||
} |
||||
else { |
||||
throw new IllegalArgumentException("Unexpected message type: " + message); |
||||
} |
||||
} |
||||
|
||||
@OnWebSocketClose |
||||
public void onWebSocketClose(int statusCode, String reason) { |
||||
if (this.delegateSession != null) { |
||||
this.delegateSession.handleClose(CloseStatus.create(statusCode, reason)); |
||||
} |
||||
} |
||||
|
||||
@OnWebSocketError |
||||
public void onWebSocketError(Throwable cause) { |
||||
if (this.delegateSession != null) { |
||||
this.delegateSession.handleError(cause); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,154 @@
@@ -0,0 +1,154 @@
|
||||
/* |
||||
* Copyright 2002-2021 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.lang.reflect.Method; |
||||
import java.util.function.Supplier; |
||||
|
||||
import javax.servlet.ServletContext; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import javax.servlet.http.HttpServletResponse; |
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor; |
||||
import org.aopalliance.intercept.MethodInvocation; |
||||
import reactor.core.publisher.Mono; |
||||
|
||||
import org.springframework.aop.framework.ProxyFactory; |
||||
import org.springframework.aop.target.EmptyTargetSource; |
||||
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.NonNull; |
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.util.ReflectionUtils; |
||||
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.Jetty10WebSocketHandlerAdapter; |
||||
import org.springframework.web.reactive.socket.adapter.JettyWebSocketSession; |
||||
import org.springframework.web.reactive.socket.server.RequestUpgradeStrategy; |
||||
import org.springframework.web.server.ServerWebExchange; |
||||
|
||||
/** |
||||
* A {@link RequestUpgradeStrategy} for use with Jetty 10. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 5.3.4 |
||||
*/ |
||||
public class Jetty10RequestUpgradeStrategy implements RequestUpgradeStrategy { |
||||
|
||||
private static final Class<?> webSocketCreatorClass; |
||||
|
||||
private static final Method getContainerMethod; |
||||
|
||||
private static final Method upgradeMethod; |
||||
|
||||
private static final Method setAcceptedSubProtocol; |
||||
|
||||
static { |
||||
ClassLoader loader = Jetty10RequestUpgradeStrategy.class.getClassLoader(); |
||||
try { |
||||
webSocketCreatorClass = loader.loadClass("org.eclipse.jetty.websocket.server.JettyWebSocketCreator"); |
||||
|
||||
Class<?> type = loader.loadClass("org.eclipse.jetty.websocket.server.JettyWebSocketServerContainer"); |
||||
getContainerMethod = type.getMethod("getContainer", ServletContext.class); |
||||
upgradeMethod = ReflectionUtils.findMethod(type, "upgrade", (Class<?>[]) null); |
||||
|
||||
type = loader.loadClass("org.eclipse.jetty.websocket.server.JettyServerUpgradeResponse"); |
||||
setAcceptedSubProtocol = type.getMethod("setAcceptedSubProtocol", String.class); |
||||
} |
||||
catch (Exception ex) { |
||||
throw new IllegalStateException("No compatible Jetty version found", ex); |
||||
} |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public Mono<Void> upgrade( |
||||
ServerWebExchange exchange, WebSocketHandler handler, |
||||
@Nullable String subProtocol, Supplier<HandshakeInfo> handshakeInfoFactory) { |
||||
|
||||
ServerHttpRequest request = exchange.getRequest(); |
||||
ServerHttpResponse response = exchange.getResponse(); |
||||
|
||||
HttpServletRequest servletRequest = ServerHttpRequestDecorator.getNativeRequest(request); |
||||
HttpServletResponse servletResponse = ServerHttpResponseDecorator.getNativeResponse(response); |
||||
ServletContext servletContext = servletRequest.getServletContext(); |
||||
|
||||
HandshakeInfo handshakeInfo = handshakeInfoFactory.get(); |
||||
DataBufferFactory factory = response.bufferFactory(); |
||||
|
||||
// Trigger WebFlux preCommit actions and upgrade
|
||||
return exchange.getResponse().setComplete() |
||||
.then(Mono.deferContextual(contextView -> { |
||||
Jetty10WebSocketHandlerAdapter adapter = new Jetty10WebSocketHandlerAdapter( |
||||
ContextWebSocketHandler.decorate(handler, contextView), |
||||
session -> new JettyWebSocketSession(session, handshakeInfo, factory)); |
||||
|
||||
try { |
||||
Object creator = createJettyWebSocketCreator(adapter, subProtocol); |
||||
Object container = ReflectionUtils.invokeMethod(getContainerMethod, null, servletContext); |
||||
ReflectionUtils.invokeMethod(upgradeMethod, container, creator, servletRequest, servletResponse); |
||||
} |
||||
catch (Exception ex) { |
||||
return Mono.error(ex); |
||||
} |
||||
return Mono.empty(); |
||||
})); |
||||
} |
||||
|
||||
private static Object createJettyWebSocketCreator( |
||||
Jetty10WebSocketHandlerAdapter adapter, @Nullable String protocol) { |
||||
|
||||
ProxyFactory factory = new ProxyFactory(EmptyTargetSource.INSTANCE); |
||||
factory.addInterface(webSocketCreatorClass); |
||||
factory.addAdvice(new WebSocketCreatorInterceptor(adapter, protocol)); |
||||
return factory.getProxy(); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Proxy for a JettyWebSocketCreator to supply the WebSocket handler and set the sub-protocol. |
||||
*/ |
||||
private static class WebSocketCreatorInterceptor implements MethodInterceptor { |
||||
|
||||
private final Jetty10WebSocketHandlerAdapter adapter; |
||||
|
||||
@Nullable |
||||
private final String protocol; |
||||
|
||||
|
||||
public WebSocketCreatorInterceptor( |
||||
Jetty10WebSocketHandlerAdapter adapter, @Nullable String protocol) { |
||||
|
||||
this.adapter = adapter; |
||||
this.protocol = protocol; |
||||
} |
||||
|
||||
@Nullable |
||||
@Override |
||||
public Object invoke(@NonNull MethodInvocation invocation) { |
||||
if (this.protocol != null) { |
||||
ReflectionUtils.invokeMethod( |
||||
setAcceptedSubProtocol, invocation.getArguments()[2], this.protocol); |
||||
} |
||||
return this.adapter; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,137 @@
@@ -0,0 +1,137 @@
|
||||
/* |
||||
* Copyright 2002-2021 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.adapter.jetty; |
||||
|
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
import org.eclipse.jetty.websocket.api.Session; |
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; |
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; |
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; |
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; |
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket; |
||||
import org.eclipse.jetty.websocket.api.extensions.Frame; |
||||
|
||||
import org.springframework.util.Assert; |
||||
import org.springframework.web.socket.BinaryMessage; |
||||
import org.springframework.web.socket.CloseStatus; |
||||
import org.springframework.web.socket.TextMessage; |
||||
import org.springframework.web.socket.WebSocketHandler; |
||||
import org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator; |
||||
|
||||
/** |
||||
* Identical to {@link JettyWebSocketHandlerAdapter}, only excluding the |
||||
* {@code onWebSocketFrame} method, since the {@link Frame} argument has moved |
||||
* to a different package in Jetty 10. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 5.3.4 |
||||
*/ |
||||
@WebSocket |
||||
public class Jetty10WebSocketHandlerAdapter { |
||||
|
||||
private static final Log logger = LogFactory.getLog(Jetty10WebSocketHandlerAdapter.class); |
||||
|
||||
|
||||
private final WebSocketHandler webSocketHandler; |
||||
|
||||
private final JettyWebSocketSession wsSession; |
||||
|
||||
|
||||
public Jetty10WebSocketHandlerAdapter(WebSocketHandler webSocketHandler, JettyWebSocketSession wsSession) { |
||||
Assert.notNull(webSocketHandler, "WebSocketHandler must not be null"); |
||||
Assert.notNull(wsSession, "WebSocketSession must not be null"); |
||||
this.webSocketHandler = webSocketHandler; |
||||
this.wsSession = wsSession; |
||||
} |
||||
|
||||
|
||||
@OnWebSocketConnect |
||||
public void onWebSocketConnect(Session session) { |
||||
try { |
||||
this.wsSession.initializeNativeSession(session); |
||||
this.webSocketHandler.afterConnectionEstablished(this.wsSession); |
||||
} |
||||
catch (Exception ex) { |
||||
ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, ex, logger); |
||||
} |
||||
} |
||||
|
||||
@OnWebSocketMessage |
||||
public void onWebSocketText(String payload) { |
||||
TextMessage message = new TextMessage(payload); |
||||
try { |
||||
this.webSocketHandler.handleMessage(this.wsSession, message); |
||||
} |
||||
catch (Exception ex) { |
||||
ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, ex, logger); |
||||
} |
||||
} |
||||
|
||||
@OnWebSocketMessage |
||||
public void onWebSocketBinary(byte[] payload, int offset, int length) { |
||||
BinaryMessage message = new BinaryMessage(payload, offset, length, true); |
||||
try { |
||||
this.webSocketHandler.handleMessage(this.wsSession, message); |
||||
} |
||||
catch (Exception ex) { |
||||
ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, ex, logger); |
||||
} |
||||
} |
||||
|
||||
// TODO: onWebSocketFrame can't be declared without compiling against Jetty 10
|
||||
// Jetty 10: org.eclipse.jetty.websocket.api.Frame
|
||||
// Jetty 9: org.eclipse.jetty.websocket.api.extensions.Frame
|
||||
|
||||
// @OnWebSocketFrame
|
||||
// public void onWebSocketFrame(Frame frame) {
|
||||
// if (OpCode.PONG == frame.getOpCode()) {
|
||||
// ByteBuffer payload = frame.getPayload() != null ? frame.getPayload() : EMPTY_PAYLOAD;
|
||||
// PongMessage message = new PongMessage(payload);
|
||||
// try {
|
||||
// this.webSocketHandler.handleMessage(this.wsSession, message);
|
||||
// }
|
||||
// catch (Exception ex) {
|
||||
// ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, ex, logger);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
@OnWebSocketClose |
||||
public void onWebSocketClose(int statusCode, String reason) { |
||||
CloseStatus closeStatus = new CloseStatus(statusCode, reason); |
||||
try { |
||||
this.webSocketHandler.afterConnectionClosed(this.wsSession, closeStatus); |
||||
} |
||||
catch (Exception ex) { |
||||
if (logger.isWarnEnabled()) { |
||||
logger.warn("Unhandled exception after connection closed for " + this, ex); |
||||
} |
||||
} |
||||
} |
||||
|
||||
@OnWebSocketError |
||||
public void onWebSocketError(Throwable cause) { |
||||
try { |
||||
this.webSocketHandler.handleTransportError(this.wsSession, cause); |
||||
} |
||||
catch (Exception ex) { |
||||
ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, ex, logger); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,165 @@
@@ -0,0 +1,165 @@
|
||||
/* |
||||
* Copyright 2002-2021 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.jetty; |
||||
|
||||
import java.lang.reflect.Method; |
||||
import java.lang.reflect.UndeclaredThrowableException; |
||||
import java.security.Principal; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
import javax.servlet.ServletContext; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import javax.servlet.http.HttpServletResponse; |
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor; |
||||
import org.aopalliance.intercept.MethodInvocation; |
||||
import org.eclipse.jetty.websocket.server.HandshakeRFC6455; |
||||
|
||||
import org.springframework.aop.framework.ProxyFactory; |
||||
import org.springframework.aop.target.EmptyTargetSource; |
||||
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.NonNull; |
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.ReflectionUtils; |
||||
import org.springframework.web.socket.WebSocketExtension; |
||||
import org.springframework.web.socket.WebSocketHandler; |
||||
import org.springframework.web.socket.adapter.jetty.Jetty10WebSocketHandlerAdapter; |
||||
import org.springframework.web.socket.adapter.jetty.JettyWebSocketSession; |
||||
import org.springframework.web.socket.server.HandshakeFailureException; |
||||
import org.springframework.web.socket.server.RequestUpgradeStrategy; |
||||
|
||||
/** |
||||
* A {@link RequestUpgradeStrategy} for Jetty 10. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 5.3.4 |
||||
*/ |
||||
public class Jetty10RequestUpgradeStrategy implements RequestUpgradeStrategy { |
||||
|
||||
private static final String[] SUPPORTED_VERSIONS = new String[] { String.valueOf(HandshakeRFC6455.VERSION) }; |
||||
|
||||
private static final Class<?> webSocketCreatorClass; |
||||
|
||||
private static final Method getContainerMethod; |
||||
|
||||
private static final Method upgradeMethod; |
||||
|
||||
private static final Method setAcceptedSubProtocol; |
||||
|
||||
static { |
||||
ClassLoader loader = Jetty10RequestUpgradeStrategy.class.getClassLoader(); |
||||
try { |
||||
webSocketCreatorClass = loader.loadClass("org.eclipse.jetty.websocket.server.JettyWebSocketCreator"); |
||||
|
||||
Class<?> type = loader.loadClass("org.eclipse.jetty.websocket.server.JettyWebSocketServerContainer"); |
||||
getContainerMethod = type.getMethod("getContainer", ServletContext.class); |
||||
upgradeMethod = ReflectionUtils.findMethod(type, "upgrade", (Class<?>[]) null); |
||||
|
||||
type = loader.loadClass("org.eclipse.jetty.websocket.server.JettyServerUpgradeResponse"); |
||||
setAcceptedSubProtocol = type.getMethod("setAcceptedSubProtocol", String.class); |
||||
} |
||||
catch (Exception ex) { |
||||
throw new IllegalStateException("No compatible Jetty version found", ex); |
||||
} |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public String[] getSupportedVersions() { |
||||
return SUPPORTED_VERSIONS; |
||||
} |
||||
|
||||
@Override |
||||
public List<WebSocketExtension> getSupportedExtensions(ServerHttpRequest request) { |
||||
return Collections.emptyList(); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void upgrade(ServerHttpRequest request, ServerHttpResponse response, |
||||
@Nullable String selectedProtocol, List<WebSocketExtension> selectedExtensions, |
||||
@Nullable Principal user, WebSocketHandler handler, Map<String, Object> attributes) |
||||
throws HandshakeFailureException { |
||||
|
||||
Assert.isInstanceOf(ServletServerHttpRequest.class, request, "ServletServerHttpRequest required"); |
||||
HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest(); |
||||
ServletContext servletContext = servletRequest.getServletContext(); |
||||
|
||||
Assert.isInstanceOf(ServletServerHttpResponse.class, response, "ServletServerHttpResponse required"); |
||||
HttpServletResponse servletResponse = ((ServletServerHttpResponse) response).getServletResponse(); |
||||
|
||||
JettyWebSocketSession session = new JettyWebSocketSession(attributes, user); |
||||
Jetty10WebSocketHandlerAdapter handlerAdapter = new Jetty10WebSocketHandlerAdapter(handler, session); |
||||
|
||||
try { |
||||
Object creator = createJettyWebSocketCreator(handlerAdapter, selectedProtocol); |
||||
Object container = ReflectionUtils.invokeMethod(getContainerMethod, null, servletContext); |
||||
ReflectionUtils.invokeMethod(upgradeMethod, container, creator, servletRequest, servletResponse); |
||||
} |
||||
catch (UndeclaredThrowableException ex) { |
||||
throw new HandshakeFailureException("Failed to upgrade", ex.getUndeclaredThrowable()); |
||||
} |
||||
catch (Exception ex) { |
||||
throw new HandshakeFailureException("Failed to upgrade", ex); |
||||
} |
||||
} |
||||
|
||||
private static Object createJettyWebSocketCreator( |
||||
Jetty10WebSocketHandlerAdapter adapter, @Nullable String protocol) { |
||||
|
||||
ProxyFactory factory = new ProxyFactory(EmptyTargetSource.INSTANCE); |
||||
factory.addInterface(webSocketCreatorClass); |
||||
factory.addAdvice(new WebSocketCreatorInterceptor(adapter, protocol)); |
||||
return factory.getProxy(); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Proxy for a JettyWebSocketCreator to supply the WebSocket handler and set the sub-protocol. |
||||
*/ |
||||
private static class WebSocketCreatorInterceptor implements MethodInterceptor { |
||||
|
||||
private final Jetty10WebSocketHandlerAdapter adapter; |
||||
|
||||
@Nullable |
||||
private final String protocol; |
||||
|
||||
|
||||
public WebSocketCreatorInterceptor( |
||||
Jetty10WebSocketHandlerAdapter adapter, @Nullable String protocol) { |
||||
|
||||
this.adapter = adapter; |
||||
this.protocol = protocol; |
||||
} |
||||
|
||||
@Nullable |
||||
@Override |
||||
public Object invoke(@NonNull MethodInvocation invocation) { |
||||
if (this.protocol != null) { |
||||
ReflectionUtils.invokeMethod( |
||||
setAcceptedSubProtocol, invocation.getArguments()[2], this.protocol); |
||||
} |
||||
return this.adapter; |
||||
} |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue