Skip to content

Commit 2a6c1f7

Browse files
committed
Simplify determination of SockJS path
The SockJS path is now passed to the SockJsService handleRequest method thus removing the need to guess it. Issue: SPR-11058
1 parent 6a18dae commit 2a6c1f7

File tree

8 files changed

+46
-242
lines changed

8 files changed

+46
-242
lines changed

spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractStompEndpointRegistration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ public StompSockJsServiceRegistration(TaskScheduler defaultTaskScheduler) {
147147
}
148148

149149
protected SockJsService getSockJsService() {
150-
return super.getSockJsService(paths);
150+
return super.getSockJsService();
151151
}
152152
}
153153

spring-websocket/src/main/java/org/springframework/web/socket/server/config/AbstractWebSocketHandlerRegistration.java

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@
1616

1717
package org.springframework.web.socket.server.config;
1818

19-
import java.util.ArrayList;
2019
import java.util.Arrays;
21-
import java.util.List;
2220

2321
import org.springframework.scheduling.TaskScheduler;
2422
import org.springframework.util.Assert;
@@ -110,7 +108,7 @@ final M getMappings() {
110108
M mappings = createMappings();
111109

112110
if (this.sockJsServiceRegistration != null) {
113-
SockJsService sockJsService = this.sockJsServiceRegistration.getSockJsService(getAllPrefixes());
111+
SockJsService sockJsService = this.sockJsServiceRegistration.getSockJsService();
114112
for (WebSocketHandler wsHandler : this.handlerMap.keySet()) {
115113
for (String path : this.handlerMap.get(wsHandler)) {
116114
String pathPattern = path.endsWith("/") ? path + "**" : path + "/**";
@@ -130,14 +128,6 @@ final M getMappings() {
130128
return mappings;
131129
}
132130

133-
private final String[] getAllPrefixes() {
134-
List<String> all = new ArrayList<String>();
135-
for (List<String> prefixes: this.handlerMap.values()) {
136-
all.addAll(prefixes);
137-
}
138-
return all.toArray(new String[all.size()]);
139-
}
140-
141131
private HandshakeHandler getOrCreateHandshakeHandler() {
142132
return (this.handshakeHandler != null) ? this.handshakeHandler : new DefaultHandshakeHandler();
143133
}

spring-websocket/src/main/java/org/springframework/web/socket/server/config/SockJsServiceRegistration.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -199,11 +199,8 @@ public SockJsServiceRegistration setInterceptors(HandshakeInterceptor... interce
199199
return this;
200200
}
201201

202-
protected SockJsService getSockJsService(String[] sockJsPrefixes) {
202+
protected SockJsService getSockJsService() {
203203
DefaultSockJsService service = createSockJsService();
204-
if (sockJsPrefixes != null) {
205-
service.setValidSockJsPrefixes(sockJsPrefixes);
206-
}
207204
if (this.clientLibraryUrl != null) {
208205
service.setSockJsClientLibraryUrl(this.clientLibraryUrl);
209206
}

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.springframework.http.server.ServletServerHttpResponse;
2929
import org.springframework.util.Assert;
3030
import org.springframework.web.HttpRequestHandler;
31+
import org.springframework.web.servlet.HandlerMapping;
3132
import org.springframework.web.socket.WebSocketHandler;
3233
import org.springframework.web.socket.support.ExceptionWebSocketHandlerDecorator;
3334
import org.springframework.web.socket.support.LoggingWebSocketHandlerDecorator;
@@ -81,11 +82,17 @@ public void handleRequest(HttpServletRequest servletRequest, HttpServletResponse
8182
ServerHttpResponse response = new ServletServerHttpResponse(servletResponse);
8283

8384
try {
84-
this.sockJsService.handleRequest(request, response, this.wsHandler);
85+
this.sockJsService.handleRequest(request, response, getSockJsPath(servletRequest), this.wsHandler);
8586
}
8687
catch (Throwable t) {
8788
throw new SockJsException("Uncaught failure in SockJS request, uri=" + request.getURI(), t);
8889
}
8990
}
9091

92+
private String getSockJsPath(HttpServletRequest servletRequest) {
93+
String attribute = HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE;
94+
String path = (String) servletRequest.getAttribute(attribute);
95+
return ((path.length() > 0) && (path.charAt(0) != '/')) ? "/" + path : path;
96+
}
97+
9198
}

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

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,6 @@
2929
* asynchronous support enabled through the ServletContext API or by adding an
3030
* {@code <async-support>true</async-support>} element to servlet and filter declarations
3131
* in web.xml.
32-
* <p>
33-
* The service can be integrated into any HTTP request handling mechanism (e.g. plain
34-
* Servlet, Spring MVC, or other). It is expected that it will be mapped, as expected
35-
* by the SockJS protocol, to a specific prefix (e.g. "/echo") including all sub-URLs
36-
* (i.e. Ant path pattern "/echo/**").
3732
*
3833
* @author Rossen Stoyanchev
3934
* @since 4.0
@@ -52,6 +47,7 @@ public interface SockJsService {
5247
*
5348
* @param request the current request
5449
* @param response the current response
50+
* @param sockJsPath the remainder of the path within the SockJS service prefix
5551
* @param handler the handler that will exchange messages with the SockJS client
5652
*
5753
* @throws SockJsException raised when request processing fails; generally, failed
@@ -64,7 +60,7 @@ public interface SockJsService {
6460
* The former is automatically added when using
6561
* {@link SockJsHttpRequestHandler}.
6662
*/
67-
void handleRequest(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler)
68-
throws SockJsException;
63+
void handleRequest(ServerHttpRequest request, ServerHttpResponse response, String sockJsPath,
64+
WebSocketHandler handler) throws SockJsException;
6965

7066
}

spring-websocket/src/main/java/org/springframework/web/socket/sockjs/support/AbstractSockJsService.java

Lines changed: 2 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,11 @@
1818

1919
import java.io.IOException;
2020
import java.nio.charset.Charset;
21-
import java.util.ArrayList;
2221
import java.util.Arrays;
23-
import java.util.Collections;
24-
import java.util.Comparator;
2522
import java.util.Date;
2623
import java.util.HashSet;
2724
import java.util.List;
2825
import java.util.Random;
29-
import java.util.concurrent.CopyOnWriteArrayList;
3026
import java.util.concurrent.TimeUnit;
3127

3228
import org.apache.commons.logging.Log;
@@ -51,14 +47,6 @@
5147
* An abstract base class for {@link SockJsService} implementations that provides SockJS
5248
* path resolution and handling of static SockJS requests (e.g. "/info", "/iframe.html",
5349
* etc). Sub-classes must handle session URLs (i.e. transport-specific requests).
54-
* <p>
55-
* This service is unaware of the underlying HTTP request processing mechanism and URL
56-
* mappings but nevertheless needs to know the "SockJS path" for a given request, i.e. the
57-
* portion of the URL path that follows the SockJS prefix. In most cases, this can be
58-
* auto-detected since the <a href="https://github.com/sockjs/sockjs-client">SockJS
59-
* client</a> sends a "greeting URL" first. However it is recommended to configure
60-
* explicitly the expected SockJS prefixes via {@link #setValidSockJsPrefixes(String...)}
61-
* to eliminate any potential issues.
6250
*
6351
* @author Rossen Stoyanchev
6452
* @since 4.0
@@ -90,10 +78,6 @@ public abstract class AbstractSockJsService implements SockJsService {
9078

9179
private final TaskScheduler taskScheduler;
9280

93-
private final List<String> validSockJsPrefixes = new ArrayList<String>();
94-
95-
private final List<String> knownSockJsPrefixes = new CopyOnWriteArrayList<String>();
96-
9781

9882
public AbstractSockJsService(TaskScheduler scheduler) {
9983
Assert.notNull(scheduler, "scheduler must not be null");
@@ -112,39 +96,6 @@ public String getName() {
11296
return this.name;
11397
}
11498

115-
/**
116-
* Use this property to configure one or more prefixes that this SockJS service is
117-
* allowed to serve. The prefix (e.g. "/echo") is needed to extract the SockJS
118-
* specific portion of the URL (e.g. "${prefix}/info", "${prefix}/iframe.html", etc).
119-
*
120-
* <p>This property is not strictly required. In most cases, the SockJS path can be
121-
* auto-detected since the initial request from the SockJS client is of the form
122-
* "{prefix}/info". Assuming the SockJS service is mapped correctly (e.g. using
123-
* Ant-style pattern "/echo/**") this should work fine. This property can be used
124-
* to configure explicitly the prefixes this service is allowed to service.
125-
*
126-
* @param prefixes the prefixes to use; prefixes do not need to include the portions
127-
* of the path that represent Servlet container context or Servlet path.
128-
*/
129-
public void setValidSockJsPrefixes(String... prefixes) {
130-
131-
this.validSockJsPrefixes.clear();
132-
for (String prefix : prefixes) {
133-
if (prefix.endsWith("/") && (prefix.length() > 1)) {
134-
prefix = prefix.substring(0, prefix.length() - 1);
135-
}
136-
this.validSockJsPrefixes.add(prefix);
137-
}
138-
139-
// sort with longest prefix at the top
140-
Collections.sort(this.validSockJsPrefixes, Collections.reverseOrder(new Comparator<String>() {
141-
@Override
142-
public int compare(String o1, String o2) {
143-
return new Integer(o1.length()).compareTo(new Integer(o2.length()));
144-
}
145-
}));
146-
}
147-
14899
/**
149100
* Transports which don't support cross-domain communication natively (e.g.
150101
* "eventsource", "htmlfile") rely on serving a simple page (using the
@@ -308,12 +259,10 @@ public boolean isWebSocketEnabled() {
308259
*/
309260
@Override
310261
public final void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
311-
WebSocketHandler wsHandler) throws SockJsException {
262+
String sockJsPath, WebSocketHandler wsHandler) throws SockJsException {
312263

313-
String sockJsPath = getSockJsPath(request);
314264
if (sockJsPath == null) {
315-
logger.warn("Could not determine SockJS path for URL \"" + request.getURI().getPath() +
316-
". Consider setting validSockJsPrefixes.");
265+
logger.warn("No SockJS path provided, URI=\"" + request.getURI());
317266
response.setStatusCode(HttpStatus.NOT_FOUND);
318267
return;
319268
}
@@ -365,70 +314,6 @@ else if (sockJsPath.equals("/websocket")) {
365314
}
366315
}
367316

368-
/**
369-
* Return the SockJS path or null if the path could not be determined.
370-
*/
371-
private String getSockJsPath(ServerHttpRequest request) {
372-
373-
String path = request.getURI().getPath();
374-
375-
// Try SockJS prefix hints
376-
if (!this.validSockJsPrefixes.isEmpty()) {
377-
for (String prefix : this.validSockJsPrefixes) {
378-
int index = path.lastIndexOf(prefix);
379-
if (index != -1) {
380-
return path.substring(index + prefix.length());
381-
}
382-
}
383-
return null;
384-
}
385-
386-
// Try SockJS info request
387-
if (path.endsWith("/info")) {
388-
addKnownSockJsPrefix(path.substring(0, path.length() - "/info".length()));
389-
return "/info";
390-
}
391-
392-
// Have we seen this prefix before (following the initial /info request)?
393-
String match = null;
394-
for (String sockJsPath : this.knownSockJsPrefixes) {
395-
if (path.startsWith(sockJsPath)) {
396-
if ((match == null) || (match.length() < sockJsPath.length())) {
397-
match = sockJsPath;
398-
}
399-
}
400-
}
401-
if (match != null) {
402-
String result = path.substring(match.length());
403-
Assert.isTrue(result.charAt(0) == '/', "Invalid SockJS path extracted from incoming path \"" +
404-
path + "\". The extracted SockJS path is \"" + result +
405-
"\". It was extracted from these known SockJS prefixes " + this.knownSockJsPrefixes +
406-
". Consider setting 'validSockJsPrefixes' on DefaultSockJsService.");
407-
return result;
408-
}
409-
410-
// Try SockJS greeting
411-
String pathNoSlash = path.endsWith("/") ? path.substring(0, path.length() - 1) : path;
412-
String lastSegment = pathNoSlash.substring(pathNoSlash.lastIndexOf('/') + 1);
413-
414-
if (!isValidTransportType(lastSegment) && !lastSegment.startsWith("iframe")) {
415-
addKnownSockJsPrefix(path);
416-
return "";
417-
}
418-
419-
return null;
420-
}
421-
422-
private void addKnownSockJsPrefix(String path) {
423-
if (this.knownSockJsPrefixes.size() > MAX_KNOWN_SOCKJS_PREFIX_COUNT) {
424-
String removed = this.knownSockJsPrefixes.remove(0);
425-
if (logger.isWarnEnabled()) {
426-
logger.warn("MAX_KNOWN_SOCKJS_PREFIX_COUNT reached, removed prefix " + removed);
427-
}
428-
}
429-
this.knownSockJsPrefixes.add(path);
430-
}
431-
432317
/**
433318
* Validate whether the given transport String extracted from the URL is a valid
434319
* SockJS transport type (regardless of whether a transport handler is configured).

0 commit comments

Comments
 (0)