Skip to content

Commit bddcc66

Browse files
committed
WebSocketSession.getExtensions consistently exposes unmodifiable/empty list
Issue: SPR-15180 (cherry picked from commit e94fa3f)
1 parent 2024b37 commit bddcc66

File tree

4 files changed

+49
-37
lines changed

4 files changed

+49
-37
lines changed

spring-websocket/src/main/java/org/springframework/web/socket/WebSocketSession.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -46,7 +46,7 @@ public interface WebSocketSession extends Closeable {
4646
URI getUri();
4747

4848
/**
49-
* Return the headers used in the handshake request.
49+
* Return the headers used in the handshake request (never {@code null}).
5050
*/
5151
HttpHeaders getHandshakeHeaders();
5252

@@ -57,13 +57,13 @@ public interface WebSocketSession extends Closeable {
5757
* HandshakeInterceptor}. On the client side the map can be populated via
5858
* {@link org.springframework.web.socket.client.WebSocketClient
5959
* WebSocketClient} handshake methods.
60-
* @return a Map with the session attributes, never {@code null}.
60+
* @return a Map with the session attributes (never {@code null})
6161
*/
6262
Map<String, Object> getAttributes();
6363

6464
/**
65-
* Return a {@link java.security.Principal} instance containing the name of the
66-
* authenticated user.
65+
* Return a {@link java.security.Principal} instance containing the name
66+
* of the authenticated user.
6767
* <p>If the user has not been authenticated, the method returns <code>null</code>.
6868
*/
6969
Principal getPrincipal();
@@ -79,8 +79,9 @@ public interface WebSocketSession extends Closeable {
7979
InetSocketAddress getRemoteAddress();
8080

8181
/**
82-
* Return the negotiated sub-protocol or {@code null} if none was specified or
83-
* negotiated successfully.
82+
* Return the negotiated sub-protocol.
83+
* @return the protocol identifier, or {@code null} if no protocol
84+
* was specified or negotiated successfully
8485
*/
8586
String getAcceptedProtocol();
8687

@@ -105,8 +106,9 @@ public interface WebSocketSession extends Closeable {
105106
int getBinaryMessageSizeLimit();
106107

107108
/**
108-
* Return the negotiated extensions or {@code null} if none was specified or
109-
* negotiated successfully.
109+
* Determine the negotiated extensions.
110+
* @return the list of extensions, or an empty list if no extension
111+
* was specified or negotiated successfully
110112
*/
111113
List<WebSocketExtension> getExtensions();
112114

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

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.net.URI;
2323
import java.security.Principal;
2424
import java.util.ArrayList;
25+
import java.util.Collections;
2526
import java.util.List;
2627
import java.util.Map;
2728

@@ -33,6 +34,7 @@
3334
import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
3435

3536
import org.springframework.http.HttpHeaders;
37+
import org.springframework.util.CollectionUtils;
3638
import org.springframework.util.ObjectUtils;
3739
import org.springframework.util.ReflectionUtils;
3840
import org.springframework.web.socket.BinaryMessage;
@@ -213,19 +215,20 @@ private void initializeJettySessionDirectly(Session session) {
213215

214216
this.headers = new HttpHeaders();
215217
this.headers.putAll(session.getUpgradeRequest().getHeaders());
216-
this.headers = HttpHeaders.readOnlyHttpHeaders(headers);
218+
this.headers = HttpHeaders.readOnlyHttpHeaders(this.headers);
217219

218220
this.acceptedProtocol = session.getUpgradeResponse().getAcceptedSubProtocol();
219221

220-
List<ExtensionConfig> source = session.getUpgradeResponse().getExtensions();
221-
if (source != null) {
222-
this.extensions = new ArrayList<WebSocketExtension>(source.size());
223-
for (ExtensionConfig ec : source) {
224-
this.extensions.add(new WebSocketExtension(ec.getName(), ec.getParameters()));
222+
List<ExtensionConfig> jettyExtensions = session.getUpgradeResponse().getExtensions();
223+
if (!CollectionUtils.isEmpty(jettyExtensions)) {
224+
this.extensions = new ArrayList<WebSocketExtension>(jettyExtensions.size());
225+
for (ExtensionConfig jettyExtension : jettyExtensions) {
226+
this.extensions.add(new WebSocketExtension(jettyExtension.getName(), jettyExtension.getParameters()));
225227
}
228+
this.extensions = Collections.unmodifiableList(this.extensions);
226229
}
227230
else {
228-
this.extensions = new ArrayList<WebSocketExtension>(0);
231+
this.extensions = Collections.emptyList();
229232
}
230233

231234
if (this.user == null) {
@@ -243,19 +246,20 @@ private void initializeJettySessionReflectively(Session session) {
243246

244247
this.headers = new HttpHeaders();
245248
this.headers.putAll((Map<String, List<String>>) ReflectionUtils.invokeMethod(getHeaders, request));
246-
this.headers = HttpHeaders.readOnlyHttpHeaders(headers);
249+
this.headers = HttpHeaders.readOnlyHttpHeaders(this.headers);
247250

248251
this.acceptedProtocol = (String) ReflectionUtils.invokeMethod(getAcceptedSubProtocol, response);
249252

250-
List<ExtensionConfig> source = (List<ExtensionConfig>) ReflectionUtils.invokeMethod(getExtensions, response);
251-
if (source != null) {
252-
this.extensions = new ArrayList<WebSocketExtension>(source.size());
253-
for (ExtensionConfig ec : source) {
254-
this.extensions.add(new WebSocketExtension(ec.getName(), ec.getParameters()));
253+
List<ExtensionConfig> extensions = (List<ExtensionConfig>) ReflectionUtils.invokeMethod(getExtensions, response);
254+
if (!CollectionUtils.isEmpty(extensions)) {
255+
this.extensions = new ArrayList<WebSocketExtension>(extensions.size());
256+
for (ExtensionConfig extension : extensions) {
257+
this.extensions.add(new WebSocketExtension(extension.getName(), extension.getParameters()));
255258
}
259+
this.extensions = Collections.unmodifiableList(this.extensions);
256260
}
257261
else {
258-
this.extensions = new ArrayList<WebSocketExtension>(0);
262+
this.extensions = Collections.emptyList();
259263
}
260264

261265
if (this.user == null) {

spring-websocket/src/main/java/org/springframework/web/socket/adapter/standard/StandardWebSocketSession.java

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@
2121
import java.net.URI;
2222
import java.security.Principal;
2323
import java.util.ArrayList;
24+
import java.util.Collections;
2425
import java.util.List;
2526
import java.util.Map;
2627
import javax.websocket.CloseReason;
@@ -29,6 +30,7 @@
2930
import javax.websocket.Session;
3031

3132
import org.springframework.http.HttpHeaders;
33+
import org.springframework.util.CollectionUtils;
3234
import org.springframework.web.socket.BinaryMessage;
3335
import org.springframework.web.socket.CloseStatus;
3436
import org.springframework.web.socket.PingMessage;
@@ -64,8 +66,7 @@ public class StandardWebSocketSession extends AbstractWebSocketSession<Session>
6466

6567

6668
/**
67-
* Class constructor.
68-
*
69+
* Constructor for a standard WebSocket session.
6970
* @param headers the headers of the handshake request
7071
* @param attributes attributes from the HTTP handshake to associate with the WebSocket
7172
* session; the provided attributes are copied, the original map is not used.
@@ -79,8 +80,7 @@ public StandardWebSocketSession(HttpHeaders headers, Map<String, Object> attribu
7980
}
8081

8182
/**
82-
* Class constructor that associates a user with the WebSocket session.
83-
*
83+
* Constructor that associates a user with the WebSocket session.
8484
* @param headers the headers of the handshake request
8585
* @param attributes attributes from the HTTP handshake to associate with the WebSocket session
8686
* @param localAddress the address on which the request was received
@@ -181,10 +181,16 @@ public void initializeNativeSession(Session session) {
181181

182182
this.acceptedProtocol = session.getNegotiatedSubprotocol();
183183

184-
List<Extension> source = getNativeSession().getNegotiatedExtensions();
185-
this.extensions = new ArrayList<WebSocketExtension>(source.size());
186-
for (Extension ext : source) {
187-
this.extensions.add(new StandardToWebSocketExtensionAdapter(ext));
184+
List<Extension> standardExtensions = getNativeSession().getNegotiatedExtensions();
185+
if (!CollectionUtils.isEmpty(standardExtensions)) {
186+
this.extensions = new ArrayList<WebSocketExtension>(standardExtensions.size());
187+
for (Extension standardExtension : standardExtensions) {
188+
this.extensions.add(new StandardToWebSocketExtensionAdapter(standardExtension));
189+
}
190+
this.extensions = Collections.unmodifiableList(this.extensions);
191+
}
192+
else {
193+
this.extensions = Collections.emptyList();
188194
}
189195

190196
if (this.user == null) {

spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/XhrClientSockJsSession.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
1818

1919
import java.net.InetSocketAddress;
2020
import java.net.URI;
21+
import java.util.Collections;
2122
import java.util.List;
2223

2324
import org.springframework.http.HttpHeaders;
@@ -31,7 +32,6 @@
3132
import org.springframework.web.socket.WebSocketSession;
3233
import org.springframework.web.socket.sockjs.transport.TransportType;
3334

34-
3535
/**
3636
* An extension of {@link AbstractClientSockJsSession} for use with HTTP
3737
* transports simulating a WebSocket session.
@@ -58,7 +58,7 @@ public XhrClientSockJsSession(TransportRequest request, WebSocketHandler handler
5858
XhrTransport transport, SettableListenableFuture<WebSocketSession> connectFuture) {
5959

6060
super(request, handler, connectFuture);
61-
Assert.notNull(transport, "'restTemplate' is required");
61+
Assert.notNull(transport, "'transport' is required");
6262
this.transport = transport;
6363
this.headers = request.getHttpRequestHeaders();
6464
this.sendHeaders = new HttpHeaders();
@@ -111,7 +111,7 @@ public int getBinaryMessageSizeLimit() {
111111

112112
@Override
113113
public List<WebSocketExtension> getExtensions() {
114-
return null;
114+
return Collections.emptyList();
115115
}
116116

117117
@Override
@@ -121,7 +121,7 @@ protected void sendInternal(TextMessage message) {
121121

122122
@Override
123123
protected void disconnect(CloseStatus status) {
124-
// Nothing to do, XHR transports check if session is disconnected
124+
// Nothing to do: XHR transports check if session is disconnected.
125125
}
126126

127127
}

0 commit comments

Comments
 (0)