Skip to content

Commit 35b0c8b

Browse files
committed
Update websocket support for Jetty 9.3+
Due to a few changes in `WebSocketServerFactory` and `Session` API, our `JettyRequestUpgradeStrategy` and `JettyWebSocketSession` needed to adapt. As of 9.3.15+ and 9.4.0+, some reflection is required to support previous versions. Spring 4.3 websocket officially supports Jetty 9.1 to 9.3. Issue: SPR-14940
1 parent 75034c3 commit 35b0c8b

File tree

3 files changed

+187
-55
lines changed

3 files changed

+187
-55
lines changed

build.gradle

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ configure(allprojects) { project ->
5656
ext.jasperreportsVersion = "6.2.1" // our tests fail with JR-internal NPEs against 6.2.2 and higher
5757
ext.javamailVersion = "1.5.6"
5858
ext.jettyVersion = "9.3.13.v20161014"
59+
ext.jetty94Version = "9.4.0.v20161208" // for spring-websocket support
5960
ext.jodaVersion = "2.9.6"
6061
ext.jrubyVersion = "1.7.26" // JRuby 9000 only supported through JSR-223 (StandardScriptFactory)
6162
ext.jtaVersion = "1.2"
@@ -949,11 +950,11 @@ project("spring-websocket") {
949950
optional("org.eclipse.jetty:jetty-webapp:${jettyVersion}") {
950951
exclude group: "javax.servlet", module: "javax.servlet"
951952
}
952-
optional("org.eclipse.jetty.websocket:websocket-server:${jettyVersion}") {
953+
optional("org.eclipse.jetty.websocket:websocket-server:${jetty94Version}") {
953954
exclude group: "javax.servlet", module: "javax.servlet"
954955
}
955-
optional("org.eclipse.jetty.websocket:websocket-client:${jettyVersion}")
956-
optional("org.eclipse.jetty:jetty-client:${jettyVersion}")
956+
optional("org.eclipse.jetty.websocket:websocket-client:${jetty94Version}")
957+
optional("org.eclipse.jetty:jetty-client:${jetty94Version}")
957958
optional("io.undertow:undertow-core:${undertowVersion}")
958959
optional("io.undertow:undertow-servlet:${undertowVersion}") {
959960
exclude group: "org.jboss.spec.javax.servlet", module: "jboss-servlet-api_3.1_spec"

spring-websocket/src/main/java/org/springframework/web/socket/adapter/jetty/JettyWebSocketSession.java

Lines changed: 81 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.web.socket.adapter.jetty;
1818

1919
import java.io.IOException;
20+
import java.lang.reflect.Method;
2021
import java.net.InetSocketAddress;
2122
import java.net.URI;
2223
import java.security.Principal;
@@ -26,11 +27,14 @@
2627

2728
import org.eclipse.jetty.websocket.api.RemoteEndpoint;
2829
import org.eclipse.jetty.websocket.api.Session;
30+
import org.eclipse.jetty.websocket.api.UpgradeRequest;
31+
import org.eclipse.jetty.websocket.api.UpgradeResponse;
2932
import org.eclipse.jetty.websocket.api.WebSocketException;
3033
import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
3134

3235
import org.springframework.http.HttpHeaders;
3336
import org.springframework.util.ObjectUtils;
37+
import org.springframework.util.ReflectionUtils;
3438
import org.springframework.web.socket.BinaryMessage;
3539
import org.springframework.web.socket.CloseStatus;
3640
import org.springframework.web.socket.PingMessage;
@@ -45,10 +49,22 @@
4549
*
4650
* @author Phillip Webb
4751
* @author Rossen Stoyanchev
52+
* @author Brian Clozel
4853
* @since 4.0
4954
*/
5055
public class JettyWebSocketSession extends AbstractWebSocketSession<Session> {
5156

57+
// As of Jetty 9.4, UpgradeRequest and UpgradeResponse are interfaces instead of classes
58+
private static final boolean isJetty94;
59+
60+
private static Method getUpgradeRequest;
61+
private static Method getUpgradeResponse;
62+
private static Method getRequestURI;
63+
private static Method getHeaders;
64+
private static Method getAcceptedSubProtocol;
65+
private static Method getExtensions;
66+
private static Method getUserPrincipal;
67+
5268
private String id;
5369

5470
private URI uri;
@@ -61,6 +77,23 @@ public class JettyWebSocketSession extends AbstractWebSocketSession<Session> {
6177

6278
private Principal user;
6379

80+
static {
81+
isJetty94 = UpgradeRequest.class.isInterface();
82+
if (!isJetty94) {
83+
try {
84+
getUpgradeRequest = Session.class.getMethod("getUpgradeRequest");
85+
getUpgradeResponse = Session.class.getMethod("getUpgradeResponse");
86+
getRequestURI = UpgradeRequest.class.getMethod("getRequestURI");
87+
getHeaders = UpgradeRequest.class.getMethod("getHeaders");
88+
getAcceptedSubProtocol = UpgradeResponse.class.getMethod("getAcceptedSubProtocol");
89+
getExtensions = UpgradeResponse.class.getMethod("getExtensions");
90+
getUserPrincipal = UpgradeRequest.class.getMethod("getUserPrincipal");
91+
}
92+
catch (NoSuchMethodException ex) {
93+
throw new IllegalStateException("Incompatible Jetty API", ex);
94+
}
95+
}
96+
}
6497

6598
/**
6699
* Create a new {@link JettyWebSocketSession} instance.
@@ -164,20 +197,64 @@ public boolean isOpen() {
164197
@Override
165198
public void initializeNativeSession(Session session) {
166199
super.initializeNativeSession(session);
200+
if (isJetty94) {
201+
initializeJetty94Session(session);
202+
}
203+
else {
204+
initializeJettySession(session);
205+
}
206+
}
207+
208+
@SuppressWarnings("unchecked")
209+
private void initializeJettySession(Session session) {
210+
211+
Object request = ReflectionUtils.invokeMethod(getUpgradeRequest, session);
212+
Object response = ReflectionUtils.invokeMethod(getUpgradeResponse, session);
167213

214+
this.id = ObjectUtils.getIdentityHexString(getNativeSession());
215+
this.uri = (URI) ReflectionUtils.invokeMethod(getRequestURI, request);
216+
217+
this.headers = new HttpHeaders();
218+
this.headers.putAll((Map<String, List<String>>) ReflectionUtils.invokeMethod(getHeaders, request));
219+
this.headers = HttpHeaders.readOnlyHttpHeaders(headers);
220+
221+
this.acceptedProtocol = (String) ReflectionUtils.invokeMethod(getAcceptedSubProtocol, response);
222+
223+
List<ExtensionConfig> source = (List<ExtensionConfig>) ReflectionUtils.invokeMethod(getExtensions, response);
224+
if (source != null) {
225+
this.extensions = new ArrayList<WebSocketExtension>(source.size());
226+
for (ExtensionConfig ec : source) {
227+
this.extensions.add(new WebSocketExtension(ec.getName(), ec.getParameters()));
228+
}
229+
}
230+
else {
231+
this.extensions = new ArrayList<WebSocketExtension>(0);
232+
}
233+
234+
if (this.user == null) {
235+
this.user = (Principal) ReflectionUtils.invokeMethod(getUserPrincipal, request);
236+
}
237+
}
238+
239+
private void initializeJetty94Session(Session session) {
168240
this.id = ObjectUtils.getIdentityHexString(getNativeSession());
169241
this.uri = session.getUpgradeRequest().getRequestURI();
170242

171243
this.headers = new HttpHeaders();
172-
this.headers.putAll(getNativeSession().getUpgradeRequest().getHeaders());
244+
this.headers.putAll(session.getUpgradeRequest().getHeaders());
173245
this.headers = HttpHeaders.readOnlyHttpHeaders(headers);
174246

175247
this.acceptedProtocol = session.getUpgradeResponse().getAcceptedSubProtocol();
176248

177249
List<ExtensionConfig> source = getNativeSession().getUpgradeResponse().getExtensions();
178-
this.extensions = new ArrayList<WebSocketExtension>(source.size());
179-
for (ExtensionConfig ec : source) {
180-
this.extensions.add(new WebSocketExtension(ec.getName(), ec.getParameters()));
250+
if (source != null) {
251+
this.extensions = new ArrayList<WebSocketExtension>(source.size());
252+
for (ExtensionConfig ec : source) {
253+
this.extensions.add(new WebSocketExtension(ec.getName(), ec.getParameters()));
254+
}
255+
}
256+
else {
257+
this.extensions = new ArrayList<WebSocketExtension>(0);
181258
}
182259

183260
if (this.user == null) {

0 commit comments

Comments
 (0)