Add SockJsSessionFactory

This commit is contained in:
Rossen Stoyanchev 2013-04-11 21:50:57 -04:00
parent 3a2c15b0fd
commit 71e82069ce
9 changed files with 108 additions and 99 deletions

View File

@ -0,0 +1,29 @@
/*
* Copyright 2002-2013 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
*
* http://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.sockjs;
/**
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public interface SockJsSessionFactory<S extends SockJsSession>{
S createSession(String sessionId);
}

View File

@ -58,7 +58,7 @@ public abstract class AbstractSockJsService implements SockJsConfiguration {
private int streamBytesLimit = 128 * 1024;
private boolean jsessionIdCookieNeeded = true;
private boolean jsessionIdCookieRequired = true;
private long heartbeatTime = 25 * 1000;
@ -138,17 +138,17 @@ public abstract class AbstractSockJsService implements SockJsConfiguration {
* Set this option to indicate if a JSESSIONID cookie should be created. The
* default value is "true".
*/
public AbstractSockJsService setJsessionIdCookieNeeded(boolean jsessionIdCookieNeeded) {
this.jsessionIdCookieNeeded = jsessionIdCookieNeeded;
public AbstractSockJsService setJsessionIdCookieRequired(boolean jsessionIdCookieRequired) {
this.jsessionIdCookieRequired = jsessionIdCookieRequired;
return this;
}
/**
* Whether setting JSESSIONID cookie is necessary.
* @see #setJsessionIdCookieNeeded(boolean)
* @see #setJsessionIdCookieRequired(boolean)
*/
public boolean isJsessionIdCookieNeeded() {
return this.jsessionIdCookieNeeded;
public boolean isJsessionIdCookieRequired() {
return this.jsessionIdCookieRequired;
}
public AbstractSockJsService setHeartbeatTime(long heartbeatTime) {
@ -344,7 +344,7 @@ public abstract class AbstractSockJsService implements SockJsConfiguration {
addCorsHeaders(request, response);
addNoCacheHeaders(response);
String content = String.format(INFO_CONTENT, random.nextInt(), isJsessionIdCookieNeeded(), isWebSocketsEnabled());
String content = String.format(INFO_CONTENT, random.nextInt(), isJsessionIdCookieRequired(), isWebSocketsEnabled());
response.getBody().write(content.getBytes());
}
else if (HttpMethod.OPTIONS.equals(request.getMethod())) {

View File

@ -29,12 +29,6 @@ public interface TransportHandler {
TransportType getTransportType();
boolean canCreateSession();
SockJsSessionSupport createSession(String sessionId);
boolean handleNoSession(ServerHttpRequest request, ServerHttpResponse response);
void handleRequest(ServerHttpRequest request, ServerHttpResponse response, SockJsSessionSupport session)
throws Exception;

View File

@ -15,6 +15,9 @@
*/
package org.springframework.sockjs.server;
import java.util.Arrays;
import java.util.List;
import org.springframework.http.HttpMethod;
@ -25,30 +28,34 @@ import org.springframework.http.HttpMethod;
*/
public enum TransportType {
WEBSOCKET("websocket", HttpMethod.GET, false),
WEBSOCKET("websocket", HttpMethod.GET),
XHR("xhr", HttpMethod.POST, true),
XHR_SEND("xhr_send", HttpMethod.POST, true),
XHR("xhr", HttpMethod.POST, "cors", "jsessionid", "no_cache"),
JSONP("jsonp", HttpMethod.GET, false),
JSONP_SEND("jsonp_send", HttpMethod.POST, false),
XHR_SEND("xhr_send", HttpMethod.POST, "cors", "jsessionid", "no_cache"),
XHR_STREAMING("xhr_streaming", HttpMethod.POST, true),
EVENT_SOURCE("eventsource", HttpMethod.GET, false),
HTML_FILE("htmlfile", HttpMethod.GET, false);
JSONP("jsonp", HttpMethod.GET, "jsessionid", "no_cache"),
JSONP_SEND("jsonp_send", HttpMethod.POST, "jsessionid", "no_cache"),
XHR_STREAMING("xhr_streaming", HttpMethod.POST, "cors", "jsessionid", "no_cache"),
EVENT_SOURCE("eventsource", HttpMethod.GET, "jsessionid", "no_cache"),
HTML_FILE("htmlfile", HttpMethod.GET, "jsessionid", "no_cache");
private final String value;
private final HttpMethod httpMethod;
private final boolean corsSupported;
private final List<String> headerHints;
private TransportType(String value, HttpMethod httpMethod, boolean corsSupported) {
private TransportType(String value, HttpMethod httpMethod, String... headerHints) {
this.value = value;
this.httpMethod = httpMethod;
this.corsSupported = corsSupported;
this.headerHints = Arrays.asList(headerHints);
}
public String value() {
@ -62,11 +69,16 @@ public enum TransportType {
return this.httpMethod;
}
/**
* Are cross-domain requests (CORS) supported?
*/
public boolean isCorsSupported() {
return this.corsSupported;
public boolean setsNoCacheHeader() {
return this.headerHints.contains("no_cache");
}
public boolean supportsCors() {
return this.headerHints.contains("cors");
}
public boolean setsJsessionIdCookie() {
return this.headerHints.contains("jsessionid");
}
public static TransportType fromValue(String transportValue) {

View File

@ -34,6 +34,7 @@ import org.springframework.http.server.ServerHttpResponse;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.sockjs.SockJsHandler;
import org.springframework.sockjs.SockJsSessionFactory;
import org.springframework.sockjs.SockJsSessionSupport;
import org.springframework.sockjs.server.AbstractSockJsService;
import org.springframework.sockjs.server.TransportHandler;
@ -188,14 +189,14 @@ public class DefaultSockJsService extends AbstractSockJsService
HttpMethod supportedMethod = transportType.getHttpMethod();
if (!supportedMethod.equals(request.getMethod())) {
if (HttpMethod.OPTIONS.equals(request.getMethod()) && transportType.isCorsSupported()) {
if (HttpMethod.OPTIONS.equals(request.getMethod()) && transportType.supportsCors()) {
response.setStatusCode(HttpStatus.NO_CONTENT);
addCorsHeaders(request, response, supportedMethod, HttpMethod.OPTIONS);
addCacheHeaders(response);
}
else {
List<HttpMethod> supportedMethods = Arrays.asList(supportedMethod);
if (transportType.isCorsSupported()) {
if (transportType.supportsCors()) {
supportedMethods.add(HttpMethod.OPTIONS);
}
sendMethodNotAllowed(response, supportedMethods);
@ -204,21 +205,22 @@ public class DefaultSockJsService extends AbstractSockJsService
}
SockJsSessionSupport session = getSockJsSession(sessionId, transportHandler);
if ((session == null) && !transportHandler.handleNoSession(request, response)) {
return;
}
addNoCacheHeaders(response);
if (session != null) {
if (transportType.setsNoCacheHeader()) {
addNoCacheHeaders(response);
}
if (isJsessionIdCookieNeeded()) {
Cookie cookie = request.getCookies().getCookie("JSESSIONID");
String jsid = (cookie != null) ? cookie.getValue() : "dummy";
// TODO: bypass use of Cookie object (causes Jetty to set Expires header)
response.getHeaders().set("Set-Cookie", "JSESSIONID=" + jsid + ";path=/"); // TODO
}
if (transportType.setsJsessionIdCookie() && isJsessionIdCookieRequired()) {
Cookie cookie = request.getCookies().getCookie("JSESSIONID");
String jsid = (cookie != null) ? cookie.getValue() : "dummy";
// TODO: bypass use of Cookie object (causes Jetty to set Expires header)
response.getHeaders().set("Set-Cookie", "JSESSIONID=" + jsid + ";path=/"); // TODO
}
if (transportType.isCorsSupported()) {
addCorsHeaders(request, response);
if (transportType.supportsCors()) {
addCorsHeaders(request, response);
}
}
transportHandler.handleRequest(request, response, session);
@ -231,22 +233,22 @@ public class DefaultSockJsService extends AbstractSockJsService
return session;
}
if (!transportHandler.canCreateSession()) {
return null;
}
if (transportHandler instanceof SockJsSessionFactory) {
SockJsSessionFactory<?> sessionFactory = (SockJsSessionFactory<?>) transportHandler;
synchronized (this.sessions) {
session = this.sessions.get(sessionId);
if (session != null) {
synchronized (this.sessions) {
session = this.sessions.get(sessionId);
if (session != null) {
return session;
}
logger.debug("Creating new session with session id \"" + sessionId + "\"");
session = (SockJsSessionSupport) sessionFactory.createSession(sessionId);
this.sessions.put(sessionId, session);
return session;
}
logger.debug("Creating new session with session id \"" + sessionId + "\"");
session = transportHandler.createSession(sessionId);
this.sessions.put(sessionId, session);
return session;
}
return null;
}
}

View File

@ -51,25 +51,20 @@ public abstract class AbstractHttpReceivingTransportHandler implements Transport
}
@Override
public boolean canCreateSession() {
return false;
}
@Override
public SockJsSessionSupport createSession(String sessionId) {
throw new IllegalStateException("Transport handlers receiving messages do not create new sessions");
}
@Override
public boolean handleNoSession(ServerHttpRequest request, ServerHttpResponse response) {
response.setStatusCode(HttpStatus.NOT_FOUND);
return false;
}
@Override
public void handleRequest(ServerHttpRequest request, ServerHttpResponse response, SockJsSessionSupport session)
public final void handleRequest(ServerHttpRequest request, ServerHttpResponse response, SockJsSessionSupport session)
throws Exception {
if (session == null) {
response.setStatusCode(HttpStatus.NOT_FOUND);
return;
}
handleRequestInternal(request, response, session);
}
protected void handleRequestInternal(ServerHttpRequest request, ServerHttpResponse response,
SockJsSessionSupport session) throws Exception {
String[] messages = null;
try {
messages = readMessages(request);

View File

@ -22,6 +22,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.sockjs.SockJsSessionFactory;
import org.springframework.sockjs.SockJsSessionSupport;
import org.springframework.sockjs.server.SockJsConfiguration;
import org.springframework.sockjs.server.SockJsFrame;
@ -34,7 +35,8 @@ import org.springframework.sockjs.server.TransportHandler;
* @author Rossen Stoyanchev
* @since 4.0
*/
public abstract class AbstractHttpSendingTransportHandler implements TransportHandler {
public abstract class AbstractHttpSendingTransportHandler
implements TransportHandler, SockJsSessionFactory<SockJsSessionSupport> {
protected final Log logger = LogFactory.getLog(this.getClass());
@ -49,16 +51,6 @@ public abstract class AbstractHttpSendingTransportHandler implements TransportHa
return this.sockJsConfig;
}
@Override
public boolean canCreateSession() {
return true;
}
@Override
public boolean handleNoSession(ServerHttpRequest request, ServerHttpResponse response) {
return true;
}
@Override
public final void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
SockJsSessionSupport session) throws Exception {

View File

@ -50,21 +50,6 @@ public abstract class AbstractWebSocketTransportHandler implements TransportHand
return TransportType.WEBSOCKET;
}
@Override
public boolean canCreateSession() {
return false;
}
@Override
public SockJsSessionSupport createSession(String sessionId) {
throw new IllegalStateException("WebSocket transport handlers do not create new sessions");
}
@Override
public boolean handleNoSession(ServerHttpRequest request, ServerHttpResponse response) {
return true;
}
@Override
public void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
SockJsSessionSupport session) throws Exception {

View File

@ -33,7 +33,7 @@ public class JsonpTransportHandler extends AbstractHttpReceivingTransportHandler
}
@Override
public void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
public void handleRequestInternal(ServerHttpRequest request, ServerHttpResponse response,
SockJsSessionSupport sockJsSession) throws Exception {
if (MediaType.APPLICATION_FORM_URLENCODED.equals(request.getHeaders().getContentType())) {