Refactor and polish SockJS package structure

After this change the top-level sockjs package contains the main types
for use in applications.
This commit is contained in:
Rossen Stoyanchev 2013-07-31 21:37:39 -04:00
parent dad7115c23
commit ad40ecff4b
49 changed files with 557 additions and 467 deletions

View File

@ -1,63 +0,0 @@
/*
* 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.web.socket.sockjs;
import org.springframework.scheduling.TaskScheduler;
/**
* SockJS configuration options that need to be made available to {@link TransportHandler}
* s and SockJS sessions.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public interface SockJsConfiguration {
/**
* Streaming transports save responses on the client side and don't free
* memory used by delivered messages. Such transports need to recycle the
* connection once in a while. This property sets a minimum number of bytes
* that can be send over a single HTTP streaming request before it will be
* closed. After that client will open a new request. Setting this value to
* one effectively disables streaming and will make streaming transports to
* behave like polling transports.
*
* <p>The default value is 128K (i.e. 128 * 1024).
*/
int getStreamBytesLimit();
/**
* The amount of time in milliseconds when the server has not sent any
* messages and after which the server should send a heartbeat frame to the
* client in order to keep the connection from breaking.
*
* <p>The default value is 25,000 (25 seconds).
*/
long getHeartbeatTime();
/**
* A scheduler instance to use for scheduling heart-beat messages.
*/
TaskScheduler getTaskScheduler();
/**
* The codec to use for encoding and decoding SockJS messages.
* @exception IllegalStateException if no {@link SockJsMessageCodec} is available
*/
SockJsMessageCodec getMessageCodecRequired();
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.support;
package org.springframework.web.socket.sockjs;
import java.io.IOException;
@ -29,20 +29,12 @@ import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.util.Assert;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.SockJsService;
import org.springframework.web.socket.support.ExceptionWebSocketHandlerDecorator;
import org.springframework.web.socket.support.LoggingWebSocketHandlerDecorator;
/**
* An {@link HttpRequestHandler} for processing SockJS requests. This is the main class
* to use when configuring a SockJS service at a specific URL. It is a very thin wrapper
* around a {@link SockJsService} and a {@link WebSocketHandler} instance also adapting
* the {@link HttpServletRequest} and {@link HttpServletResponse} to
* {@link ServerHttpRequest} and {@link ServerHttpResponse} respectively.
*
* <p>The {@link #decorateWebSocketHandler(WebSocketHandler)} method decorates the given
* WebSocketHandler with a logging and exception handling decorators. This method can be
* overridden to change that.
* An {@link HttpRequestHandler} that allows mapping a {@link SockJsService} to requests
* in a Servlet container.
*
* @author Rossen Stoyanchev
* @since 4.0

View File

@ -17,37 +17,31 @@
package org.springframework.web.socket.sockjs;
import org.springframework.core.NestedRuntimeException;
import org.springframework.web.socket.WebSocketHandler;
/**
* Raised when a TransportHandler fails during request processing. If the underlying
* exception occurs while sending messages to the client, the session is closed and the
* {@link WebSocketHandler} notified.
*
* <p>If the underlying exception occurs while processing an incoming HTTP request,
* including over HTTP POST, the session will remain open. Only the incoming request is
* rejected.
* Raised when SockJS request handling fails.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
@SuppressWarnings("serial")
public class TransportErrorException extends NestedRuntimeException {
public class SockJsProcessingException extends NestedRuntimeException {
private final String sockJsSessionId;
private final String sessionId;
public TransportErrorException(String msg, Throwable cause, String sockJsSessionId) {
public SockJsProcessingException(String msg, Throwable cause, String sessionId) {
super(msg, cause);
this.sockJsSessionId = sockJsSessionId;
this.sessionId = sessionId;
}
public String getSockJsSessionId() {
return this.sockJsSessionId;
return this.sessionId;
}
@Override
public String getMessage() {
return "Transport error for SockJS session id=" + this.sockJsSessionId + ", " + super.getMessage();
return "Transport error for SockJS session id=" + this.sessionId + ", " + super.getMessage();
}
}

View File

@ -23,16 +23,17 @@ import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.socket.WebSocketHandler;
/**
* A service for processing SockJS requests.
* A service for processing SockJS HTTP requests.
*
* @author Rossen Stoyanchev
* @since 4.0
*
* @see org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler
* @see SockJsHttpRequestHandler
*/
public interface SockJsService {
void handleRequest(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler)
throws IOException, TransportErrorException;
throws IOException, SockJsProcessingException;
}

View File

