|
|
|
@ -18,13 +18,15 @@ package org.springframework.web.socket.sockjs.support; |
|
|
|
|
|
|
|
|
|
|
|
import java.io.IOException; |
|
|
|
import java.io.IOException; |
|
|
|
import java.nio.charset.Charset; |
|
|
|
import java.nio.charset.Charset; |
|
|
|
import java.util.ArrayList; |
|
|
|
|
|
|
|
import java.util.Arrays; |
|
|
|
import java.util.Arrays; |
|
|
|
|
|
|
|
import java.util.Collection; |
|
|
|
import java.util.Collections; |
|
|
|
import java.util.Collections; |
|
|
|
import java.util.Date; |
|
|
|
import java.util.Date; |
|
|
|
import java.util.HashSet; |
|
|
|
import java.util.HashSet; |
|
|
|
|
|
|
|
import java.util.LinkedHashSet; |
|
|
|
import java.util.List; |
|
|
|
import java.util.List; |
|
|
|
import java.util.Random; |
|
|
|
import java.util.Random; |
|
|
|
|
|
|
|
import java.util.Set; |
|
|
|
import java.util.concurrent.TimeUnit; |
|
|
|
import java.util.concurrent.TimeUnit; |
|
|
|
import javax.servlet.http.HttpServletRequest; |
|
|
|
import javax.servlet.http.HttpServletRequest; |
|
|
|
|
|
|
|
|
|
|
|
@ -56,7 +58,7 @@ import org.springframework.web.util.WebUtils; |
|
|
|
* path resolution and handling of static SockJS requests (e.g. "/info", "/iframe.html", |
|
|
|
* path resolution and handling of static SockJS requests (e.g. "/info", "/iframe.html", |
|
|
|
* etc). Sub-classes must handle session URLs (i.e. transport-specific requests). |
|
|
|
* etc). Sub-classes must handle session URLs (i.e. transport-specific requests). |
|
|
|
* |
|
|
|
* |
|
|
|
* By default, only same origin requests are allowed. Use {@link #setAllowedOrigins(List)} |
|
|
|
* By default, only same origin requests are allowed. Use {@link #setAllowedOrigins} |
|
|
|
* to specify a list of allowed origins (a list containing "*" will allow all origins). |
|
|
|
* to specify a list of allowed origins (a list containing "*" will allow all origins). |
|
|
|
* |
|
|
|
* |
|
|
|
* @author Rossen Stoyanchev |
|
|
|
* @author Rossen Stoyanchev |
|
|
|
@ -94,10 +96,10 @@ public abstract class AbstractSockJsService implements SockJsService, CorsConfig |
|
|
|
|
|
|
|
|
|
|
|
private boolean webSocketEnabled = true; |
|
|
|
private boolean webSocketEnabled = true; |
|
|
|
|
|
|
|
|
|
|
|
private final List<String> allowedOrigins = new ArrayList<String>(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private boolean suppressCors = false; |
|
|
|
private boolean suppressCors = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected final Set<String> allowedOrigins = new LinkedHashSet<String>(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public AbstractSockJsService(TaskScheduler scheduler) { |
|
|
|
public AbstractSockJsService(TaskScheduler scheduler) { |
|
|
|
Assert.notNull(scheduler, "TaskScheduler must not be null"); |
|
|
|
Assert.notNull(scheduler, "TaskScheduler must not be null"); |
|
|
|
@ -274,6 +276,24 @@ public abstract class AbstractSockJsService implements SockJsService, CorsConfig |
|
|
|
return this.webSocketEnabled; |
|
|
|
return this.webSocketEnabled; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* This option can be used to disable automatic addition of CORS headers for |
|
|
|
|
|
|
|
* SockJS requests. |
|
|
|
|
|
|
|
* <p>The default value is "false". |
|
|
|
|
|
|
|
* @since 4.1.2 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public void setSuppressCors(boolean suppressCors) { |
|
|
|
|
|
|
|
this.suppressCors = suppressCors; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* @since 4.1.2 |
|
|
|
|
|
|
|
* @see #setSuppressCors(boolean) |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public boolean shouldSuppressCors() { |
|
|
|
|
|
|
|
return this.suppressCors; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Configure allowed {@code Origin} header values. This check is mostly |
|
|
|
* Configure allowed {@code Origin} header values. This check is mostly |
|
|
|
* designed for browsers. There is nothing preventing other types of client |
|
|
|
* designed for browsers. There is nothing preventing other types of client |
|
|
|
@ -289,36 +309,18 @@ public abstract class AbstractSockJsService implements SockJsService, CorsConfig |
|
|
|
* @see <a href="https://tools.ietf.org/html/rfc6454">RFC 6454: The Web Origin Concept</a> |
|
|
|
* @see <a href="https://tools.ietf.org/html/rfc6454">RFC 6454: The Web Origin Concept</a> |
|
|
|
* @see <a href="https://github.com/sockjs/sockjs-client#supported-transports-by-browser-html-served-from-http-or-https">SockJS supported transports by browser</a> |
|
|
|
* @see <a href="https://github.com/sockjs/sockjs-client#supported-transports-by-browser-html-served-from-http-or-https">SockJS supported transports by browser</a> |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public void setAllowedOrigins(List<String> allowedOrigins) { |
|
|
|
public void setAllowedOrigins(Collection<String> allowedOrigins) { |
|
|
|
Assert.notNull(allowedOrigins, "Allowed origin List must not be null"); |
|
|
|
Assert.notNull(allowedOrigins, "Allowed origins Collection must not be null"); |
|
|
|
this.allowedOrigins.clear(); |
|
|
|
this.allowedOrigins.clear(); |
|
|
|
this.allowedOrigins.addAll(allowedOrigins); |
|
|
|
this.allowedOrigins.addAll(allowedOrigins); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* @since 4.1.2 |
|
|
|
* @since 4.1.2 |
|
|
|
* @see #setAllowedOrigins(List) |
|
|
|
* @see #setAllowedOrigins |
|
|
|
*/ |
|
|
|
|
|
|
|
public List<String> getAllowedOrigins() { |
|
|
|
|
|
|
|
return Collections.unmodifiableList(this.allowedOrigins); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* This option can be used to disable automatic addition of CORS headers for |
|
|
|
|
|
|
|
* SockJS requests. |
|
|
|
|
|
|
|
* <p>The default value is "false". |
|
|
|
|
|
|
|
* @since 4.1.2 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public void setSuppressCors(boolean suppressCors) { |
|
|
|
|
|
|
|
this.suppressCors = suppressCors; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* @since 4.1.2 |
|
|
|
|
|
|
|
* @see #setSuppressCors(boolean) |
|
|
|
|
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public boolean shouldSuppressCors() { |
|
|
|
public Collection<String> getAllowedOrigins() { |
|
|
|
return this.suppressCors; |
|
|
|
return Collections.unmodifiableSet(this.allowedOrigins); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -465,24 +467,11 @@ public abstract class AbstractSockJsService implements SockJsService, CorsConfig |
|
|
|
String path = request.getURI().getPath(); |
|
|
|
String path = request.getURI().getPath(); |
|
|
|
int index = path.lastIndexOf('/') + 1; |
|
|
|
int index = path.lastIndexOf('/') + 1; |
|
|
|
String filename = path.substring(index); |
|
|
|
String filename = path.substring(index); |
|
|
|
return filename.indexOf(';') == -1; |
|
|
|
return (filename.indexOf(';') == -1); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
protected boolean checkOrigin(ServerHttpRequest request, ServerHttpResponse response, HttpMethod... httpMethods) |
|
|
|
* Handle request for raw WebSocket communication, i.e. without any SockJS message framing. |
|
|
|
throws IOException { |
|
|
|
*/ |
|
|
|
|
|
|
|
protected abstract void handleRawWebSocketRequest(ServerHttpRequest request, |
|
|
|
|
|
|
|
ServerHttpResponse response, WebSocketHandler webSocketHandler) throws IOException; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Handle a SockJS session URL (i.e. transport-specific request). |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
protected abstract void handleTransportRequest(ServerHttpRequest request, ServerHttpResponse response, |
|
|
|
|
|
|
|
WebSocketHandler webSocketHandler, String sessionId, String transport) throws SockJsException; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected boolean checkOrigin(ServerHttpRequest request, ServerHttpResponse response, |
|
|
|
|
|
|
|
HttpMethod... httpMethods) throws IOException { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (WebUtils.isSameOrigin(request)) { |
|
|
|
if (WebUtils.isSameOrigin(request)) { |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
@ -529,6 +518,19 @@ public abstract class AbstractSockJsService implements SockJsService, CorsConfig |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Handle request for raw WebSocket communication, i.e. without any SockJS message framing. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
protected abstract void handleRawWebSocketRequest(ServerHttpRequest request, |
|
|
|
|
|
|
|
ServerHttpResponse response, WebSocketHandler webSocketHandler) throws IOException; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Handle a SockJS session URL (i.e. transport-specific request). |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
protected abstract void handleTransportRequest(ServerHttpRequest request, ServerHttpResponse response, |
|
|
|
|
|
|
|
WebSocketHandler webSocketHandler, String sessionId, String transport) throws SockJsException; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private interface SockJsRequestHandler { |
|
|
|
private interface SockJsRequestHandler { |
|
|
|
|
|
|
|
|
|
|
|
void handle(ServerHttpRequest request, ServerHttpResponse response) throws IOException; |
|
|
|
void handle(ServerHttpRequest request, ServerHttpResponse response) throws IOException; |
|
|
|
@ -546,8 +548,8 @@ public abstract class AbstractSockJsService implements SockJsService, CorsConfig |
|
|
|
addNoCacheHeaders(response); |
|
|
|
addNoCacheHeaders(response); |
|
|
|
if (checkOrigin(request, response)) { |
|
|
|
if (checkOrigin(request, response)) { |
|
|
|
response.getHeaders().setContentType(new MediaType("application", "json", UTF8_CHARSET)); |
|
|
|
response.getHeaders().setContentType(new MediaType("application", "json", UTF8_CHARSET)); |
|
|
|
String content = String.format(INFO_CONTENT, random.nextInt(), |
|
|
|
String content = String.format( |
|
|
|
isSessionCookieNeeded(), isWebSocketEnabled()); |
|
|
|
INFO_CONTENT, random.nextInt(), isSessionCookieNeeded(), isWebSocketEnabled()); |
|
|
|
response.getBody().write(content.getBytes()); |
|
|
|
response.getBody().write(content.getBytes()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|