@ -15,7 +15,7 @@
*/
/**
* Common abstractions for the SockJS protocol.
* Top-level SockJS types.
*/
package org.springframework.web.socket.sockjs;

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs;
package org.springframework.web.socket.sockjs.support;
import java.io.IOException;
import java.nio.charset.Charset;
@ -44,6 +44,8 @@ import org.springframework.util.DigestUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.SockJsProcessingException;
import org.springframework.web.socket.sockjs.SockJsService;
/**
* An abstract base class for {@link SockJsService} implementations that provides SockJS
@ -63,7 +65,7 @@ import org.springframework.web.socket.WebSocketHandler;
* @author Rossen Stoyanchev
* @since 4.0
*/
public abstract class AbstractSockJsService implements SockJsService, SockJsConfiguration {
public abstract class AbstractSockJsService implements SockJsService {
protected final Log logger = LogFactory.getLog(getClass());
@ -153,9 +155,8 @@ public abstract class AbstractSockJsService implements SockJsService, SockJsConf
* <p>By default this is set to point to
* "https://d1fxtkz8shb9d2.cloudfront.net/sockjs-0.3.4.min.js".
*/
public AbstractSockJsService setSockJsClientLibraryUrl(String clientLibraryUrl) {
public void setSockJsClientLibraryUrl(String clientLibraryUrl) {
this.clientLibraryUrl = clientLibraryUrl;
return this;
}
/**
@ -166,12 +167,21 @@ public abstract class AbstractSockJsService implements SockJsService, SockJsConf
return this.clientLibraryUrl;
}
public AbstractSockJsService setStreamBytesLimit(int streamBytesLimit) {
/**
* Streaming transports save responses on the client side and don't free
* memory used by delivered messages. Such transports need to recycle the
* connection once in a while. This property sets a minimum number of bytes
* that can be send over a single HTTP streaming request before it will be
* closed. After that client will open a new request. Setting this value to
* one effectively disables streaming and will make streaming transports to
* behave like polling transports.
*
* <p>The default value is 128K (i.e. 128 * 1024).
*/
public void setStreamBytesLimit(int streamBytesLimit) {
this.streamBytesLimit = streamBytesLimit;
return this;
}
@Override
public int getStreamBytesLimit() {
return this.streamBytesLimit;
}
@ -184,9 +194,8 @@ public abstract class AbstractSockJsService implements SockJsService, SockJsConf
* <p>Set this option to indicate if a JSESSIONID cookie should be created. The
* default value is "true".
*/
public AbstractSockJsService setJsessionIdCookieRequired(boolean jsessionIdCookieRequired) {
public void setJsessionIdCookieRequired(boolean jsessionIdCookieRequired) {
this.jsessionIdCookieRequired = jsessionIdCookieRequired;
return this;
}
/**
@ -197,17 +206,24 @@ public abstract class AbstractSockJsService implements SockJsService, SockJsConf
return this.jsessionIdCookieRequired;
}
public AbstractSockJsService setHeartbeatTime(long heartbeatTime) {
/**
* The amount of time in milliseconds when the server has not sent any
* messages and after which the server should send a heartbeat frame to the
* client in order to keep the connection from breaking.
*
* <p>The default value is 25,000 (25 seconds).
*/
public void setHeartbeatTime(long heartbeatTime) {
this.heartbeatTime = heartbeatTime;
return this;
}
@Override
public long getHeartbeatTime() {
return this.heartbeatTime;
}
@Override
/**
* A scheduler instance to use for scheduling heart-beat messages.
*/
public TaskScheduler getTaskScheduler() {
return this.taskScheduler;
}
@ -253,7 +269,7 @@ public abstract class AbstractSockJsService implements SockJsService, SockJsConf
*/
@Override
public final void handleRequest(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler)
throws IOException, TransportErrorException {
throws IOException, SockJsProcessingException {
String sockJsPath = getSockJsPath(request);
if (sockJsPath == null) {
@ -307,7 +323,7 @@ public abstract class AbstractSockJsService implements SockJsService, SockJsConf
return;
}
handleTransportRequest(request, response, sessionId, TransportType.fromValue(transport), handler);
handleTransportRequest(request, response, handler, sessionId, transport);
}
finally {
response.flush();
@ -321,7 +337,7 @@ public abstract class AbstractSockJsService implements SockJsService, SockJsConf
String path = request.getURI().getPath();
// SockJS prefix hints?
// Try SockJS prefix hints
if (!this.validSockJsPrefixes.isEmpty()) {
for (String prefix : this.validSockJsPrefixes) {
int index = path.indexOf(prefix);
@ -333,7 +349,7 @@ public abstract class AbstractSockJsService implements SockJsService, SockJsConf
return null;
}
// SockJS info request?
// Try SockJS info request
if (path.endsWith("/info")) {
this.knownSockJsPrefixes.add(path.substring(0, path.length() - "/info".length()));
return "/info";
@ -357,11 +373,11 @@ public abstract class AbstractSockJsService implements SockJsService, SockJsConf
return result;
}
// SockJS greeting?
// Try SockJS greeting
String pathNoSlash = path.endsWith("/") ? path.substring(0, path.length() - 1) : path;
String lastSegment = pathNoSlash.substring(pathNoSlash.lastIndexOf('/') + 1);
if ((TransportType.fromValue(lastSegment) == null) && !lastSegment.startsWith("iframe")) {
if (!isValidTransportType(lastSegment) && !lastSegment.startsWith("iframe")) {
this.knownSockJsPrefixes.add(path);
return "";
}
@ -369,12 +385,14 @@ public abstract class AbstractSockJsService implements SockJsService, SockJsConf
return null;
}
protected abstract boolean isValidTransportType(String transportType);
protected abstract void handleRawWebSocketRequest(ServerHttpRequest request,
ServerHttpResponse response, WebSocketHandler webSocketHandler) throws IOException;
protected abstract void handleTransportRequest(ServerHttpRequest request, ServerHttpResponse response,
String sessionId, TransportType transportType, WebSocketHandler webSocketHandler)
throws IOException, TransportErrorException;
WebSocketHandler webSocketHandler, String sessionId, String transport)
throws IOException, SockJsProcessingException;
protected boolean validateRequest(String serverId, String sessionId, String transport) {
@ -390,7 +408,7 @@ public abstract class AbstractSockJsService implements SockJsService, SockJsConf
return false;
}
if (!isWebSocketEnabled() && transport.equals(TransportType.WEBSOCKET.value())) {
if (!isWebSocketEnabled() && transport.equals("websocket")) {
logger.warn("Websocket transport is disabled");
return false;
}

View File

@ -14,10 +14,9 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.support;
package org.springframework.web.socket.sockjs.support.frame;
import org.springframework.util.Assert;
import org.springframework.web.socket.sockjs.SockJsMessageCodec;
/**

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.support;
package org.springframework.web.socket.sockjs.support.frame;
import java.io.IOException;
import java.io.InputStream;

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.support;
package org.springframework.web.socket.sockjs.support.frame;
import java.io.IOException;
import java.io.InputStream;

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs;
package org.springframework.web.socket.sockjs.support.frame;
import java.nio.charset.Charset;

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs;
package org.springframework.web.socket.sockjs.support.frame;
import java.io.IOException;
import java.io.InputStream;

View File

@ -0,0 +1,22 @@
/*
* 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.
*/
/**
* Support classes for creating SockJS frames including the encoding and decoding
* of SockJS message frames.
*/
package org.springframework.web.socket.sockjs.support.frame;

View File

@ -15,7 +15,9 @@
*/
/**
* Support classes for the SockJS implementation.
* Support classes for SockJS including an
* {@link org.springframework.web.socket.sockjs.support.AbstractSockJsService}
* implementation.
*/
package org.springframework.web.socket.sockjs.support;

View File

@ -14,31 +14,25 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs;
package org.springframework.web.socket.sockjs.transport;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.sockjs.SockJsProcessingException;
/**
* A contract for SockJS transport implementations. A {@link TransportHandler} is closely
* related to and often delegates to an {@link AbstractSockJsSession}. In fact most
* transports are also implementations of {@link SockJsSessionFactory} with the only exception
* to that being HTTP transports that receive messages as they depend on finding an existing
* session. See {@link TransportType} for a list of all available transport types.
* Handles SockJS session URLs.
*
* @author Rossen Stoyanchev
* @since 4.0
*
* @see SockJsService
*/
public interface TransportHandler {
TransportType getTransportType();
void setSockJsConfiguration(SockJsConfiguration sockJsConfig);
void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler handler, AbstractSockJsSession session) throws TransportErrorException;
WebSocketHandler handler, WebSocketSession session) throws SockJsProcessingException;
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs;
package org.springframework.web.socket.sockjs.transport;
import java.util.Arrays;
import java.util.Collections;
@ -25,7 +25,7 @@ import java.util.Map;
import org.springframework.http.HttpMethod;
/**
* SockJS server transport types.
* Defines SockJS transport types.
*
* @author Rossen Stoyanchev
* @since 4.0

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.transport;
package org.springframework.web.socket.sockjs.transport.handler;
import java.io.IOException;
import java.nio.charset.Charset;
@ -24,10 +24,11 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.AbstractSockJsSession;
import org.springframework.web.socket.sockjs.TransportErrorException;
import org.springframework.web.socket.sockjs.TransportHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.sockjs.SockJsProcessingException;
import org.springframework.web.socket.sockjs.transport.TransportHandler;
import org.springframework.web.socket.support.ExceptionWebSocketHandlerDecorator;
import com.fasterxml.jackson.databind.JsonMappingException;
@ -44,22 +45,16 @@ public abstract class AbstractHttpReceivingTransportHandler
@Override
public final void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler webSocketHandler, AbstractSockJsSession session) throws TransportErrorException {
if (session == null) {
response.setStatusCode(HttpStatus.NOT_FOUND);
logger.warn("Session not found");
return;
}
WebSocketHandler webSocketHandler, WebSocketSession webSocketSession) throws SockJsProcessingException {
// TODO: check "Sec-WebSocket-Protocol" header
// https://github.com/sockjs/sockjs-client/issues/130
handleRequestInternal(request, response, session);
handleRequestInternal(request, response, webSocketHandler, webSocketSession);
}
protected void handleRequestInternal(ServerHttpRequest request, ServerHttpResponse response,
AbstractSockJsSession session) throws TransportErrorException {
WebSocketHandler wsHandler, WebSocketSession wsSession) throws SockJsProcessingException {
String[] messages = null;
try {
@ -67,22 +62,22 @@ public abstract class AbstractHttpReceivingTransportHandler
}
catch (JsonMappingException ex) {
logger.error("Failed to read message: " + ex.getMessage());
sendInternalServerError(response, "Payload expected.", session.getId());
sendInternalServerError(response, "Payload expected.", wsSession.getId());
return;
}
catch (IOException ex) {
logger.error("Failed to read message: " + ex.getMessage());
sendInternalServerError(response, "Broken JSON encoding.", session.getId());
sendInternalServerError(response, "Broken JSON encoding.", wsSession.getId());
return;
}
catch (Throwable t) {
logger.error("Failed to read message: " + t.getMessage());
sendInternalServerError(response, "Failed to process messages", session.getId());
sendInternalServerError(response, "Failed to process messages", wsSession.getId());
return;
}
if (messages == null) {
sendInternalServerError(response, "Payload expected.", session.getId());
sendInternalServerError(response, "Payload expected.", wsSession.getId());
return;
}
@ -94,23 +89,25 @@ public abstract class AbstractHttpReceivingTransportHandler
response.getHeaders().setContentType(new MediaType("text", "plain", Charset.forName("UTF-8")));
try {
session.delegateMessages(messages);
for (String message : messages) {
wsHandler.handleMessage(wsSession, new TextMessage(message));
}
}
catch (Throwable t) {
ExceptionWebSocketHandlerDecorator.tryCloseWithError(session, t, logger);
throw new TransportErrorException("Unhandled WebSocketHandler error in " + this, t, session.getId());
ExceptionWebSocketHandlerDecorator.tryCloseWithError(wsSession, t, logger);
throw new SockJsProcessingException("Unhandled WebSocketHandler error in " + this, t, wsSession.getId());
}
}
protected void sendInternalServerError(ServerHttpResponse response, String error,
String sessionId) throws TransportErrorException {
String sessionId) throws SockJsProcessingException {
try {
response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
response.getBody().write(error.getBytes("UTF-8"));
}
catch (Throwable t) {
throw new TransportErrorException("Failed to send error message to client", t, sessionId);
throw new SockJsProcessingException("Failed to send error message to client", t, sessionId);
}
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.transport;
package org.springframework.web.socket.sockjs.transport.handler;
import java.io.IOException;
@ -22,12 +22,12 @@ import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.SockJsFrame;
import org.springframework.web.socket.sockjs.SockJsFrame.FrameFormat;
import org.springframework.web.socket.sockjs.AbstractSockJsSession;
import org.springframework.web.socket.sockjs.SockJsSessionFactory;
import org.springframework.web.socket.sockjs.TransportErrorException;
import org.springframework.web.socket.sockjs.TransportHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.sockjs.SockJsProcessingException;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame.FrameFormat;
import org.springframework.web.socket.sockjs.transport.TransportHandler;
import org.springframework.web.socket.sockjs.transport.session.AbstractHttpSockJsSession;
/**
* Base class for HTTP-based transports that send messages over HTTP.
@ -41,34 +41,34 @@ public abstract class AbstractHttpSendingTransportHandler extends TransportHandl
@Override
public final void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler webSocketHandler, AbstractSockJsSession session) throws TransportErrorException {
WebSocketHandler webSocketHandler, WebSocketSession session) throws SockJsProcessingException {
// Set content type before writing
response.getHeaders().setContentType(getContentType());
AbstractHttpSockJsSession httpServerSession = (AbstractHttpSockJsSession) session;
handleRequestInternal(request, response, httpServerSession);
AbstractHttpSockJsSession sockJsSession = (AbstractHttpSockJsSession) session;
handleRequestInternal(request, response, sockJsSession);
}
protected void handleRequestInternal(ServerHttpRequest request, ServerHttpResponse response,
AbstractHttpSockJsSession httpServerSession) throws TransportErrorException {
AbstractHttpSockJsSession sockJsSession) throws SockJsProcessingException {
if (httpServerSession.isNew()) {
if (sockJsSession.isNew()) {
logger.debug("Opening " + getTransportType() + " connection");
httpServerSession.setInitialRequest(request, response, getFrameFormat(request));
sockJsSession.setInitialRequest(request, response, getFrameFormat(request));
}
else if (!httpServerSession.isActive()) {
else if (!sockJsSession.isActive()) {
logger.debug("starting " + getTransportType() + " async request");
httpServerSession.setLongPollingRequest(request, response, getFrameFormat(request));
sockJsSession.setLongPollingRequest(request, response, getFrameFormat(request));
}
else {
try {
logger.debug("another " + getTransportType() + " connection still open: " + httpServerSession);
logger.debug("another " + getTransportType() + " connection still open: " + sockJsSession);
SockJsFrame closeFrame = SockJsFrame.closeFrameAnotherConnectionOpen();
response.getBody().write(getFrameFormat(request).format(closeFrame).getContentBytes());
}
catch (IOException e) {
throw new TransportErrorException("Failed to send SockJS close frame", e, httpServerSession.getId());
throw new SockJsProcessingException("Failed to send SockJS close frame", e, sockJsSession.getId());
}
}
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.transport;
package org.springframework.web.socket.sockjs.transport.handler;
import java.io.IOException;
import java.util.Arrays;
@ -39,18 +39,19 @@ import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.server.DefaultHandshakeHandler;
import org.springframework.web.socket.server.HandshakeHandler;
import org.springframework.web.socket.server.support.ServerWebSocketSessionInitializer;
import org.springframework.web.socket.sockjs.AbstractSockJsService;
import org.springframework.web.socket.sockjs.AbstractSockJsSession;
import org.springframework.web.socket.sockjs.SockJsMessageCodec;
import org.springframework.web.socket.sockjs.SockJsProcessingException;
import org.springframework.web.socket.sockjs.SockJsService;
import org.springframework.web.socket.sockjs.SockJsSessionFactory;
import org.springframework.web.socket.sockjs.TransportErrorException;
import org.springframework.web.socket.sockjs.TransportHandler;
import org.springframework.web.socket.sockjs.TransportType;
import org.springframework.web.socket.sockjs.support.Jackson2SockJsMessageCodec;
import org.springframework.web.socket.sockjs.support.AbstractSockJsService;
import org.springframework.web.socket.sockjs.support.frame.Jackson2SockJsMessageCodec;
import org.springframework.web.socket.sockjs.support.frame.SockJsMessageCodec;
import org.springframework.web.socket.sockjs.transport.TransportHandler;
import org.springframework.web.socket.sockjs.transport.TransportType;
import org.springframework.web.socket.sockjs.transport.session.AbstractSockJsSession;
import org.springframework.web.socket.sockjs.transport.session.SockJsServiceConfig;
/**
@ -159,12 +160,18 @@ public class DefaultSockJsService extends AbstractSockJsService {
protected void addTransportHandlers(Collection<TransportHandler> handlers) {
for (TransportHandler handler : handlers) {
handler.setSockJsConfiguration(this);
if (handler instanceof TransportHandlerSupport) {
((TransportHandlerSupport) handler).setSockJsServiceConfiguration(this.sockJsServiceConfig);
}
this.transportHandlers.put(handler.getTransportType(), handler);
}
}
/**
* The codec to use for encoding and decoding SockJS messages.
* @exception IllegalStateException if no {@link SockJsMessageCodec} is available
*/
public void setMessageCodec(SockJsMessageCodec messageCodec) {
this.messageCodec = messageCodec;
}
@ -173,13 +180,6 @@ public class DefaultSockJsService extends AbstractSockJsService {
return this.messageCodec;
}
@Override
public SockJsMessageCodec getMessageCodecRequired() {
Assert.state(this.messageCodec != null, "A SockJsMessageCodec is required but not available."
+ " Either add Jackson 2 or Jackson 1.x to the classpath, or configure a SockJsMessageCode");
return this.messageCodec;
}
public Map<TransportType, TransportHandler> getTransportHandlers() {
return Collections.unmodifiableMap(this.transportHandlers);
}
@ -203,11 +203,17 @@ public class DefaultSockJsService extends AbstractSockJsService {
@Override
protected void handleTransportRequest(ServerHttpRequest request, ServerHttpResponse response,
String sessionId, TransportType transportType, WebSocketHandler webSocketHandler)
throws IOException, TransportErrorException {
WebSocketHandler wsHandler, String sessionId, String transport)
throws IOException, SockJsProcessingException {
TransportType transportType = TransportType.fromValue(transport);
if (transportType == null) {
logger.debug("Unknown transport type: " + transportType);
response.setStatusCode(HttpStatus.NOT_FOUND);
return;
}
TransportHandler transportHandler = this.transportHandlers.get(transportType);
if (transportHandler == null) {
logger.debug("Transport handler not found");
response.setStatusCode(HttpStatus.NOT_FOUND);
@ -231,58 +237,60 @@ public class DefaultSockJsService extends AbstractSockJsService {
return;
}
AbstractSockJsSession session = getSockJsSession(sessionId, webSocketHandler,
transportHandler, request, response);
if (session != null) {
if (transportType.sendsNoCacheInstruction()) {
addNoCacheHeaders(response);
}
if (transportType.setsJsessionId() && 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=/");
}
if (transportType.supportsCors()) {
addCorsHeaders(request, response);
WebSocketSession session = this.sessions.get(sessionId);
if (session == null) {
if (transportHandler instanceof SockJsSessionFactory) {
SockJsSessionFactory sessionFactory = (SockJsSessionFactory) transportHandler;
session = createSockJsSession(sessionId, sessionFactory, wsHandler, request, response);
}
}
if (session == null) {
response.setStatusCode(HttpStatus.NOT_FOUND);
logger.warn("Session not found");
return;
}
transportHandler.handleRequest(request, response, webSocketHandler, session);
if (transportType.sendsNoCacheInstruction()) {
addNoCacheHeaders(response);
}
if (transportType.setsJsessionId() && 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=/");
}
if (transportType.supportsCors()) {
addCorsHeaders(request, response);
}
transportHandler.handleRequest(request, response, wsHandler, session);
}
protected AbstractSockJsSession getSockJsSession(String sessionId, WebSocketHandler handler,
TransportHandler transportHandler, ServerHttpRequest request, ServerHttpResponse response) {
private WebSocketSession createSockJsSession(String sessionId, SockJsSessionFactory sessionFactory,
WebSocketHandler handler, ServerHttpRequest request, ServerHttpResponse response) {
AbstractSockJsSession session = this.sessions.get(sessionId);
if (session != null) {
return session;
}
if (transportHandler instanceof SockJsSessionFactory) {
SockJsSessionFactory sessionFactory = (SockJsSessionFactory) transportHandler;
synchronized (this.sessions) {
session = this.sessions.get(sessionId);
if (session != null) {
return session;
}
if (this.sessionCleanupTask == null) {
scheduleSessionTask();
}
logger.debug("Creating new session with session id \"" + sessionId + "\"");
session = sessionFactory.createSession(sessionId, handler);
String protocol = null; // TODO: https://github.com/sockjs/sockjs-client/issues/130
this.sessionInitializer.initialize(request, response, protocol, session);
this.sessions.put(sessionId, session);
synchronized (this.sessions) {
AbstractSockJsSession session = this.sessions.get(sessionId);
if (session != null) {
return session;
}
if (this.sessionCleanupTask == null) {
scheduleSessionTask();
}
logger.debug("Creating new session with session id \"" + sessionId + "\"");
session = sessionFactory.createSession(sessionId, handler);
String protocol = null; // TODO: https://github.com/sockjs/sockjs-client/issues/130
this.sessionInitializer.initialize(request, response, protocol, session);
this.sessions.put(sessionId, session);
return session;
}
}
return null;
@Override
protected boolean isValidTransportType(String lastSegment) {
return TransportType.fromValue(lastSegment) != null;
}
private void scheduleSessionTask() {
@ -314,4 +322,31 @@ public class DefaultSockJsService extends AbstractSockJsService {
}, getDisconnectDelay());
}
private final SockJsServiceConfig sockJsServiceConfig = new SockJsServiceConfig() {
@Override
public int getStreamBytesLimit() {
return DefaultSockJsService.this.getStreamBytesLimit();
}
@Override
public long getHeartbeatTime() {
return DefaultSockJsService.this.getHeartbeatTime();
}
@Override
public TaskScheduler getTaskScheduler() {
return DefaultSockJsService.this.getTaskScheduler();
}
@Override
public SockJsMessageCodec getMessageCodec() {
Assert.state(DefaultSockJsService.this.getMessageCodec() != null,
"A SockJsMessageCodec is required but not available."
+ " Either add Jackson 2 or Jackson 1.x to the classpath, or configure a SockJsMessageCode");
return DefaultSockJsService.this.getMessageCodec();
}
};
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.transport;
package org.springframework.web.socket.sockjs.transport.handler;
import java.io.IOException;
import java.nio.charset.Charset;
@ -22,10 +22,11 @@ import java.nio.charset.Charset;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.SockJsConfiguration;
import org.springframework.web.socket.sockjs.SockJsFrame.DefaultFrameFormat;
import org.springframework.web.socket.sockjs.SockJsFrame.FrameFormat;
import org.springframework.web.socket.sockjs.TransportType;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame.DefaultFrameFormat;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame.FrameFormat;
import org.springframework.web.socket.sockjs.transport.TransportType;
import org.springframework.web.socket.sockjs.transport.session.SockJsServiceConfig;
import org.springframework.web.socket.sockjs.transport.session.StreamingSockJsSession;
/**
* A TransportHandler for sending messages via Server-Sent events:
@ -49,7 +50,7 @@ public class EventSourceTransportHandler extends AbstractHttpSendingTransportHan
@Override
public StreamingSockJsSession createSession(String sessionId, WebSocketHandler handler) {
return new EventSourceStreamingSockJsSession(sessionId, getSockJsConfig(), handler);
return new EventSourceStreamingSockJsSession(sessionId, getSockJsServiceConfig(), handler);
}
@Override
@ -60,7 +61,7 @@ public class EventSourceTransportHandler extends AbstractHttpSendingTransportHan
private final class EventSourceStreamingSockJsSession extends StreamingSockJsSession {
private EventSourceStreamingSockJsSession(String sessionId, SockJsConfiguration config, WebSocketHandler handler) {
private EventSourceStreamingSockJsSession(String sessionId, SockJsServiceConfig config, WebSocketHandler handler) {
super(sessionId, config, handler);
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.transport;
package org.springframework.web.socket.sockjs.transport.handler;
import java.io.IOException;
import java.nio.charset.Charset;
@ -25,12 +25,14 @@ import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.SockJsConfiguration;
import org.springframework.web.socket.sockjs.SockJsFrame.DefaultFrameFormat;
import org.springframework.web.socket.sockjs.SockJsFrame.FrameFormat;
import org.springframework.web.socket.sockjs.TransportErrorException;
import org.springframework.web.socket.sockjs.TransportHandler;
import org.springframework.web.socket.sockjs.TransportType;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame.DefaultFrameFormat;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame.FrameFormat;
import org.springframework.web.socket.sockjs.transport.TransportHandler;
import org.springframework.web.socket.sockjs.transport.TransportType;
import org.springframework.web.socket.sockjs.transport.session.AbstractHttpSockJsSession;
import org.springframework.web.socket.sockjs.transport.session.SockJsServiceConfig;
import org.springframework.web.socket.sockjs.transport.session.StreamingSockJsSession;
import org.springframework.web.socket.sockjs.SockJsProcessingException;
import org.springframework.web.util.JavaScriptUtils;
/**
@ -85,12 +87,12 @@ public class HtmlFileTransportHandler extends AbstractHttpSendingTransportHandle
@Override
public StreamingSockJsSession createSession(String sessionId, WebSocketHandler handler) {
return new HtmlFileStreamingSockJsSession(sessionId, getSockJsConfig(), handler);
return new HtmlFileStreamingSockJsSession(sessionId, getSockJsServiceConfig(), handler);
}
@Override
public void handleRequestInternal(ServerHttpRequest request, ServerHttpResponse response,
AbstractHttpSockJsSession session) throws TransportErrorException {
AbstractHttpSockJsSession session) throws SockJsProcessingException {
try {
String callback = request.getQueryParams().getFirst("c");
@ -101,7 +103,7 @@ public class HtmlFileTransportHandler extends AbstractHttpSendingTransportHandle
}
}
catch (Throwable t) {
throw new TransportErrorException("Failed to send error to client", t, session.getId());
throw new SockJsProcessingException("Failed to send error to client", t, session.getId());
}
super.handleRequestInternal(request, response, session);
}
@ -119,7 +121,7 @@ public class HtmlFileTransportHandler extends AbstractHttpSendingTransportHandle
private final class HtmlFileStreamingSockJsSession extends StreamingSockJsSession {
private HtmlFileStreamingSockJsSession(String sessionId, SockJsConfiguration config, WebSocketHandler handler) {
private HtmlFileStreamingSockJsSession(String sessionId, SockJsServiceConfig config, WebSocketHandler handler) {
super(sessionId, config, handler);
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.transport;
package org.springframework.web.socket.sockjs.transport.handler;
import java.nio.charset.Charset;
@ -24,10 +24,12 @@ import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.SockJsFrame;
import org.springframework.web.socket.sockjs.SockJsFrame.FrameFormat;
import org.springframework.web.socket.sockjs.TransportErrorException;
import org.springframework.web.socket.sockjs.TransportType;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame.FrameFormat;
import org.springframework.web.socket.sockjs.transport.TransportType;
import org.springframework.web.socket.sockjs.transport.session.AbstractHttpSockJsSession;
import org.springframework.web.socket.sockjs.transport.session.PollingSockJsSession;
import org.springframework.web.socket.sockjs.SockJsProcessingException;
import org.springframework.web.util.JavaScriptUtils;
/**
@ -50,12 +52,12 @@ public class JsonpPollingTransportHandler extends AbstractHttpSendingTransportHa
@Override
public PollingSockJsSession createSession(String sessionId, WebSocketHandler handler) {
return new PollingSockJsSession(sessionId, getSockJsConfig(), handler);
return new PollingSockJsSession(sessionId, getSockJsServiceConfig(), handler);
}
@Override
public void handleRequestInternal(ServerHttpRequest request, ServerHttpResponse response,
AbstractHttpSockJsSession session) throws TransportErrorException {
AbstractHttpSockJsSession session) throws SockJsProcessingException {
try {
String callback = request.getQueryParams().getFirst("c");
@ -66,7 +68,7 @@ public class JsonpPollingTransportHandler extends AbstractHttpSendingTransportHa
}
}
catch (Throwable t) {
throw new TransportErrorException("Failed to send error to client", t, session.getId());
throw new SockJsProcessingException("Failed to send error to client", t, session.getId());
}
super.handleRequestInternal(request, response, session);

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.transport;
package org.springframework.web.socket.sockjs.transport.handler;
import java.io.IOException;
@ -25,11 +25,12 @@ import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.sockjs.AbstractSockJsSession;
import org.springframework.web.socket.sockjs.SockJsMessageCodec;
import org.springframework.web.socket.sockjs.TransportErrorException;
import org.springframework.web.socket.sockjs.TransportHandler;
import org.springframework.web.socket.sockjs.TransportType;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.sockjs.SockJsProcessingException;
import org.springframework.web.socket.sockjs.support.frame.SockJsMessageCodec;
import org.springframework.web.socket.sockjs.transport.TransportHandler;
import org.springframework.web.socket.sockjs.transport.TransportType;
/**
* A {@link TransportHandler} that receives messages over HTTP.
@ -48,22 +49,22 @@ public class JsonpTransportHandler extends AbstractHttpReceivingTransportHandler
@Override
public void handleRequestInternal(ServerHttpRequest request, ServerHttpResponse response,
AbstractSockJsSession sockJsSession) throws TransportErrorException {
WebSocketHandler webSocketHandler, WebSocketSession webSocketSession) throws SockJsProcessingException {
super.handleRequestInternal(request, response, sockJsSession);
super.handleRequestInternal(request, response, webSocketHandler, webSocketSession);
try {
response.getBody().write("ok".getBytes("UTF-8"));
}
catch (Throwable t) {
throw new TransportErrorException("Failed to write response body", t, sockJsSession.getId());
throw new SockJsProcessingException("Failed to write response body", t, webSocketSession.getId());
}
}
@Override
protected String[] readMessages(ServerHttpRequest request) throws IOException {
SockJsMessageCodec messageCodec = getSockJsConfig().getMessageCodecRequired();
SockJsMessageCodec messageCodec = getSockJsServiceConfig().getMessageCodec();
MediaType contentType = request.getHeaders().getContentType();
if ((contentType != null) && MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(contentType)) {

View File

@ -14,9 +14,11 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs;
package org.springframework.web.socket.sockjs.transport.handler;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.transport.TransportHandler;
import org.springframework.web.socket.sockjs.transport.session.AbstractSockJsSession;
/**
* A factory for creating a SockJS session. {@link TransportHandler}s typically also serve

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.transport;
package org.springframework.web.socket.sockjs.transport.handler;
import java.util.concurrent.atomic.AtomicInteger;
@ -24,7 +24,8 @@ import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.adapter.TextWebSocketHandlerAdapter;
import org.springframework.web.socket.sockjs.SockJsConfiguration;
import org.springframework.web.socket.sockjs.transport.session.SockJsServiceConfig;
import org.springframework.web.socket.sockjs.transport.session.WebSocketServerSockJsSession;
/**
* An implementation of {@link WebSocketHandler} that adds SockJS messages frames, sends
@ -43,26 +44,26 @@ import org.springframework.web.socket.sockjs.SockJsConfiguration;
*/
public class SockJsWebSocketHandler extends TextWebSocketHandlerAdapter {
private final SockJsConfiguration sockJsConfig;
private final SockJsServiceConfig sockJsServiceConfig;
private final WebSocketServerSockJsSession session;
private final AtomicInteger sessionCount = new AtomicInteger(0);
public SockJsWebSocketHandler(SockJsConfiguration config,
public SockJsWebSocketHandler(SockJsServiceConfig sockJsServiceConfig,
WebSocketHandler webSocketHandler, WebSocketServerSockJsSession session) {
Assert.notNull(config, "config must not be null");
Assert.notNull(sockJsServiceConfig, "sockJsServiceConfig must not be null");
Assert.notNull(webSocketHandler, "webSocketHandler must not be null");
Assert.notNull(session, "session must not be null");
this.sockJsConfig = config;
this.sockJsServiceConfig = sockJsServiceConfig;
this.session = session;
}
protected SockJsConfiguration getSockJsConfig() {
return this.sockJsConfig;
protected SockJsServiceConfig getSockJsConfig() {
return this.sockJsServiceConfig;
}
@Override

View File

@ -14,11 +14,11 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.transport;
package org.springframework.web.socket.sockjs.transport.handler;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.socket.sockjs.SockJsConfiguration;
import org.springframework.web.socket.sockjs.transport.session.SockJsServiceConfig;
/**
@ -29,16 +29,15 @@ public abstract class TransportHandlerSupport {
protected final Log logger = LogFactory.getLog(this.getClass());
private SockJsConfiguration sockJsConfig;
private SockJsServiceConfig sockJsServiceConfig;
public void setSockJsConfiguration(SockJsConfiguration sockJsConfig) {
this.sockJsConfig = sockJsConfig;
public void setSockJsServiceConfiguration(SockJsServiceConfig sockJsConfig) {
this.sockJsServiceConfig = sockJsConfig;
}
public SockJsConfiguration getSockJsConfig() {
return this.sockJsConfig;
public SockJsServiceConfig getSockJsServiceConfig() {
return this.sockJsServiceConfig;
}
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.transport;
package org.springframework.web.socket.sockjs.transport.handler;
import java.io.IOException;
@ -22,12 +22,13 @@ import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.util.Assert;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.server.HandshakeHandler;
import org.springframework.web.socket.sockjs.AbstractSockJsSession;
import org.springframework.web.socket.sockjs.SockJsSessionFactory;
import org.springframework.web.socket.sockjs.TransportErrorException;
import org.springframework.web.socket.sockjs.TransportHandler;
import org.springframework.web.socket.sockjs.TransportType;
import org.springframework.web.socket.sockjs.SockJsProcessingException;
import org.springframework.web.socket.sockjs.transport.TransportHandler;
import org.springframework.web.socket.sockjs.transport.TransportType;
import org.springframework.web.socket.sockjs.transport.session.AbstractSockJsSession;
import org.springframework.web.socket.sockjs.transport.session.WebSocketServerSockJsSession;
/**
* A WebSocket {@link TransportHandler}. Uses {@link SockJsWebSocketHandler} and
@ -58,20 +59,20 @@ public class WebSocketTransportHandler extends TransportHandlerSupport
@Override
public AbstractSockJsSession createSession(String sessionId, WebSocketHandler webSocketHandler) {
return new WebSocketServerSockJsSession(sessionId, getSockJsConfig(), webSocketHandler);
return new WebSocketServerSockJsSession(sessionId, getSockJsServiceConfig(), webSocketHandler);
}
@Override
public void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler webSocketHandler, AbstractSockJsSession session) throws TransportErrorException {
WebSocketHandler wsHandler, WebSocketSession wsSession) throws SockJsProcessingException {
try {
WebSocketServerSockJsSession wsSession = (WebSocketServerSockJsSession) session;
WebSocketHandler sockJsWrapper = new SockJsWebSocketHandler(getSockJsConfig(), webSocketHandler, wsSession);
this.handshakeHandler.doHandshake(request, response, sockJsWrapper);
WebSocketServerSockJsSession sockJsSession = (WebSocketServerSockJsSession) wsSession;
WebSocketHandler sockJsHandler = new SockJsWebSocketHandler(getSockJsServiceConfig(), wsHandler, sockJsSession);
this.handshakeHandler.doHandshake(request, response, sockJsHandler);
}
catch (Throwable t) {
throw new TransportErrorException("Failed to start handshake request", t, session.getId());
throw new SockJsProcessingException("Failed to start handshake request", t, wsSession.getId());
}
}

View File

@ -14,17 +14,18 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.transport;
package org.springframework.web.socket.sockjs.transport.handler;
import java.nio.charset.Charset;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.SockJsFrame.DefaultFrameFormat;
import org.springframework.web.socket.sockjs.SockJsFrame.FrameFormat;
import org.springframework.web.socket.sockjs.TransportHandler;
import org.springframework.web.socket.sockjs.TransportType;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame.DefaultFrameFormat;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame.FrameFormat;
import org.springframework.web.socket.sockjs.transport.TransportHandler;
import org.springframework.web.socket.sockjs.transport.TransportType;
import org.springframework.web.socket.sockjs.transport.session.PollingSockJsSession;
/**
* A {@link TransportHandler} based on XHR (long) polling.
@ -51,7 +52,7 @@ public class XhrPollingTransportHandler extends AbstractHttpSendingTransportHand
@Override
public PollingSockJsSession createSession(String sessionId, WebSocketHandler handler) {
return new PollingSockJsSession(sessionId, getSockJsConfig(), handler);
return new PollingSockJsSession(sessionId, getSockJsServiceConfig(), handler);
}
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.transport;
package org.springframework.web.socket.sockjs.transport.handler;
import java.io.IOException;
import java.nio.charset.Charset;
@ -22,11 +22,12 @@ import java.nio.charset.Charset;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.SockJsConfiguration;
import org.springframework.web.socket.sockjs.SockJsFrame.DefaultFrameFormat;
import org.springframework.web.socket.sockjs.SockJsFrame.FrameFormat;
import org.springframework.web.socket.sockjs.TransportHandler;
import org.springframework.web.socket.sockjs.TransportType;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame.DefaultFrameFormat;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame.FrameFormat;
import org.springframework.web.socket.sockjs.transport.TransportHandler;
import org.springframework.web.socket.sockjs.transport.TransportType;
import org.springframework.web.socket.sockjs.transport.session.SockJsServiceConfig;
import org.springframework.web.socket.sockjs.transport.session.StreamingSockJsSession;
/**
* A {@link TransportHandler} that sends messages over an HTTP streaming request.
@ -49,7 +50,7 @@ public class XhrStreamingTransportHandler extends AbstractHttpSendingTransportHa
@Override
public StreamingSockJsSession createSession(String sessionId, WebSocketHandler handler) {
return new XhrStreamingSockJsSession(sessionId, getSockJsConfig(), handler);
return new XhrStreamingSockJsSession(sessionId, getSockJsServiceConfig(), handler);
}
@Override
@ -60,7 +61,7 @@ public class XhrStreamingTransportHandler extends AbstractHttpSendingTransportHa
private final class XhrStreamingSockJsSession extends StreamingSockJsSession {
private XhrStreamingSockJsSession(String sessionId, SockJsConfiguration config, WebSocketHandler handler) {
private XhrStreamingSockJsSession(String sessionId, SockJsServiceConfig config, WebSocketHandler handler) {
super(sessionId, config, handler);
}

View File

@ -14,14 +14,14 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.transport;
package org.springframework.web.socket.sockjs.transport.handler;
import java.io.IOException;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.web.socket.sockjs.TransportHandler;
import org.springframework.web.socket.sockjs.TransportType;
import org.springframework.web.socket.sockjs.transport.TransportHandler;
import org.springframework.web.socket.sockjs.transport.TransportType;
/**
* A {@link TransportHandler} that receives messages over HTTP.
@ -38,7 +38,7 @@ public class XhrTransportHandler extends AbstractHttpReceivingTransportHandler {
@Override
protected String[] readMessages(ServerHttpRequest request) throws IOException {
return getSockJsConfig().getMessageCodecRequired().decodeInputStream(request.getBody());
return getSockJsServiceConfig().getMessageCodec().decodeInputStream(request.getBody());
}
@Override

View File

@ -0,0 +1,23 @@
/*
* 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.
*/
/**
* {@link org.springframework.web.socket.sockjs.transport.TransportHandler}
* implementation classes as well as a concrete
* {@link org.springframework.web.socket.sockjs.SockJsService}.
*/
package org.springframework.web.socket.sockjs.transport.handler;

View File

@ -16,11 +16,11 @@
/**
* Server-side support for SockJS transports including
* {@link org.springframework.web.socket.sockjs.TransportHandler} implementations
* {@link org.springframework.web.socket.sockjs.transport.TransportHandler} implementations
* for processing incoming requests, their
* {@link org.springframework.web.socket.sockjs.AbstractSockJsSession session} counterparts for
* {@link org.springframework.web.socket.sockjs.transport.session.AbstractSockJsSession session} counterparts for
* sending messages over the various transports, and
* {@link org.springframework.web.socket.sockjs.transport.DefaultSockJsService}.
* {@link org.springframework.web.socket.sockjs.transport.handler.DefaultSockJsService}.
*/
package org.springframework.web.socket.sockjs.transport;

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.transport;
package org.springframework.web.socket.sockjs.transport.session;
import java.io.IOException;
import java.util.concurrent.ArrayBlockingQueue;
@ -26,11 +26,9 @@ import org.springframework.http.server.ServerHttpResponse;
import org.springframework.util.Assert;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.AbstractSockJsSession;
import org.springframework.web.socket.sockjs.SockJsConfiguration;
import org.springframework.web.socket.sockjs.SockJsFrame;
import org.springframework.web.socket.sockjs.SockJsFrame.FrameFormat;
import org.springframework.web.socket.sockjs.TransportErrorException;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame.FrameFormat;
import org.springframework.web.socket.sockjs.SockJsProcessingException;
import org.springframework.web.socket.support.ExceptionWebSocketHandlerDecorator;
/**
@ -52,7 +50,7 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
private String protocol;
public AbstractHttpSockJsSession(String sessionId, SockJsConfiguration config, WebSocketHandler handler) {
public AbstractHttpSockJsSession(String sessionId, SockJsServiceConfig config, WebSocketHandler handler) {
super(sessionId, config, handler);
}
@ -76,7 +74,7 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
}
public synchronized void setInitialRequest(ServerHttpRequest request, ServerHttpResponse response,
FrameFormat frameFormat) throws TransportErrorException {
FrameFormat frameFormat) throws SockJsProcessingException {
try {
udpateRequest(request, response, frameFormat);
@ -85,7 +83,7 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
}
catch (Throwable t) {
tryCloseWithSockJsTransportError(t, null);
throw new TransportErrorException("Failed open SockJS session", t, getId());
throw new SockJsProcessingException("Failed open SockJS session", t, getId());
}
try {
delegateConnectionEstablished();
@ -99,7 +97,7 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
}
public synchronized void setLongPollingRequest(ServerHttpRequest request, ServerHttpResponse response,
FrameFormat frameFormat) throws TransportErrorException {
FrameFormat frameFormat) throws SockJsProcessingException {
try {
udpateRequest(request, response, frameFormat);
@ -110,7 +108,7 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
writeFrame(SockJsFrame.closeFrameGoAway());
}
catch (IOException ex) {
throw new TransportErrorException("Failed to send SockJS close frame", ex, getId());
throw new SockJsProcessingException("Failed to send SockJS close frame", ex, getId());
}
return;
}
@ -123,7 +121,7 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
}
catch (Throwable t) {
tryCloseWithSockJsTransportError(t, null);
throw new TransportErrorException("Failed to start long running request and flush messages", t, getId());
throw new SockJsProcessingException("Failed to start long running request and flush messages", t, getId());
}
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs;
package org.springframework.web.socket.sockjs.transport.session;
import java.io.EOFException;
import java.io.IOException;
@ -33,6 +33,8 @@ import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.adapter.ConfigurableWebSocketSession;
import org.springframework.web.socket.sockjs.SockJsProcessingException;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame;
/**
* An abstract base class SockJS sessions implementing {@link WebSocketSession}.
@ -55,7 +57,7 @@ public abstract class AbstractSockJsSession implements ConfigurableWebSocketSess
private Principal principal;
private final SockJsConfiguration sockJsConfig;
private final SockJsServiceConfig sockJsServiceConfig;
private final WebSocketHandler handler;
@ -70,16 +72,16 @@ public abstract class AbstractSockJsSession implements ConfigurableWebSocketSess
/**
* @param sessionId the session ID
* @param config the sockJS configuration
* @param webSocketHandler the recipient of SockJS messages
* @param config SockJS service configuration options
* @param wsHandler the recipient of SockJS messages
*/
public AbstractSockJsSession(String sessionId, SockJsConfiguration config, WebSocketHandler webSocketHandler) {
public AbstractSockJsSession(String sessionId, SockJsServiceConfig config, WebSocketHandler wsHandler) {
Assert.notNull(sessionId, "sessionId is required");
Assert.notNull(config, "sockJsConfig is required");
Assert.notNull(webSocketHandler, "webSocketHandler is required");
Assert.notNull(wsHandler, "webSocketHandler is required");
this.id = sessionId;
this.sockJsConfig = config;
this.handler = webSocketHandler;
this.sockJsServiceConfig = config;
this.handler = wsHandler;
}
@Override
@ -132,8 +134,8 @@ public abstract class AbstractSockJsSession implements ConfigurableWebSocketSess
this.principal = principal;
}
public SockJsConfiguration getSockJsConfig() {
return this.sockJsConfig;
public SockJsServiceConfig getSockJsServiceConfig() {
return this.sockJsServiceConfig;
}
public boolean isNew() {
@ -318,7 +320,7 @@ public abstract class AbstractSockJsSession implements ConfigurableWebSocketSess
logger.warn("Terminating connection due to failure to send message: " + ex.getMessage());
disconnect(CloseStatus.SERVER_ERROR);
close(CloseStatus.SERVER_ERROR);
throw new TransportErrorException("Failed to write " + frame, ex, this.getId());
throw new SockJsProcessingException("Failed to write " + frame, ex, this.getId());
}
}
@ -332,13 +334,13 @@ public abstract class AbstractSockJsSession implements ConfigurableWebSocketSess
}
protected void scheduleHeartbeat() {
Assert.state(this.sockJsConfig.getTaskScheduler() != null, "heartbeatScheduler not configured");
Assert.state(this.sockJsServiceConfig.getTaskScheduler() != null, "heartbeatScheduler not configured");
cancelHeartbeat();
if (!isActive()) {
return;
}
Date time = new Date(System.currentTimeMillis() + this.sockJsConfig.getHeartbeatTime());
this.heartbeatTask = this.sockJsConfig.getTaskScheduler().schedule(new Runnable() {
Date time = new Date(System.currentTimeMillis() + this.sockJsServiceConfig.getHeartbeatTime());
this.heartbeatTask = this.sockJsServiceConfig.getTaskScheduler().schedule(new Runnable() {
public void run() {
try {
sendHeartbeat();
@ -349,7 +351,7 @@ public abstract class AbstractSockJsSession implements ConfigurableWebSocketSess
}
}, time);
if (logger.isTraceEnabled()) {
logger.trace("Scheduled heartbeat after " + this.sockJsConfig.getHeartbeatTime()/1000 + " seconds");
logger.trace("Scheduled heartbeat after " + this.sockJsServiceConfig.getHeartbeatTime()/1000 + " seconds");
}
}

View File

@ -14,14 +14,13 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.transport;
package org.springframework.web.socket.sockjs.transport.session;
import java.io.IOException;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.SockJsConfiguration;
import org.springframework.web.socket.sockjs.SockJsFrame;
import org.springframework.web.socket.sockjs.SockJsMessageCodec;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame;
import org.springframework.web.socket.sockjs.support.frame.SockJsMessageCodec;
/**
* A SockJS session for use with polling HTTP transports.
@ -30,7 +29,7 @@ import org.springframework.web.socket.sockjs.SockJsMessageCodec;
*/
public class PollingSockJsSession extends AbstractHttpSockJsSession {
public PollingSockJsSession(String sessionId, SockJsConfiguration config, WebSocketHandler handler) {
public PollingSockJsSession(String sessionId, SockJsServiceConfig config, WebSocketHandler handler) {
super(sessionId, config, handler);
}
@ -42,7 +41,7 @@ public class PollingSockJsSession extends AbstractHttpSockJsSession {
String[] messages = getMessageCache().toArray(new String[getMessageCache().size()]);
getMessageCache().clear();
SockJsMessageCodec messageCodec = getSockJsConfig().getMessageCodecRequired();
SockJsMessageCodec messageCodec = getSockJsServiceConfig().getMessageCodec();
SockJsFrame frame = SockJsFrame.messageFrame(messageCodec, messages);
writeFrame(frame);
}

View File

@ -0,0 +1,40 @@
/*
* 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.web.socket.sockjs.transport.session;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.web.socket.sockjs.SockJsService;
import org.springframework.web.socket.sockjs.support.frame.SockJsMessageCodec;
/**
* Provides transport handling code with access to the {@link SockJsService} configuration
* options they need to have access to. Mainly for internal use.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public interface SockJsServiceConfig {
int getStreamBytesLimit();
long getHeartbeatTime();
TaskScheduler getTaskScheduler();
SockJsMessageCodec getMessageCodec();
}

View File

@ -14,18 +14,17 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.transport;
package org.springframework.web.socket.sockjs.transport.session;
import java.io.IOException;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.SockJsConfiguration;
import org.springframework.web.socket.sockjs.SockJsFrame;
import org.springframework.web.socket.sockjs.SockJsFrame.FrameFormat;
import org.springframework.web.socket.sockjs.SockJsMessageCodec;
import org.springframework.web.socket.sockjs.TransportErrorException;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame;
import org.springframework.web.socket.sockjs.support.frame.SockJsMessageCodec;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame.FrameFormat;
import org.springframework.web.socket.sockjs.SockJsProcessingException;
/**
* A SockJS session for use with streaming HTTP transports.
@ -37,14 +36,14 @@ public class StreamingSockJsSession extends AbstractHttpSockJsSession {
private int byteCount;
public StreamingSockJsSession(String sessionId, SockJsConfiguration config, WebSocketHandler handler) {
public StreamingSockJsSession(String sessionId, SockJsServiceConfig config, WebSocketHandler handler) {
super(sessionId, config, handler);
}
@Override
public synchronized void setInitialRequest(ServerHttpRequest request, ServerHttpResponse response,
FrameFormat frameFormat) throws TransportErrorException {
FrameFormat frameFormat) throws SockJsProcessingException {
super.setInitialRequest(request, response, frameFormat);
@ -61,7 +60,7 @@ public class StreamingSockJsSession extends AbstractHttpSockJsSession {
do {
String message = getMessageCache().poll();
SockJsMessageCodec messageCodec = getSockJsConfig().getMessageCodecRequired();
SockJsMessageCodec messageCodec = getSockJsServiceConfig().getMessageCodec();
SockJsFrame frame = SockJsFrame.messageFrame(messageCodec, message);
writeFrame(frame);
@ -70,7 +69,7 @@ public class StreamingSockJsSession extends AbstractHttpSockJsSession {
logger.trace(this.byteCount + " bytes written so far, "
+ getMessageCache().size() + " more messages not flushed");
}
if (this.byteCount >= getSockJsConfig().getStreamBytesLimit()) {
if (this.byteCount >= getSockJsServiceConfig().getStreamBytesLimit()) {
if (logger.isTraceEnabled()) {
logger.trace("Streamed bytes limit reached. Recycling current request");
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.transport;
package org.springframework.web.socket.sockjs.transport.session;
import java.io.IOException;
@ -23,10 +23,8 @@ import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.sockjs.AbstractSockJsSession;
import org.springframework.web.socket.sockjs.SockJsConfiguration;
import org.springframework.web.socket.sockjs.SockJsFrame;
import org.springframework.web.socket.sockjs.SockJsMessageCodec;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame;
import org.springframework.web.socket.sockjs.support.frame.SockJsMessageCodec;
/**
* A SockJS session for use with the WebSocket transport.
@ -39,7 +37,7 @@ public class WebSocketServerSockJsSession extends AbstractSockJsSession {
private WebSocketSession webSocketSession;
public WebSocketServerSockJsSession(String sessionId, SockJsConfiguration config, WebSocketHandler handler) {
public WebSocketServerSockJsSession(String sessionId, SockJsServiceConfig config, WebSocketHandler handler) {
super(sessionId, config, handler);
}
@ -85,7 +83,7 @@ public class WebSocketServerSockJsSession extends AbstractSockJsSession {
}
String[] messages;
try {
messages = getSockJsConfig().getMessageCodecRequired().decode(payload);
messages = getSockJsServiceConfig().getMessageCodec().decode(payload);
}
catch (IOException ex) {
logger.error("Broken data received. Terminating WebSocket connection abruptly", ex);
@ -98,7 +96,7 @@ public class WebSocketServerSockJsSession extends AbstractSockJsSession {
@Override
public void sendMessageInternal(String message) throws IOException {
cancelHeartbeat();
SockJsMessageCodec messageCodec = getSockJsConfig().getMessageCodecRequired();
SockJsMessageCodec messageCodec = getSockJsServiceConfig().getMessageCodec();
SockJsFrame frame = SockJsFrame.messageFrame(messageCodec, message);
writeFrame(frame);
scheduleHeartbeat();

View File

@ -0,0 +1,22 @@
/*
* 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.
*/
/**
* SockJS specific implementations of
* {@link org.springframework.web.socket.WebSocketSession}.
*/
package org.springframework.web.socket.sockjs.transport.session;

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs;
package org.springframework.web.socket.sockjs.support;
import java.io.IOException;
@ -27,6 +27,8 @@ import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.web.socket.AbstractHttpRequestTests;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.SockJsProcessingException;
import org.springframework.web.socket.sockjs.transport.TransportType;
import static org.junit.Assert.*;
@ -77,7 +79,7 @@ public class AbstractSockJsServiceTests extends AbstractHttpRequestTests {
handleRequest("GET", "/a/server/session/xhr", HttpStatus.OK);
assertEquals("session", this.service.sessionId);
assertEquals(TransportType.XHR, this.service.transportType);
assertEquals(TransportType.XHR.value(), this.service.transport);
assertSame(this.handler, this.service.handler);
this.service.setValidSockJsPrefixes("/b");
@ -96,7 +98,7 @@ public class AbstractSockJsServiceTests extends AbstractHttpRequestTests {
handleRequest("GET", "/a/server/session/xhr", HttpStatus.OK);
assertEquals("session", this.service.sessionId);
assertEquals(TransportType.XHR, this.service.transportType);
assertEquals(TransportType.XHR.value(), this.service.transport);
assertSame(this.handler, this.service.handler);
}
@ -107,7 +109,7 @@ public class AbstractSockJsServiceTests extends AbstractHttpRequestTests {
handleRequest("GET", "/a/server/session/xhr", HttpStatus.OK);
assertEquals("session", this.service.sessionId);
assertEquals(TransportType.XHR, this.service.transportType);
assertEquals(TransportType.XHR.value(), this.service.transport);
assertSame(this.handler, this.service.handler);
}
@ -216,7 +218,7 @@ public class AbstractSockJsServiceTests extends AbstractHttpRequestTests {
private String sessionId;
private TransportType transportType;
private String transport;
private WebSocketHandler handler;
@ -224,11 +226,6 @@ public class AbstractSockJsServiceTests extends AbstractHttpRequestTests {
super(scheduler);
}
@Override
public SockJsMessageCodec getMessageCodecRequired() {
throw new UnsupportedOperationException();
}
@Override
protected void handleRawWebSocketRequest(ServerHttpRequest request,
ServerHttpResponse response, WebSocketHandler handler) throws IOException {
@ -237,15 +234,19 @@ public class AbstractSockJsServiceTests extends AbstractHttpRequestTests {
}
@Override
protected void handleTransportRequest(ServerHttpRequest request,
ServerHttpResponse response, String sessionId,
TransportType transportType, WebSocketHandler handler)
throws IOException, TransportErrorException {
protected void handleTransportRequest(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler handler, String sessionId, String transport)
throws IOException, SockJsProcessingException {
this.sessionId = sessionId;
this.transportType = transportType;
this.transport = transport;
this.handler = handler;
}
@Override
protected boolean isValidTransportType(String transportType) {
return TransportType.fromValue(transportType) != null;
}
}
}

View File

@ -14,9 +14,10 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs;
package org.springframework.web.socket.sockjs.transport;
import org.junit.Test;
import org.springframework.web.socket.sockjs.transport.TransportType;
import static org.junit.Assert.*;

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.transport;
package org.springframework.web.socket.sockjs.transport.handler;
import java.util.Collections;
import java.util.HashSet;
@ -29,14 +29,15 @@ import org.springframework.http.server.ServerHttpResponse;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.web.socket.AbstractHttpRequestTests;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.AbstractSockJsSession;
import org.springframework.web.socket.sockjs.SockJsConfiguration;
import org.springframework.web.socket.sockjs.SockJsSessionFactory;
import org.springframework.web.socket.sockjs.StubSockJsConfig;
import org.springframework.web.socket.sockjs.TestSockJsSession;
import org.springframework.web.socket.sockjs.TransportErrorException;
import org.springframework.web.socket.sockjs.TransportHandler;
import org.springframework.web.socket.sockjs.TransportType;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.sockjs.SockJsProcessingException;
import org.springframework.web.socket.sockjs.transport.TransportHandler;
import org.springframework.web.socket.sockjs.transport.TransportType;
import org.springframework.web.socket.sockjs.transport.handler.DefaultSockJsService;
import org.springframework.web.socket.sockjs.transport.handler.SockJsSessionFactory;
import org.springframework.web.socket.sockjs.transport.session.AbstractSockJsSession;
import org.springframework.web.socket.sockjs.transport.session.StubSockJsServiceConfig;
import org.springframework.web.socket.sockjs.transport.session.TestSockJsSession;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
@ -83,7 +84,7 @@ public class DefaultSockJsServiceTests extends AbstractHttpRequestTests {
WebSocketHandler webSocketHandler = mock(WebSocketHandler.class);
DefaultSockJsService service = new DefaultSockJsService(taskScheduler, transportHandlers);
service.handleTransportRequest(this.request, this.response, "123", TransportType.XHR, webSocketHandler);
service.handleTransportRequest(this.request, this.response, webSocketHandler, "123", TransportType.XHR.value());
assertEquals(200, this.servletResponse.getStatus());
assertNotNull(xhrHandler.session);
@ -107,7 +108,7 @@ public class DefaultSockJsServiceTests extends AbstractHttpRequestTests {
Set<TransportHandler> transportHandlers = Collections.<TransportHandler>singleton(xhrHandler);
DefaultSockJsService service = new DefaultSockJsService(taskScheduler, transportHandlers);
service.handleTransportRequest(this.request, this.response, "123", TransportType.XHR, null);
service.handleTransportRequest(this.request, this.response, null, "123", TransportType.XHR.value());
assertEquals(204, this.servletResponse.getStatus());
assertEquals("*", this.response.getHeaders().getFirst("Access-Control-Allow-Origin"));
@ -122,7 +123,7 @@ public class DefaultSockJsServiceTests extends AbstractHttpRequestTests {
Set<TransportHandler> transportHandlers = new HashSet<>();
DefaultSockJsService service = new DefaultSockJsService(mock(TaskScheduler.class), transportHandlers);
service.handleTransportRequest(this.request, this.response, "123", TransportType.XHR, null);
service.handleTransportRequest(this.request, this.response, null, "123", TransportType.XHR.value());
assertEquals(404, this.servletResponse.getStatus());
}
@ -135,20 +136,20 @@ public class DefaultSockJsServiceTests extends AbstractHttpRequestTests {
Set<TransportHandler> transportHandlers = new HashSet<>();
transportHandlers.add(new StubXhrTransportHandler());
transportHandlers.add(new StubXhrSendTransportHandler());
WebSocketHandler webSocketHandler = mock(WebSocketHandler.class);
WebSocketHandler wsHandler = mock(WebSocketHandler.class);
DefaultSockJsService service = new DefaultSockJsService(mock(TaskScheduler.class), transportHandlers);
service.handleTransportRequest(this.request, this.response, "123", TransportType.XHR_SEND, webSocketHandler);
service.handleTransportRequest(this.request, this.response, wsHandler, "123", TransportType.XHR_SEND.value());
assertEquals(404, this.servletResponse.getStatus()); // dropped (no session)
resetResponse();
service.handleTransportRequest(this.request, this.response, "123", TransportType.XHR, webSocketHandler);
service.handleTransportRequest(this.request, this.response, wsHandler, "123", TransportType.XHR.value());
assertEquals(200, this.servletResponse.getStatus());
resetResponse();
service.handleTransportRequest(this.request, this.response, "123", TransportType.XHR_SEND, webSocketHandler);
service.handleTransportRequest(this.request, this.response, wsHandler, "123", TransportType.XHR_SEND.value());
assertEquals(200, this.servletResponse.getStatus());
}
@ -158,20 +159,16 @@ public class DefaultSockJsServiceTests extends AbstractHttpRequestTests {
WebSocketHandler webSocketHandler;
AbstractSockJsSession session;
WebSocketSession session;
@Override
public TransportType getTransportType() {
return TransportType.XHR;
}
@Override
public void setSockJsConfiguration(SockJsConfiguration sockJsConfig) {
}
@Override
public void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler handler, AbstractSockJsSession session) throws TransportErrorException {
WebSocketHandler handler, WebSocketSession session) throws SockJsProcessingException {
this.webSocketHandler = handler;
this.session = session;
@ -179,7 +176,7 @@ public class DefaultSockJsServiceTests extends AbstractHttpRequestTests {
@Override
public AbstractSockJsSession createSession(String sessionId, WebSocketHandler webSocketHandler) {
return new TestSockJsSession(sessionId, new StubSockJsConfig(), webSocketHandler);
return new TestSockJsSession(sessionId, new StubSockJsServiceConfig(), webSocketHandler);
}
}
@ -191,13 +188,9 @@ public class DefaultSockJsServiceTests extends AbstractHttpRequestTests {
return TransportType.XHR_SEND;
}
@Override
public void setSockJsConfiguration(SockJsConfiguration sockJsConfig) {
}
@Override
public void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler handler, AbstractSockJsSession session) throws TransportErrorException {
WebSocketHandler handler, WebSocketSession session) throws SockJsProcessingException {
if (session == null) {
response.setStatusCode(HttpStatus.NOT_FOUND);

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.transport;
package org.springframework.web.socket.sockjs.transport.handler;
import org.junit.Before;
import org.junit.Test;
@ -23,10 +23,13 @@ import org.springframework.web.socket.AbstractHttpRequestTests;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.AbstractSockJsSession;
import org.springframework.web.socket.sockjs.StubSockJsConfig;
import org.springframework.web.socket.sockjs.TestSockJsSession;
import org.springframework.web.socket.sockjs.TransportErrorException;
import org.springframework.web.socket.sockjs.SockJsProcessingException;
import org.springframework.web.socket.sockjs.transport.handler.AbstractHttpReceivingTransportHandler;
import org.springframework.web.socket.sockjs.transport.handler.JsonpTransportHandler;
import org.springframework.web.socket.sockjs.transport.handler.XhrTransportHandler;
import org.springframework.web.socket.sockjs.transport.session.AbstractSockJsSession;
import org.springframework.web.socket.sockjs.transport.session.StubSockJsServiceConfig;
import org.springframework.web.socket.sockjs.transport.session.TestSockJsSession;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
@ -105,7 +108,7 @@ public class HttpReceivingTransportHandlerTests extends AbstractHttpRequestTest
@Test
public void delegateMessageException() throws Exception {
StubSockJsConfig sockJsConfig = new StubSockJsConfig();
StubSockJsServiceConfig sockJsConfig = new StubSockJsServiceConfig();
this.servletRequest.setContent("[\"x\"]".getBytes("UTF-8"));
@ -117,11 +120,11 @@ public class HttpReceivingTransportHandlerTests extends AbstractHttpRequestTest
try {
XhrTransportHandler transportHandler = new XhrTransportHandler();
transportHandler.setSockJsConfiguration(sockJsConfig);
transportHandler.setSockJsServiceConfiguration(sockJsConfig);
transportHandler.handleRequest(this.request, this.response, webSocketHandler, session);
fail("Expected exception");
}
catch (TransportErrorException ex) {
catch (SockJsProcessingException ex) {
assertEquals(CloseStatus.SERVER_ERROR, session.getStatus());
}
}
@ -131,9 +134,9 @@ public class HttpReceivingTransportHandlerTests extends AbstractHttpRequestTest
throws Exception {
WebSocketHandler webSocketHandler = mock(WebSocketHandler.class);
AbstractSockJsSession session = new TestSockJsSession("1", new StubSockJsConfig(), webSocketHandler);
AbstractSockJsSession session = new TestSockJsSession("1", new StubSockJsServiceConfig(), webSocketHandler);
transportHandler.setSockJsConfiguration(new StubSockJsConfig());
transportHandler.setSockJsServiceConfiguration(new StubSockJsServiceConfig());
transportHandler.handleRequest(this.request, this.response, webSocketHandler, session);
assertEquals("text/plain;charset=UTF-8", this.response.getHeaders().getContentType().toString());
@ -145,7 +148,7 @@ public class HttpReceivingTransportHandlerTests extends AbstractHttpRequestTest
resetResponse();
WebSocketHandler webSocketHandler = mock(WebSocketHandler.class);
AbstractSockJsSession session = new TestSockJsSession("1", new StubSockJsConfig(), webSocketHandler);
AbstractSockJsSession session = new TestSockJsSession("1", new StubSockJsServiceConfig(), webSocketHandler);
new XhrTransportHandler().handleRequest(this.request, this.response, webSocketHandler, session);

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.transport;
package org.springframework.web.socket.sockjs.transport.handler;
import java.sql.Date;
@ -23,10 +23,18 @@ import org.junit.Test;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.web.socket.AbstractHttpRequestTests;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.AbstractSockJsSession;
import org.springframework.web.socket.sockjs.SockJsFrame;
import org.springframework.web.socket.sockjs.SockJsFrame.FrameFormat;
import org.springframework.web.socket.sockjs.StubSockJsConfig;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame.FrameFormat;
import org.springframework.web.socket.sockjs.transport.handler.AbstractHttpSendingTransportHandler;
import org.springframework.web.socket.sockjs.transport.handler.EventSourceTransportHandler;
import org.springframework.web.socket.sockjs.transport.handler.HtmlFileTransportHandler;
import org.springframework.web.socket.sockjs.transport.handler.JsonpPollingTransportHandler;
import org.springframework.web.socket.sockjs.transport.handler.XhrPollingTransportHandler;
import org.springframework.web.socket.sockjs.transport.handler.XhrStreamingTransportHandler;
import org.springframework.web.socket.sockjs.transport.session.AbstractSockJsSession;
import org.springframework.web.socket.sockjs.transport.session.PollingSockJsSession;
import org.springframework.web.socket.sockjs.transport.session.StreamingSockJsSession;
import org.springframework.web.socket.sockjs.transport.session.StubSockJsServiceConfig;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
@ -41,7 +49,7 @@ public class HttpSendingTransportHandlerTests extends AbstractHttpRequestTests
private WebSocketHandler webSocketHandler;
private StubSockJsConfig sockJsConfig;
private StubSockJsServiceConfig sockJsConfig;
private TaskScheduler taskScheduler;
@ -54,7 +62,7 @@ public class HttpSendingTransportHandlerTests extends AbstractHttpRequestTests
this.webSocketHandler = mock(WebSocketHandler.class);
this.taskScheduler = mock(TaskScheduler.class);
this.sockJsConfig = new StubSockJsConfig();
this.sockJsConfig = new StubSockJsServiceConfig();
this.sockJsConfig.setTaskScheduler(this.taskScheduler);
}
@ -62,7 +70,7 @@ public class HttpSendingTransportHandlerTests extends AbstractHttpRequestTests
public void handleRequestXhr() throws Exception {
XhrPollingTransportHandler transportHandler = new XhrPollingTransportHandler();
transportHandler.setSockJsConfiguration(this.sockJsConfig);
transportHandler.setSockJsServiceConfiguration(this.sockJsConfig);
AbstractSockJsSession session = transportHandler.createSession("1", this.webSocketHandler);
transportHandler.handleRequest(this.request, this.response, this.webSocketHandler, session);
@ -89,7 +97,7 @@ public class HttpSendingTransportHandlerTests extends AbstractHttpRequestTests
public void jsonpTransport() throws Exception {
JsonpPollingTransportHandler transportHandler = new JsonpPollingTransportHandler();
transportHandler.setSockJsConfiguration(this.sockJsConfig);
transportHandler.setSockJsServiceConfiguration(this.sockJsConfig);
PollingSockJsSession session = transportHandler.createSession("1", this.webSocketHandler);
transportHandler.handleRequest(this.request, this.response, this.webSocketHandler, session);
@ -110,7 +118,7 @@ public class HttpSendingTransportHandlerTests extends AbstractHttpRequestTests
public void handleRequestXhrStreaming() throws Exception {
XhrStreamingTransportHandler transportHandler = new XhrStreamingTransportHandler();
transportHandler.setSockJsConfiguration(this.sockJsConfig);
transportHandler.setSockJsServiceConfiguration(this.sockJsConfig);
AbstractSockJsSession session = transportHandler.createSession("1", this.webSocketHandler);
transportHandler.handleRequest(this.request, this.response, this.webSocketHandler, session);
@ -124,7 +132,7 @@ public class HttpSendingTransportHandlerTests extends AbstractHttpRequestTests
public void htmlFileTransport() throws Exception {
HtmlFileTransportHandler transportHandler = new HtmlFileTransportHandler();
transportHandler.setSockJsConfiguration(this.sockJsConfig);
transportHandler.setSockJsServiceConfiguration(this.sockJsConfig);
StreamingSockJsSession session = transportHandler.createSession("1", this.webSocketHandler);
transportHandler.handleRequest(this.request, this.response, this.webSocketHandler, session);
@ -145,7 +153,7 @@ public class HttpSendingTransportHandlerTests extends AbstractHttpRequestTests
public void eventSourceTransport() throws Exception {
EventSourceTransportHandler transportHandler = new EventSourceTransportHandler();
transportHandler.setSockJsConfiguration(this.sockJsConfig);
transportHandler.setSockJsServiceConfiguration(this.sockJsConfig);
StreamingSockJsSession session = transportHandler.createSession("1", this.webSocketHandler);
transportHandler.handleRequest(this.request, this.response, this.webSocketHandler, session);

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.transport;
package org.springframework.web.socket.sockjs.transport.session;
import java.io.IOException;
@ -28,12 +28,11 @@ import org.springframework.mock.web.test.MockHttpServletRequest;
import org.springframework.mock.web.test.MockHttpServletResponse;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.BaseAbstractSockJsSessionTests;
import org.springframework.web.socket.sockjs.SockJsConfiguration;
import org.springframework.web.socket.sockjs.SockJsFrame;
import org.springframework.web.socket.sockjs.SockJsFrame.DefaultFrameFormat;
import org.springframework.web.socket.sockjs.SockJsFrame.FrameFormat;
import org.springframework.web.socket.sockjs.transport.AbstractHttpSockJsSessionTests.TestAbstractHttpSockJsSession;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame.DefaultFrameFormat;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame.FrameFormat;
import org.springframework.web.socket.sockjs.transport.session.AbstractHttpSockJsSession;
import org.springframework.web.socket.sockjs.transport.session.AbstractHttpSockJsSessionTests.TestAbstractHttpSockJsSession;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
@ -128,7 +127,7 @@ public class AbstractHttpSockJsSessionTests extends BaseAbstractSockJsSessionTes
private boolean heartbeatScheduled;
public TestAbstractHttpSockJsSession(SockJsConfiguration config, WebSocketHandler handler) {
public TestAbstractHttpSockJsSession(SockJsServiceConfig config, WebSocketHandler handler) {
super("1", config, handler);
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs;
package org.springframework.web.socket.sockjs.transport.session;
import java.io.IOException;
import java.sql.Date;
@ -24,6 +24,9 @@ import java.util.concurrent.ScheduledFuture;
import org.junit.Test;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.sockjs.SockJsProcessingException;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame;
import org.springframework.web.socket.sockjs.transport.session.AbstractSockJsSession;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
@ -210,7 +213,7 @@ public class AbstractSockJsSessionTests extends BaseAbstractSockJsSessionTests<T
this.session.writeFrame(SockJsFrame.openFrame());
fail("expected exception");
}
catch (TransportErrorException ex) {
catch (SockJsProcessingException ex) {
assertEquals(CloseStatus.SERVER_ERROR, this.session.getStatus());
verify(this.webSocketHandler).afterConnectionClosed(this.session, CloseStatus.SERVER_ERROR);
}

View File

@ -14,11 +14,12 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs;
package org.springframework.web.socket.sockjs.transport.session;
import org.junit.Before;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.transport.session.AbstractSockJsSession;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
@ -32,7 +33,7 @@ public abstract class BaseAbstractSockJsSessionTests<S extends AbstractSockJsSes
protected WebSocketHandler webSocketHandler;
protected StubSockJsConfig sockJsConfig;
protected StubSockJsServiceConfig sockJsConfig;
protected TaskScheduler taskScheduler;
@ -44,7 +45,7 @@ public abstract class BaseAbstractSockJsSessionTests<S extends AbstractSockJsSes
this.webSocketHandler = mock(WebSocketHandler.class);
this.taskScheduler = mock(TaskScheduler.class);
this.sockJsConfig = new StubSockJsConfig();
this.sockJsConfig = new StubSockJsServiceConfig();
this.sockJsConfig.setTaskScheduler(this.taskScheduler);
this.session = initSockJsSession();

View File

@ -14,16 +14,17 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs;
package org.springframework.web.socket.sockjs.transport.session;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.web.socket.sockjs.support.Jackson2SockJsMessageCodec;
import org.springframework.web.socket.sockjs.support.frame.Jackson2SockJsMessageCodec;
import org.springframework.web.socket.sockjs.support.frame.SockJsMessageCodec;
/**
* @author Rossen Stoyanchev
*/
public class StubSockJsConfig implements SockJsConfiguration {
public class StubSockJsServiceConfig implements SockJsServiceConfig {
private int streamBytesLimit = 128 * 1024;
@ -62,12 +63,8 @@ public class StubSockJsConfig implements SockJsConfiguration {
}
@Override
public SockJsMessageCodec getMessageCodecRequired() {
return this.messageCodec;
}
public SockJsMessageCodec getMessageCodec() {
return messageCodec;
return this.messageCodec;
}
public void setMessageCodec(SockJsMessageCodec messageCodec) {

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs;
package org.springframework.web.socket.sockjs.transport.session;
import java.io.IOException;
import java.util.ArrayList;
@ -22,6 +22,8 @@ import java.util.List;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame;
import org.springframework.web.socket.sockjs.transport.session.AbstractSockJsSession;
/**
* @author Rossen Stoyanchev
@ -43,7 +45,7 @@ public class TestSockJsSession extends AbstractSockJsSession {
private String subProtocol;
public TestSockJsSession(String sessionId, SockJsConfiguration config, WebSocketHandler handler) {
public TestSockJsSession(String sessionId, SockJsServiceConfig config, WebSocketHandler handler) {
super(sessionId, config, handler);
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.transport;
package org.springframework.web.socket.sockjs.transport.session;
import java.io.IOException;
import java.util.ArrayList;
@ -27,9 +27,8 @@ import org.junit.Test;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.BaseAbstractSockJsSessionTests;
import org.springframework.web.socket.sockjs.SockJsConfiguration;
import org.springframework.web.socket.sockjs.transport.WebSocketServerSockJsSessionTests.TestWebSocketServerSockJsSession;
import org.springframework.web.socket.sockjs.transport.session.WebSocketServerSockJsSession;
import org.springframework.web.socket.sockjs.transport.session.WebSocketServerSockJsSessionTests.TestWebSocketServerSockJsSession;
import org.springframework.web.socket.support.TestWebSocketSession;
import static org.junit.Assert.*;
@ -134,7 +133,7 @@ public class WebSocketServerSockJsSessionTests extends BaseAbstractSockJsSession
private final List<String> heartbeatSchedulingEvents = new ArrayList<>();
public TestWebSocketServerSockJsSession(SockJsConfiguration config, WebSocketHandler handler) {
public TestWebSocketServerSockJsSession(SockJsServiceConfig config, WebSocketHandler handler) {
super("1", config, handler);
}