Refactor SockJS code
- configure SockJS handler by type (as well as by instance) - add method to obtain SockJS handler instance via SockJsConfiguration - detect presense of jsr-356 and use it if available
This commit is contained in:
@@ -33,7 +33,7 @@ public abstract class SockJsSessionSupport implements SockJsSession {
|
||||
|
||||
private final String sessionId;
|
||||
|
||||
private final SockJsHandler delegate;
|
||||
private final SockJsHandler sockJsHandler;
|
||||
|
||||
private State state = State.NEW;
|
||||
|
||||
@@ -45,13 +45,13 @@ public abstract class SockJsSessionSupport implements SockJsSession {
|
||||
/**
|
||||
*
|
||||
* @param sessionId
|
||||
* @param delegate the recipient of SockJS messages
|
||||
* @param sockJsHandler the recipient of SockJS messages
|
||||
*/
|
||||
public SockJsSessionSupport(String sessionId, SockJsHandler delegate) {
|
||||
public SockJsSessionSupport(String sessionId, SockJsHandler sockJsHandler) {
|
||||
Assert.notNull(sessionId, "sessionId is required");
|
||||
Assert.notNull(delegate, "SockJsHandler is required");
|
||||
Assert.notNull(sockJsHandler, "SockJsHandler is required");
|
||||
this.sessionId = sessionId;
|
||||
this.delegate = delegate;
|
||||
this.sockJsHandler = sockJsHandler;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
@@ -59,7 +59,7 @@ public abstract class SockJsSessionSupport implements SockJsSession {
|
||||
}
|
||||
|
||||
public SockJsHandler getSockJsHandler() {
|
||||
return this.delegate;
|
||||
return this.sockJsHandler;
|
||||
}
|
||||
|
||||
public boolean isNew() {
|
||||
@@ -105,17 +105,22 @@ public abstract class SockJsSessionSupport implements SockJsSession {
|
||||
|
||||
public void connectionInitialized() throws Exception {
|
||||
this.state = State.OPEN;
|
||||
this.delegate.newSession(this);
|
||||
this.sockJsHandler.newSession(this);
|
||||
}
|
||||
|
||||
public void delegateMessages(String... messages) throws Exception {
|
||||
for (String message : messages) {
|
||||
this.delegate.handleMessage(this, message);
|
||||
this.sockJsHandler.handleMessage(this, message);
|
||||
}
|
||||
}
|
||||
|
||||
public void delegateException(Throwable ex) {
|
||||
this.sockJsHandler.handleException(this, ex);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
this.state = State.CLOSED;
|
||||
this.sockJsHandler.sessionClosed(this);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
||||
@@ -40,13 +40,17 @@ public abstract class AbstractServerSession extends SockJsSessionSupport {
|
||||
private ScheduledFuture<?> heartbeatTask;
|
||||
|
||||
|
||||
public AbstractServerSession(String sessionId, SockJsHandler delegate, SockJsConfiguration sockJsConfig) {
|
||||
super(sessionId, delegate);
|
||||
Assert.notNull(sockJsConfig, "sockJsConfig is required");
|
||||
public AbstractServerSession(String sessionId, SockJsConfiguration sockJsConfig) {
|
||||
super(sessionId, getSockJsHandler(sockJsConfig));
|
||||
this.sockJsConfig = sockJsConfig;
|
||||
}
|
||||
|
||||
public SockJsConfiguration getSockJsConfig() {
|
||||
private static SockJsHandler getSockJsHandler(SockJsConfiguration sockJsConfig) {
|
||||
Assert.notNull(sockJsConfig, "sockJsConfig is required");
|
||||
return sockJsConfig.getSockJsHandler();
|
||||
}
|
||||
|
||||
protected SockJsConfiguration getSockJsConfig() {
|
||||
return this.sockJsConfig;
|
||||
}
|
||||
|
||||
@@ -61,18 +65,15 @@ public abstract class AbstractServerSession extends SockJsSessionSupport {
|
||||
if (!isClosed()) {
|
||||
logger.debug("Closing session");
|
||||
|
||||
// set the status
|
||||
super.close();
|
||||
|
||||
if (isActive()) {
|
||||
// deliver messages "in flight" before sending close frame
|
||||
writeFrame(SockJsFrame.closeFrameGoAway());
|
||||
}
|
||||
|
||||
super.close();
|
||||
|
||||
cancelHeartbeat();
|
||||
closeInternal();
|
||||
|
||||
getSockJsHandler().sessionClosed(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,12 +91,12 @@ public abstract class AbstractServerSession extends SockJsSessionSupport {
|
||||
writeFrameInternal(frame);
|
||||
}
|
||||
catch (EOFException ex) {
|
||||
logger.warn("Failed to send message due to client disconnect. Terminating connection abruptly");
|
||||
logger.warn("Client went away. Terminating connection abruptly");
|
||||
deactivate();
|
||||
close();
|
||||
}
|
||||
catch (Throwable t) {
|
||||
logger.error("Failed to send message. Terminating connection abruptly", t);
|
||||
logger.warn("Failed to send message. Terminating connection abruptly: " + t.getMessage());
|
||||
deactivate();
|
||||
close();
|
||||
}
|
||||
|
||||
@@ -32,13 +32,11 @@ import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.springframework.sockjs.TransportType;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.DigestUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.websocket.server.HandshakeRequestHandler;
|
||||
|
||||
|
||||
/**
|
||||
@@ -70,8 +68,6 @@ public abstract class AbstractSockJsService implements SockJsConfiguration {
|
||||
|
||||
private boolean webSocketsEnabled = true;
|
||||
|
||||
private HandshakeRequestHandler handshakeRequestHandler;
|
||||
|
||||
|
||||
/**
|
||||
* Class constructor...
|
||||
@@ -199,16 +195,6 @@ public abstract class AbstractSockJsService implements SockJsConfiguration {
|
||||
return this.webSocketsEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* SockJS exposes an entry point at "/websocket" for raw WebSocket
|
||||
* communication without additional custom framing, e.g. no open frame, no
|
||||
* heartbeats, only raw WebSocket protocol. This property allows setting a
|
||||
* handler for requests for raw WebSocket communication.
|
||||
*/
|
||||
public void setWebsocketHandler(HandshakeRequestHandler handshakeRequestHandler) {
|
||||
this.handshakeRequestHandler = handshakeRequestHandler;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* TODO
|
||||
@@ -245,8 +231,7 @@ public abstract class AbstractSockJsService implements SockJsConfiguration {
|
||||
return;
|
||||
}
|
||||
else if (sockJsPath.equals("/websocket")) {
|
||||
Assert.notNull(this.handshakeRequestHandler, "No handler for raw Websockets configured");
|
||||
this.handshakeRequestHandler.doHandshake(request, response);
|
||||
handleRawWebSocket(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -270,6 +255,9 @@ public abstract class AbstractSockJsService implements SockJsConfiguration {
|
||||
|
||||
}
|
||||
|
||||
protected abstract void handleRawWebSocket(ServerHttpRequest request, ServerHttpResponse response)
|
||||
throws Exception;
|
||||
|
||||
protected boolean validateRequest(String serverId, String sessionId, String transport) {
|
||||
|
||||
if (!StringUtils.hasText(serverId) || !StringUtils.hasText(sessionId) || !StringUtils.hasText(transport)) {
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.sockjs.server;
|
||||
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.springframework.sockjs.SockJsHandler;
|
||||
|
||||
|
||||
/**
|
||||
@@ -66,4 +67,11 @@ public interface SockJsConfiguration {
|
||||
*/
|
||||
public TaskScheduler getHeartbeatScheduler();
|
||||
|
||||
/**
|
||||
* Provides access to the {@link SockJsHandler} that will handle the request. This
|
||||
* method should be called once per SockJS session. It may return the same or a
|
||||
* different instance every time it is called.
|
||||
*/
|
||||
SockJsHandler getSockJsHandler();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,96 +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.sockjs.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.websocket.WebSocketHandler;
|
||||
import org.springframework.websocket.WebSocketSession;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
|
||||
/**
|
||||
* An implementation of {@link WebSocketHandler} supporting the SockJS protocol.
|
||||
* Methods merely delegate to a {@link StandardWebSocketServerSession}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
*/
|
||||
public class SockJsWebSocketHandler implements WebSocketHandler {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(SockJsWebSocketHandler.class);
|
||||
|
||||
private final StandardWebSocketServerSession sockJsSession;
|
||||
|
||||
// TODO: the JSON library used must be configurable
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
|
||||
public SockJsWebSocketHandler(StandardWebSocketServerSession sockJsSession) {
|
||||
this.sockJsSession = sockJsSession;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void newSession(WebSocketSession webSocketSession) throws Exception {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("New session: " + webSocketSession);
|
||||
}
|
||||
this.sockJsSession.setWebSocketSession(webSocketSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleTextMessage(WebSocketSession session, String message) throws Exception {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Received payload " + message + " for " + sockJsSession);
|
||||
}
|
||||
if (StringUtils.isEmpty(message)) {
|
||||
logger.trace("Ignoring empty payload");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
String[] messages = this.objectMapper.readValue(message, String[].class);
|
||||
this.sockJsSession.delegateMessages(messages);
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger.error("Broken data received. Terminating WebSocket connection abruptly", e);
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleBinaryMessage(WebSocketSession session, InputStream message) throws Exception {
|
||||
// should not happen
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleException(WebSocketSession session, Throwable exception) {
|
||||
exception.printStackTrace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sessionClosed(WebSocketSession session, int statusCode, String reason) throws Exception {
|
||||
logger.debug("WebSocket connection closed for " + this.sockJsSession);
|
||||
this.sockJsSession.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,80 +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.sockjs.server;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.sockjs.SockJsHandler;
|
||||
import org.springframework.websocket.WebSocketSession;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
*/
|
||||
public class StandardWebSocketServerSession extends AbstractServerSession {
|
||||
|
||||
private static Log logger = LogFactory.getLog(StandardWebSocketServerSession.class);
|
||||
|
||||
private WebSocketSession webSocketSession;
|
||||
|
||||
|
||||
public StandardWebSocketServerSession(String sessionId, SockJsHandler delegate, SockJsConfiguration sockJsConfig) {
|
||||
super(sessionId, delegate, sockJsConfig);
|
||||
}
|
||||
|
||||
public void setWebSocketSession(WebSocketSession webSocketSession) throws Exception {
|
||||
this.webSocketSession = webSocketSession;
|
||||
webSocketSession.sendText(SockJsFrame.openFrame().getContent());
|
||||
scheduleHeartbeat();
|
||||
connectionInitialized();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return ((this.webSocketSession != null) && this.webSocketSession.isOpen());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessageInternal(String message) {
|
||||
cancelHeartbeat();
|
||||
writeFrame(SockJsFrame.messageFrame(message));
|
||||
scheduleHeartbeat();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeFrameInternal(SockJsFrame frame) throws Exception {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Write " + frame);
|
||||
}
|
||||
this.webSocketSession.sendText(frame.getContent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeInternal() {
|
||||
this.webSocketSession.close();
|
||||
this.webSocketSession = null;
|
||||
updateLastActiveTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deactivate() {
|
||||
this.webSocketSession.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,9 +17,7 @@ package org.springframework.sockjs.server;
|
||||
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.sockjs.SockJsHandler;
|
||||
import org.springframework.sockjs.SockJsSessionSupport;
|
||||
import org.springframework.sockjs.TransportType;
|
||||
|
||||
|
||||
/**
|
||||
@@ -31,7 +29,11 @@ public interface TransportHandler {
|
||||
|
||||
TransportType getTransportType();
|
||||
|
||||
SockJsSessionSupport createSession(String sessionId, SockJsHandler handler, SockJsConfiguration config);
|
||||
boolean canCreateSession();
|
||||
|
||||
SockJsSessionSupport createSession(String sessionId);
|
||||
|
||||
boolean handleNoSession(ServerHttpRequest request, ServerHttpResponse response);
|
||||
|
||||
void handleRequest(ServerHttpRequest request, ServerHttpResponse response, SockJsSessionSupport session)
|
||||
throws Exception;
|
||||
|
||||
@@ -23,6 +23,6 @@ package org.springframework.sockjs.server;
|
||||
*/
|
||||
public interface TransportHandlerRegistrar {
|
||||
|
||||
void registerTransportHandlers(TransportHandlerRegistry registry);
|
||||
void registerTransportHandlers(TransportHandlerRegistry registry, SockJsConfiguration config);
|
||||
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.sockjs;
|
||||
package org.springframework.sockjs.server;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.springframework.http.HttpMethod;
|
||||
*/
|
||||
public enum TransportType {
|
||||
|
||||
WEBSOCKET("websocket", HttpMethod.GET, false /* CORS ? */),
|
||||
WEBSOCKET("websocket", HttpMethod.GET, false),
|
||||
|
||||
XHR("xhr", HttpMethod.POST, true),
|
||||
XHR_SEND("xhr_send", HttpMethod.POST, true),
|
||||
@@ -45,10 +45,10 @@ public enum TransportType {
|
||||
private final boolean corsSupported;
|
||||
|
||||
|
||||
private TransportType(String value, HttpMethod httpMethod, boolean supportsCors) {
|
||||
private TransportType(String value, HttpMethod httpMethod, boolean corsSupported) {
|
||||
this.value = value;
|
||||
this.httpMethod = httpMethod;
|
||||
this.corsSupported = supportsCors;
|
||||
this.corsSupported = corsSupported;
|
||||
}
|
||||
|
||||
public String value() {
|
||||
@@ -20,9 +20,12 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.http.Cookie;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -32,12 +35,13 @@ import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.springframework.sockjs.SockJsHandler;
|
||||
import org.springframework.sockjs.SockJsSessionSupport;
|
||||
import org.springframework.sockjs.TransportType;
|
||||
import org.springframework.sockjs.server.AbstractSockJsService;
|
||||
import org.springframework.sockjs.server.TransportHandler;
|
||||
import org.springframework.sockjs.server.TransportHandlerRegistrar;
|
||||
import org.springframework.sockjs.server.TransportHandlerRegistry;
|
||||
import org.springframework.sockjs.server.TransportType;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.websocket.server.HandshakeHandler;
|
||||
|
||||
|
||||
/**
|
||||
@@ -46,10 +50,10 @@ import org.springframework.util.Assert;
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
*/
|
||||
public class DefaultSockJsService extends AbstractSockJsService implements TransportHandlerRegistry, InitializingBean {
|
||||
|
||||
private static final AtomicLong webSocketSessionIdSuffix = new AtomicLong();
|
||||
public class DefaultSockJsService extends AbstractSockJsService
|
||||
implements TransportHandlerRegistry, BeanFactoryAware, InitializingBean {
|
||||
|
||||
private final Class<? extends SockJsHandler> sockJsHandlerClass;
|
||||
|
||||
private final SockJsHandler sockJsHandler;
|
||||
|
||||
@@ -59,17 +63,24 @@ public class DefaultSockJsService extends AbstractSockJsService implements Trans
|
||||
|
||||
private final Map<TransportType, TransportHandler> transportHandlers = new HashMap<TransportType, TransportHandler>();
|
||||
|
||||
private AutowireCapableBeanFactory beanFactory;
|
||||
|
||||
|
||||
public DefaultSockJsService(String prefix, Class<? extends SockJsHandler> sockJsHandlerClass) {
|
||||
this(prefix, sockJsHandlerClass, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor...
|
||||
*
|
||||
*/
|
||||
public DefaultSockJsService(String prefix, SockJsHandler sockJsHandler) {
|
||||
this(prefix, null, sockJsHandler);
|
||||
}
|
||||
|
||||
private DefaultSockJsService(String prefix, Class<? extends SockJsHandler> handlerClass, SockJsHandler handler) {
|
||||
super(prefix);
|
||||
Assert.notNull(sockJsHandler, "sockJsHandler is required");
|
||||
this.sockJsHandler = sockJsHandler;
|
||||
Assert.isTrue(((handlerClass != null) || (handler != null)), "A sockJsHandler class or instance is required");
|
||||
this.sockJsHandlerClass = handlerClass;
|
||||
this.sockJsHandler = handler;
|
||||
this.sessionTimeoutScheduler = createScheduler("SockJs-sessionTimeout-");
|
||||
new DefaultTransportHandlerRegistrar().registerTransportHandlers(this);
|
||||
new DefaultTransportHandlerRegistrar().registerTransportHandlers(this, this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,12 +106,34 @@ public class DefaultSockJsService extends AbstractSockJsService implements Trans
|
||||
public void setTransportHandlerRegistrar(TransportHandlerRegistrar registrar) {
|
||||
Assert.notNull(registrar, "registrar is required");
|
||||
this.transportHandlers.clear();
|
||||
registrar.registerTransportHandlers(this);
|
||||
registrar.registerTransportHandlers(this, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
if (beanFactory instanceof AutowireCapableBeanFactory) {
|
||||
this.beanFactory = (AutowireCapableBeanFactory) beanFactory;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SockJsHandler getSockJsHandler() {
|
||||
return (this.sockJsHandlerClass != null) ?
|
||||
this.beanFactory.createBean(this.sockJsHandlerClass) : this.sockJsHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
|
||||
if (this.sockJsHandler != null) {
|
||||
Assert.notNull(this.beanFactory,
|
||||
"An AutowirecapableBeanFactory is required to initialize SockJS handler instances per request.");
|
||||
}
|
||||
|
||||
if (this.transportHandlers.get(TransportType.WEBSOCKET) == null) {
|
||||
logger.warn("No WebSocket transport handler was registered");
|
||||
}
|
||||
|
||||
this.sessionTimeoutScheduler.scheduleAtFixedRate(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
@@ -128,6 +161,19 @@ public class DefaultSockJsService extends AbstractSockJsService implements Trans
|
||||
}, getDisconnectDelay());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleRawWebSocket(ServerHttpRequest request, ServerHttpResponse response) throws Exception {
|
||||
TransportHandler transportHandler = this.transportHandlers.get(TransportType.WEBSOCKET);
|
||||
if ((transportHandler != null) && transportHandler instanceof HandshakeHandler) {
|
||||
HandshakeHandler handshakeHandler = (HandshakeHandler) transportHandler;
|
||||
handshakeHandler.doHandshake(request, response);
|
||||
}
|
||||
else {
|
||||
logger.debug("No handler found for raw WebSocket messages");
|
||||
response.setStatusCode(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleRequestInternal(ServerHttpRequest request, ServerHttpResponse response,
|
||||
String sessionId, TransportType transportType) throws Exception {
|
||||
@@ -159,8 +205,7 @@ public class DefaultSockJsService extends AbstractSockJsService implements Trans
|
||||
}
|
||||
|
||||
SockJsSessionSupport session = getSockJsSession(sessionId, transportHandler);
|
||||
if (session == null) {
|
||||
response.setStatusCode(HttpStatus.NOT_FOUND);
|
||||
if ((session == null) && !transportHandler.handleNoSession(request, response)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -184,19 +229,12 @@ public class DefaultSockJsService extends AbstractSockJsService implements Trans
|
||||
|
||||
public SockJsSessionSupport getSockJsSession(String sessionId, TransportHandler transportHandler) {
|
||||
|
||||
TransportType transportType = transportHandler.getTransportType();
|
||||
|
||||
// Always create new session for WebSocket requests
|
||||
sessionId = TransportType.WEBSOCKET.equals(transportType) ?
|
||||
sessionId + "#" + webSocketSessionIdSuffix.getAndIncrement() : sessionId;
|
||||
|
||||
SockJsSessionSupport session = this.sessions.get(sessionId);
|
||||
if (session != null) {
|
||||
return session;
|
||||
}
|
||||
|
||||
if (TransportType.XHR_SEND.equals(transportType) || TransportType.JSONP_SEND.equals(transportType)) {
|
||||
logger.debug(transportType + " did not find session");
|
||||
if (!transportHandler.canCreateSession()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -207,7 +245,7 @@ public class DefaultSockJsService extends AbstractSockJsService implements Trans
|
||||
}
|
||||
|
||||
logger.debug("Creating new session with session id \"" + sessionId + "\"");
|
||||
session = transportHandler.createSession(sessionId, this.sockJsHandler, this);
|
||||
session = transportHandler.createSession(sessionId);
|
||||
this.sessions.put(sessionId, session);
|
||||
|
||||
return session;
|
||||
|
||||
@@ -15,16 +15,21 @@
|
||||
*/
|
||||
package org.springframework.sockjs.server.support;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.sockjs.server.SockJsConfiguration;
|
||||
import org.springframework.sockjs.server.TransportHandler;
|
||||
import org.springframework.sockjs.server.TransportHandlerRegistrar;
|
||||
import org.springframework.sockjs.server.TransportHandlerRegistry;
|
||||
import org.springframework.sockjs.server.transport.EventSourceTransportHandler;
|
||||
import org.springframework.sockjs.server.transport.HtmlFileTransportHandler;
|
||||
import org.springframework.sockjs.server.transport.JsonpPollingTransportHandler;
|
||||
import org.springframework.sockjs.server.transport.JsonpTransportHandler;
|
||||
import org.springframework.sockjs.server.transport.WebSocketTransportHandler;
|
||||
import org.springframework.sockjs.server.transport.XhrPollingTransportHandler;
|
||||
import org.springframework.sockjs.server.transport.XhrStreamingTransportHandler;
|
||||
import org.springframework.sockjs.server.transport.XhrTransportHandler;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
|
||||
/**
|
||||
@@ -35,19 +40,38 @@ import org.springframework.sockjs.server.transport.XhrTransportHandler;
|
||||
*/
|
||||
public class DefaultTransportHandlerRegistrar implements TransportHandlerRegistrar {
|
||||
|
||||
public void registerTransportHandlers(TransportHandlerRegistry registry) {
|
||||
private static final boolean standardWebSocketApiPresent = ClassUtils.isPresent(
|
||||
"javax.websocket.server.ServerEndpointConfig", DefaultTransportHandlerRegistrar.class.getClassLoader());
|
||||
|
||||
registry.registerHandler(new WebSocketTransportHandler());
|
||||
|
||||
registry.registerHandler(new XhrPollingTransportHandler());
|
||||
public void registerTransportHandlers(TransportHandlerRegistry registry, SockJsConfiguration config) {
|
||||
|
||||
if (standardWebSocketApiPresent) {
|
||||
registry.registerHandler(createEndpointWebSocketTransportHandler(config));
|
||||
}
|
||||
|
||||
registry.registerHandler(new XhrPollingTransportHandler(config));
|
||||
registry.registerHandler(new XhrTransportHandler());
|
||||
|
||||
registry.registerHandler(new JsonpPollingTransportHandler());
|
||||
registry.registerHandler(new JsonpPollingTransportHandler(config));
|
||||
registry.registerHandler(new JsonpTransportHandler());
|
||||
|
||||
registry.registerHandler(new XhrStreamingTransportHandler());
|
||||
registry.registerHandler(new EventSourceTransportHandler());
|
||||
registry.registerHandler(new HtmlFileTransportHandler());
|
||||
registry.registerHandler(new XhrStreamingTransportHandler(config));
|
||||
registry.registerHandler(new EventSourceTransportHandler(config));
|
||||
registry.registerHandler(new HtmlFileTransportHandler(config));
|
||||
|
||||
}
|
||||
|
||||
private TransportHandler createEndpointWebSocketTransportHandler(SockJsConfiguration config) {
|
||||
try {
|
||||
String className = "org.springframework.sockjs.server.transport.EndpointWebSocketTransportHandler";
|
||||
Class<?> clazz = ClassUtils.forName(className, DefaultTransportHandlerRegistrar.class.getClassLoader());
|
||||
Constructor<?> constructor = clazz.getConstructor(SockJsConfiguration.class);
|
||||
return (TransportHandler) BeanUtils.instantiateClass(constructor, config);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throw new IllegalStateException("Failed to instantiate EndpointWebSocketTransportHandler", t);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,9 +25,7 @@ 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.sockjs.SockJsHandler;
|
||||
import org.springframework.sockjs.SockJsSessionSupport;
|
||||
import org.springframework.sockjs.server.SockJsConfiguration;
|
||||
import org.springframework.sockjs.server.TransportHandler;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
@@ -53,8 +51,19 @@ public abstract class AbstractHttpReceivingTransportHandler implements Transport
|
||||
}
|
||||
|
||||
@Override
|
||||
public SockJsSessionSupport createSession(String sessionId, SockJsHandler handler, SockJsConfiguration config) {
|
||||
return null;
|
||||
public boolean canCreateSession() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SockJsSessionSupport createSession(String sessionId) {
|
||||
throw new IllegalStateException("Transport handlers receiving messages do not create new sessions");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleNoSession(ServerHttpRequest request, ServerHttpResponse response) {
|
||||
response.setStatusCode(HttpStatus.NOT_FOUND);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -23,9 +23,10 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.sockjs.SockJsSessionSupport;
|
||||
import org.springframework.sockjs.server.SockJsConfiguration;
|
||||
import org.springframework.sockjs.server.SockJsFrame;
|
||||
import org.springframework.sockjs.server.TransportHandler;
|
||||
import org.springframework.sockjs.server.SockJsFrame.FrameFormat;
|
||||
import org.springframework.sockjs.server.TransportHandler;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
@@ -37,6 +38,26 @@ public abstract class AbstractHttpSendingTransportHandler implements TransportHa
|
||||
|
||||
protected final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private final SockJsConfiguration sockJsConfig;
|
||||
|
||||
|
||||
public AbstractHttpSendingTransportHandler(SockJsConfiguration sockJsConfig) {
|
||||
this.sockJsConfig = sockJsConfig;
|
||||
}
|
||||
|
||||
protected SockJsConfiguration getSockJsConfig() {
|
||||
return this.sockJsConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canCreateSession() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleNoSession(ServerHttpRequest request, ServerHttpResponse response) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
|
||||
|
||||
@@ -23,12 +23,11 @@ import java.util.concurrent.BlockingQueue;
|
||||
import org.springframework.http.server.AsyncServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.sockjs.SockJsHandler;
|
||||
import org.springframework.sockjs.server.AbstractServerSession;
|
||||
import org.springframework.sockjs.server.SockJsConfiguration;
|
||||
import org.springframework.sockjs.server.SockJsFrame;
|
||||
import org.springframework.sockjs.server.TransportHandler;
|
||||
import org.springframework.sockjs.server.SockJsFrame.FrameFormat;
|
||||
import org.springframework.sockjs.server.TransportHandler;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -48,8 +47,8 @@ public abstract class AbstractHttpServerSession extends AbstractServerSession {
|
||||
private OutputStream outputStream;
|
||||
|
||||
|
||||
public AbstractHttpServerSession(String sessionId, SockJsHandler delegate, SockJsConfiguration sockJsConfig) {
|
||||
super(sessionId, delegate, sockJsConfig);
|
||||
public AbstractHttpServerSession(String sessionId, SockJsConfiguration sockJsConfig) {
|
||||
super(sessionId, sockJsConfig);
|
||||
}
|
||||
|
||||
public void setFrameFormat(FrameFormat frameFormat) {
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.sockjs.server.transport;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.sockjs.SockJsSessionSupport;
|
||||
import org.springframework.sockjs.server.SockJsConfiguration;
|
||||
import org.springframework.websocket.WebSocketHandler;
|
||||
import org.springframework.websocket.WebSocketSession;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
*/
|
||||
public abstract class AbstractSockJsWebSocketHandler implements WebSocketHandler {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final SockJsConfiguration sockJsConfig;
|
||||
|
||||
private final Map<WebSocketSession, SockJsSessionSupport> sessions =
|
||||
new ConcurrentHashMap<WebSocketSession, SockJsSessionSupport>();
|
||||
|
||||
|
||||
public AbstractSockJsWebSocketHandler(SockJsConfiguration sockJsConfig) {
|
||||
this.sockJsConfig = sockJsConfig;
|
||||
}
|
||||
|
||||
protected SockJsConfiguration getSockJsConfig() {
|
||||
return this.sockJsConfig;
|
||||
}
|
||||
|
||||
protected SockJsSessionSupport getSockJsSession(WebSocketSession wsSession) {
|
||||
return this.sessions.get(wsSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void newSession(WebSocketSession wsSession) throws Exception {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("New session: " + wsSession);
|
||||
}
|
||||
SockJsSessionSupport session = createSockJsSession(wsSession);
|
||||
this.sessions.put(wsSession, session);
|
||||
session.connectionInitialized();
|
||||
}
|
||||
|
||||
protected abstract SockJsSessionSupport createSockJsSession(WebSocketSession wsSession) throws Exception;
|
||||
|
||||
@Override
|
||||
public void handleTextMessage(WebSocketSession wsSession, String message) throws Exception {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Received payload " + message);
|
||||
}
|
||||
SockJsSessionSupport session = getSockJsSession(wsSession);
|
||||
session.delegateMessages(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleBinaryMessage(WebSocketSession session, InputStream message) throws Exception {
|
||||
// should not happen
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleException(WebSocketSession webSocketSession, Throwable exception) {
|
||||
SockJsSessionSupport session = getSockJsSession(webSocketSession);
|
||||
session.delegateException(exception);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sessionClosed(WebSocketSession webSocketSession, int statusCode, String reason) throws Exception {
|
||||
logger.debug("WebSocket connection closed " + webSocketSession);
|
||||
SockJsSessionSupport session = this.sessions.remove(webSocketSession);
|
||||
session.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,7 +19,6 @@ import java.io.IOException;
|
||||
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.sockjs.SockJsHandler;
|
||||
import org.springframework.sockjs.server.SockJsConfiguration;
|
||||
|
||||
|
||||
@@ -32,9 +31,13 @@ import org.springframework.sockjs.server.SockJsConfiguration;
|
||||
public abstract class AbstractStreamingTransportHandler extends AbstractHttpSendingTransportHandler {
|
||||
|
||||
|
||||
public AbstractStreamingTransportHandler(SockJsConfiguration sockJsConfig) {
|
||||
super(sockJsConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamingHttpServerSession createSession(String sessionId, SockJsHandler handler, SockJsConfiguration config) {
|
||||
return new StreamingHttpServerSession(sessionId, handler, config);
|
||||
public StreamingHttpServerSession createSession(String sessionId) {
|
||||
return new StreamingHttpServerSession(sessionId, getSockJsConfig());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.sockjs.server.transport;
|
||||
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.sockjs.SockJsSessionSupport;
|
||||
import org.springframework.sockjs.server.SockJsConfiguration;
|
||||
import org.springframework.sockjs.server.TransportHandler;
|
||||
import org.springframework.sockjs.server.TransportType;
|
||||
import org.springframework.websocket.WebSocketHandler;
|
||||
import org.springframework.websocket.server.HandshakeHandler;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
*/
|
||||
public abstract class AbstractWebSocketTransportHandler implements TransportHandler, HandshakeHandler {
|
||||
|
||||
private final HandshakeHandler sockJsHandshakeHandler;
|
||||
|
||||
private final HandshakeHandler handshakeHandler;
|
||||
|
||||
|
||||
public AbstractWebSocketTransportHandler(SockJsConfiguration sockJsConfig) {
|
||||
this.sockJsHandshakeHandler = createHandshakeHandler(new SockJsWebSocketHandler(sockJsConfig));
|
||||
this.handshakeHandler = createHandshakeHandler(new WebSocketSockJsHandlerAdapter(sockJsConfig));
|
||||
}
|
||||
|
||||
protected abstract HandshakeHandler createHandshakeHandler(WebSocketHandler webSocketHandler);
|
||||
|
||||
@Override
|
||||
public TransportType getTransportType() {
|
||||
return TransportType.WEBSOCKET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canCreateSession() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SockJsSessionSupport createSession(String sessionId) {
|
||||
throw new IllegalStateException("WebSocket transport handlers do not create new sessions");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleNoSession(ServerHttpRequest request, ServerHttpResponse response) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
|
||||
SockJsSessionSupport session) throws Exception {
|
||||
|
||||
this.sockJsHandshakeHandler.doHandshake(request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doHandshake(ServerHttpRequest request, ServerHttpResponse response) throws Exception {
|
||||
return this.handshakeHandler.doHandshake(request, response);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.sockjs.server.transport;
|
||||
|
||||
import org.springframework.sockjs.server.SockJsConfiguration;
|
||||
import org.springframework.websocket.WebSocketHandler;
|
||||
import org.springframework.websocket.server.HandshakeHandler;
|
||||
import org.springframework.websocket.server.endpoint.EndpointHandshakeHandler;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
*/
|
||||
public class EndpointWebSocketTransportHandler extends AbstractWebSocketTransportHandler {
|
||||
|
||||
|
||||
public EndpointWebSocketTransportHandler(SockJsConfiguration sockJsConfig) {
|
||||
super(sockJsConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HandshakeHandler createHandshakeHandler(WebSocketHandler webSocketHandler) {
|
||||
return new EndpointHandshakeHandler(webSocketHandler);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,7 +21,8 @@ import java.nio.charset.Charset;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.sockjs.TransportType;
|
||||
import org.springframework.sockjs.server.SockJsConfiguration;
|
||||
import org.springframework.sockjs.server.TransportType;
|
||||
import org.springframework.sockjs.server.SockJsFrame.DefaultFrameFormat;
|
||||
import org.springframework.sockjs.server.SockJsFrame.FrameFormat;
|
||||
|
||||
@@ -35,6 +36,10 @@ import org.springframework.sockjs.server.SockJsFrame.FrameFormat;
|
||||
public class EventSourceTransportHandler extends AbstractStreamingTransportHandler {
|
||||
|
||||
|
||||
public EventSourceTransportHandler(SockJsConfiguration sockJsConfig) {
|
||||
super(sockJsConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportType getTransportType() {
|
||||
return TransportType.EVENT_SOURCE;
|
||||
|
||||
@@ -22,7 +22,8 @@ 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.sockjs.TransportType;
|
||||
import org.springframework.sockjs.server.SockJsConfiguration;
|
||||
import org.springframework.sockjs.server.TransportType;
|
||||
import org.springframework.sockjs.server.SockJsFrame.DefaultFrameFormat;
|
||||
import org.springframework.sockjs.server.SockJsFrame.FrameFormat;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -66,6 +67,10 @@ public class HtmlFileTransportHandler extends AbstractStreamingTransportHandler
|
||||
}
|
||||
|
||||
|
||||
public HtmlFileTransportHandler(SockJsConfiguration sockJsConfig) {
|
||||
super(sockJsConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportType getTransportType() {
|
||||
return TransportType.HTML_FILE;
|
||||
|
||||
@@ -21,10 +21,9 @@ 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.sockjs.SockJsHandler;
|
||||
import org.springframework.sockjs.TransportType;
|
||||
import org.springframework.sockjs.server.SockJsConfiguration;
|
||||
import org.springframework.sockjs.server.SockJsFrame;
|
||||
import org.springframework.sockjs.server.TransportType;
|
||||
import org.springframework.sockjs.server.SockJsFrame.FrameFormat;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.util.JavaScriptUtils;
|
||||
@@ -39,6 +38,10 @@ import org.springframework.web.util.JavaScriptUtils;
|
||||
public class JsonpPollingTransportHandler extends AbstractHttpSendingTransportHandler {
|
||||
|
||||
|
||||
public JsonpPollingTransportHandler(SockJsConfiguration sockJsConfig) {
|
||||
super(sockJsConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportType getTransportType() {
|
||||
return TransportType.JSONP;
|
||||
@@ -50,8 +53,8 @@ public class JsonpPollingTransportHandler extends AbstractHttpSendingTransportHa
|
||||
}
|
||||
|
||||
@Override
|
||||
public PollingHttpServerSession createSession(String sessionId, SockJsHandler handler, SockJsConfiguration config) {
|
||||
return new PollingHttpServerSession(sessionId, handler, config);
|
||||
public PollingHttpServerSession createSession(String sessionId) {
|
||||
return new PollingHttpServerSession(sessionId, getSockJsConfig());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -22,7 +22,7 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.sockjs.SockJsSessionSupport;
|
||||
import org.springframework.sockjs.TransportType;
|
||||
import org.springframework.sockjs.server.TransportType;
|
||||
|
||||
public class JsonpTransportHandler extends AbstractHttpReceivingTransportHandler {
|
||||
|
||||
|
||||
@@ -15,15 +15,14 @@
|
||||
*/
|
||||
package org.springframework.sockjs.server.transport;
|
||||
|
||||
import org.springframework.sockjs.SockJsHandler;
|
||||
import org.springframework.sockjs.server.SockJsConfiguration;
|
||||
import org.springframework.sockjs.server.SockJsFrame;
|
||||
|
||||
|
||||
public class PollingHttpServerSession extends AbstractHttpServerSession {
|
||||
|
||||
public PollingHttpServerSession(String sessionId, SockJsHandler delegate, SockJsConfiguration sockJsConfig) {
|
||||
super(sessionId, delegate, sockJsConfig);
|
||||
public PollingHttpServerSession(String sessionId, SockJsConfiguration sockJsConfig) {
|
||||
super(sessionId, sockJsConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.sockjs.server.transport;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.sockjs.SockJsHandler;
|
||||
import org.springframework.sockjs.SockJsSessionSupport;
|
||||
import org.springframework.sockjs.server.AbstractServerSession;
|
||||
import org.springframework.sockjs.server.SockJsConfiguration;
|
||||
import org.springframework.sockjs.server.SockJsFrame;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.websocket.WebSocketHandler;
|
||||
import org.springframework.websocket.WebSocketSession;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
|
||||
/**
|
||||
* A SockJS implementation of {@link WebSocketHandler}. Delegates messages to and from a
|
||||
* {@link SockJsHandler} and adds SockJS message framing.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
*/
|
||||
public class SockJsWebSocketHandler extends AbstractSockJsWebSocketHandler {
|
||||
|
||||
// TODO: JSON library used must be configurable
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
|
||||
public SockJsWebSocketHandler(SockJsConfiguration config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SockJsSessionSupport createSockJsSession(WebSocketSession wsSession) throws Exception {
|
||||
return new WebSocketServerSession(wsSession, getSockJsConfig());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleTextMessage(WebSocketSession wsSession, String message) throws Exception {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Received payload " + message + " for " + wsSession);
|
||||
}
|
||||
if (StringUtils.isEmpty(message)) {
|
||||
logger.trace("Ignoring empty payload");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
String[] messages = this.objectMapper.readValue(message, String[].class);
|
||||
SockJsSessionSupport session = getSockJsSession(wsSession);
|
||||
session.delegateMessages(messages);
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger.error("Broken data received. Terminating WebSocket connection abruptly", e);
|
||||
wsSession.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class WebSocketServerSession extends AbstractServerSession {
|
||||
|
||||
private WebSocketSession webSocketSession;
|
||||
|
||||
|
||||
public WebSocketServerSession(WebSocketSession wsSession, SockJsConfiguration config) throws Exception {
|
||||
super(String.valueOf(wsSession.hashCode()), config);
|
||||
this.webSocketSession = wsSession;
|
||||
this.webSocketSession.sendText(SockJsFrame.openFrame().getContent());
|
||||
scheduleHeartbeat();
|
||||
connectionInitialized();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return ((this.webSocketSession != null) && this.webSocketSession.isOpen());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessageInternal(String message) {
|
||||
cancelHeartbeat();
|
||||
writeFrame(SockJsFrame.messageFrame(message));
|
||||
scheduleHeartbeat();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeFrameInternal(SockJsFrame frame) throws Exception {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Write " + frame);
|
||||
}
|
||||
this.webSocketSession.sendText(frame.getContent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeInternal() {
|
||||
this.webSocketSession.close();
|
||||
this.webSocketSession = null;
|
||||
updateLastActiveTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deactivate() {
|
||||
this.webSocketSession.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,7 +18,6 @@ package org.springframework.sockjs.server.transport;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.springframework.sockjs.SockJsHandler;
|
||||
import org.springframework.sockjs.server.SockJsConfiguration;
|
||||
import org.springframework.sockjs.server.SockJsFrame;
|
||||
|
||||
@@ -28,8 +27,8 @@ public class StreamingHttpServerSession extends AbstractHttpServerSession {
|
||||
private int byteCount;
|
||||
|
||||
|
||||
public StreamingHttpServerSession(String sessionId, SockJsHandler delegate, SockJsConfiguration sockJsConfig) {
|
||||
super(sessionId, delegate, sockJsConfig);
|
||||
public StreamingHttpServerSession(String sessionId, SockJsConfiguration sockJsConfig) {
|
||||
super(sessionId, sockJsConfig);
|
||||
}
|
||||
|
||||
protected void flush() {
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.sockjs.server.transport;
|
||||
|
||||
import org.springframework.sockjs.SockJsHandler;
|
||||
import org.springframework.sockjs.SockJsSessionSupport;
|
||||
import org.springframework.sockjs.server.SockJsConfiguration;
|
||||
import org.springframework.websocket.WebSocketHandler;
|
||||
import org.springframework.websocket.WebSocketSession;
|
||||
|
||||
|
||||
/**
|
||||
* A {@link WebSocketHandler} that merely delegates to a {@link SockJsHandler} without any
|
||||
* SockJS message framing. For use with raw WebSocket communication at SockJS path
|
||||
* "/websocket".
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
*/
|
||||
public class WebSocketSockJsHandlerAdapter extends AbstractSockJsWebSocketHandler {
|
||||
|
||||
|
||||
public WebSocketSockJsHandlerAdapter(SockJsConfiguration sockJsConfig) {
|
||||
super(sockJsConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SockJsSessionSupport createSockJsSession(WebSocketSession wsSession) {
|
||||
return new WebSocketSessionAdapter(wsSession);
|
||||
}
|
||||
|
||||
|
||||
private class WebSocketSessionAdapter extends SockJsSessionSupport {
|
||||
|
||||
private final WebSocketSession wsSession;
|
||||
|
||||
|
||||
public WebSocketSessionAdapter(WebSocketSession wsSession) {
|
||||
super(String.valueOf(wsSession.hashCode()), getSockJsConfig().getSockJsHandler());
|
||||
this.wsSession = wsSession;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return (!isClosed() && this.wsSession.isOpen());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(String message) throws Exception {
|
||||
this.wsSession.sendText(message);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (!isClosed()) {
|
||||
logger.debug("Closing session");
|
||||
super.close();
|
||||
this.wsSession.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,60 +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.sockjs.server.transport;
|
||||
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.sockjs.SockJsHandler;
|
||||
import org.springframework.sockjs.SockJsSessionSupport;
|
||||
import org.springframework.sockjs.TransportType;
|
||||
import org.springframework.sockjs.server.SockJsConfiguration;
|
||||
import org.springframework.sockjs.server.StandardWebSocketServerSession;
|
||||
import org.springframework.sockjs.server.TransportHandler;
|
||||
import org.springframework.sockjs.server.SockJsWebSocketHandler;
|
||||
import org.springframework.websocket.server.HandshakeRequestHandler;
|
||||
import org.springframework.websocket.server.endpoint.EndpointHandshakeRequestHandler;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
*/
|
||||
public class WebSocketTransportHandler implements TransportHandler {
|
||||
|
||||
|
||||
@Override
|
||||
public TransportType getTransportType() {
|
||||
return TransportType.WEBSOCKET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SockJsSessionSupport createSession(String sessionId, SockJsHandler handler, SockJsConfiguration config) {
|
||||
return new StandardWebSocketServerSession(sessionId, handler, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRequest(ServerHttpRequest request, ServerHttpResponse response, SockJsSessionSupport session)
|
||||
throws Exception {
|
||||
|
||||
StandardWebSocketServerSession sockJsSession = (StandardWebSocketServerSession) session;
|
||||
SockJsWebSocketHandler webSocketHandler = new SockJsWebSocketHandler(sockJsSession);
|
||||
HandshakeRequestHandler handshakeRequestHandler = new EndpointHandshakeRequestHandler(webSocketHandler);
|
||||
handshakeRequestHandler.doHandshake(request, response);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,9 +19,8 @@ import java.nio.charset.Charset;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.sockjs.SockJsHandler;
|
||||
import org.springframework.sockjs.TransportType;
|
||||
import org.springframework.sockjs.server.SockJsConfiguration;
|
||||
import org.springframework.sockjs.server.TransportType;
|
||||
import org.springframework.sockjs.server.SockJsFrame.DefaultFrameFormat;
|
||||
import org.springframework.sockjs.server.SockJsFrame.FrameFormat;
|
||||
|
||||
@@ -35,6 +34,10 @@ import org.springframework.sockjs.server.SockJsFrame.FrameFormat;
|
||||
public class XhrPollingTransportHandler extends AbstractHttpSendingTransportHandler {
|
||||
|
||||
|
||||
public XhrPollingTransportHandler(SockJsConfiguration sockJsConfig) {
|
||||
super(sockJsConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportType getTransportType() {
|
||||
return TransportType.XHR;
|
||||
@@ -50,8 +53,8 @@ public class XhrPollingTransportHandler extends AbstractHttpSendingTransportHand
|
||||
return new DefaultFrameFormat("%s\n");
|
||||
}
|
||||
|
||||
public PollingHttpServerSession createSession(String sessionId, SockJsHandler handler, SockJsConfiguration config) {
|
||||
return new PollingHttpServerSession(sessionId, handler, config);
|
||||
public PollingHttpServerSession createSession(String sessionId) {
|
||||
return new PollingHttpServerSession(sessionId, getSockJsConfig());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,7 +21,8 @@ import java.nio.charset.Charset;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.sockjs.TransportType;
|
||||
import org.springframework.sockjs.server.SockJsConfiguration;
|
||||
import org.springframework.sockjs.server.TransportType;
|
||||
import org.springframework.sockjs.server.SockJsFrame.DefaultFrameFormat;
|
||||
import org.springframework.sockjs.server.SockJsFrame.FrameFormat;
|
||||
|
||||
@@ -35,6 +36,10 @@ import org.springframework.sockjs.server.SockJsFrame.FrameFormat;
|
||||
public class XhrStreamingTransportHandler extends AbstractStreamingTransportHandler {
|
||||
|
||||
|
||||
public XhrStreamingTransportHandler(SockJsConfiguration sockJsConfig) {
|
||||
super(sockJsConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportType getTransportType() {
|
||||
return TransportType.XHR_STREAMING;
|
||||
|
||||
@@ -19,7 +19,7 @@ import java.io.IOException;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.sockjs.TransportType;
|
||||
import org.springframework.sockjs.server.TransportType;
|
||||
|
||||
public class XhrTransportHandler extends AbstractHttpReceivingTransportHandler {
|
||||
|
||||
|
||||
@@ -152,6 +152,8 @@ public abstract class AbstractEndpointConnectionManager implements ApplicationCo
|
||||
|
||||
protected Object getEndpoint() {
|
||||
if (this.endpointClass != null) {
|
||||
Assert.notNull(this.applicationContext,
|
||||
"An ApplicationContext is required to initialize endpoint instances per request.");
|
||||
return this.applicationContext.getAutowireCapableBeanFactory().createBean(this.endpointClass);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -28,11 +28,17 @@ import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
import org.springframework.websocket.WebSocketHandler;
|
||||
|
||||
|
||||
/**
|
||||
@@ -40,14 +46,32 @@ import org.springframework.web.util.UriComponentsBuilder;
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
*/
|
||||
public abstract class AbstractHandshakeRequestHandler implements HandshakeRequestHandler {
|
||||
public abstract class AbstractHandshakeHandler implements HandshakeHandler, BeanFactoryAware {
|
||||
|
||||
private static final String GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
|
||||
protected Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final WebSocketHandler webSocketHandler;
|
||||
|
||||
private final Class<? extends WebSocketHandler> handlerClass;
|
||||
|
||||
private List<String> protocols;
|
||||
|
||||
private AutowireCapableBeanFactory beanFactory;
|
||||
|
||||
|
||||
public AbstractHandshakeHandler(WebSocketHandler webSocketHandler) {
|
||||
Assert.notNull(webSocketHandler, "webSocketHandler is required");
|
||||
this.webSocketHandler = webSocketHandler;
|
||||
this.handlerClass = null;
|
||||
}
|
||||
|
||||
public AbstractHandshakeHandler(Class<? extends WebSocketHandler> handlerClass) {
|
||||
Assert.notNull((handlerClass), "handlerClass is required");
|
||||
this.webSocketHandler = null;
|
||||
this.handlerClass = handlerClass;
|
||||
}
|
||||
|
||||
public void setProtocols(String... protocols) {
|
||||
this.protocols = Arrays.asList(protocols);
|
||||
@@ -58,7 +82,24 @@ public abstract class AbstractHandshakeRequestHandler implements HandshakeReques
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doHandshake(ServerHttpRequest request, ServerHttpResponse response) throws Exception {
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
if (beanFactory instanceof AutowireCapableBeanFactory) {
|
||||
this.beanFactory = (AutowireCapableBeanFactory) beanFactory;
|
||||
}
|
||||
}
|
||||
|
||||
protected WebSocketHandler getWebSocketHandler() {
|
||||
if (this.handlerClass != null) {
|
||||
Assert.notNull(this.beanFactory, "BeanFactory is required for WebSocketHandler instance per request.");
|
||||
return this.beanFactory.createBean(this.handlerClass);
|
||||
}
|
||||
else {
|
||||
return this.webSocketHandler;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean doHandshake(ServerHttpRequest request, ServerHttpResponse response) throws Exception {
|
||||
|
||||
logger.debug("Starting handshake for " + request.getURI());
|
||||
|
||||
@@ -106,6 +147,9 @@ public abstract class AbstractHandshakeRequestHandler implements HandshakeReques
|
||||
return true;
|
||||
}
|
||||
|
||||
protected abstract void doHandshakeInternal(ServerHttpRequest request, ServerHttpResponse response,
|
||||
String protocol) throws Exception;
|
||||
|
||||
protected boolean validateUpgradeHeader(ServerHttpRequest request, ServerHttpResponse response) throws IOException {
|
||||
if (!"WebSocket".equalsIgnoreCase(request.getHeaders().getUpgrade())) {
|
||||
response.setStatusCode(HttpStatus.BAD_REQUEST);
|
||||
@@ -168,7 +212,4 @@ public abstract class AbstractHandshakeRequestHandler implements HandshakeReques
|
||||
return DatatypeConverter.printBase64Binary(bytes);
|
||||
}
|
||||
|
||||
protected abstract void doHandshakeInternal(ServerHttpRequest request, ServerHttpResponse response, String protocol)
|
||||
throws Exception;
|
||||
|
||||
}
|
||||
@@ -25,7 +25,7 @@ import org.springframework.http.server.ServerHttpResponse;
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
*/
|
||||
public interface HandshakeRequestHandler {
|
||||
public interface HandshakeHandler {
|
||||
|
||||
|
||||
boolean doHandshake(ServerHttpRequest request, ServerHttpResponse response) throws Exception;
|
||||
@@ -23,35 +23,36 @@ import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.websocket.WebSocketHandler;
|
||||
import org.springframework.websocket.endpoint.WebSocketHandlerEndpoint;
|
||||
import org.springframework.websocket.server.AbstractHandshakeRequestHandler;
|
||||
import org.springframework.websocket.server.AbstractHandshakeHandler;
|
||||
import org.springframework.websocket.server.HandshakeHandler;
|
||||
import org.springframework.websocket.support.WebSocketHandlerEndpoint;
|
||||
|
||||
|
||||
/**
|
||||
* A {@link HandshakeHandler} for use with standard Java WebSocket.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
*/
|
||||
public class EndpointHandshakeRequestHandler extends AbstractHandshakeRequestHandler {
|
||||
public class EndpointHandshakeHandler extends AbstractHandshakeHandler {
|
||||
|
||||
private static final boolean tomcatWebSocketPresent = ClassUtils.isPresent(
|
||||
"org.apache.tomcat.websocket.server.WsHandshakeRequest", EndpointHandshakeRequestHandler.class.getClassLoader());
|
||||
"org.apache.tomcat.websocket.server.WsHandshakeRequest", EndpointHandshakeHandler.class.getClassLoader());
|
||||
|
||||
private final EndpointRegistration endpointRegistration;
|
||||
|
||||
private final EndpointRequestUpgradeStrategy upgradeStrategy;
|
||||
private final WebSocketRequestUpgradeStrategy upgradeStrategy;
|
||||
|
||||
|
||||
public EndpointHandshakeRequestHandler(WebSocketHandler webSocketHandler) {
|
||||
this(new WebSocketHandlerEndpoint(webSocketHandler));
|
||||
}
|
||||
|
||||
public EndpointHandshakeRequestHandler(Endpoint endpoint) {
|
||||
this.endpointRegistration = new EndpointRegistration("/dummy", endpoint);
|
||||
public EndpointHandshakeHandler(WebSocketHandler webSocketHandler) {
|
||||
super(webSocketHandler);
|
||||
this.upgradeStrategy = createRequestUpgradeStrategy();
|
||||
}
|
||||
|
||||
private static EndpointRequestUpgradeStrategy createRequestUpgradeStrategy() {
|
||||
public EndpointHandshakeHandler(Class<? extends WebSocketHandler> handlerClass) {
|
||||
super(handlerClass);
|
||||
this.upgradeStrategy = createRequestUpgradeStrategy();
|
||||
}
|
||||
|
||||
private static WebSocketRequestUpgradeStrategy createRequestUpgradeStrategy() {
|
||||
String className;
|
||||
if (tomcatWebSocketPresent) {
|
||||
className = "org.springframework.websocket.server.endpoint.TomcatRequestUpgradeStrategy";
|
||||
@@ -60,24 +61,21 @@ public class EndpointHandshakeRequestHandler extends AbstractHandshakeRequestHan
|
||||
throw new IllegalStateException("No suitable EndpointRequestUpgradeStrategy");
|
||||
}
|
||||
try {
|
||||
Class<?> clazz = ClassUtils.forName(className, EndpointHandshakeRequestHandler.class.getClassLoader());
|
||||
return (EndpointRequestUpgradeStrategy) BeanUtils.instantiateClass(clazz.getConstructor());
|
||||
Class<?> clazz = ClassUtils.forName(className, EndpointHandshakeHandler.class.getClassLoader());
|
||||
return (WebSocketRequestUpgradeStrategy) BeanUtils.instantiateClass(clazz.getConstructor());
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throw new IllegalStateException("Failed to instantiate " + className, t);
|
||||
}
|
||||
}
|
||||
|
||||
public EndpointRegistration getEndpointRegistration() {
|
||||
return this.endpointRegistration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doHandshakeInternal(ServerHttpRequest request, ServerHttpResponse response, String protocol)
|
||||
throws Exception {
|
||||
|
||||
logger.debug("Upgrading HTTP request");
|
||||
this.upgradeStrategy.upgrade(request, response, protocol, this.endpointRegistration);
|
||||
Endpoint endpoint = new WebSocketHandlerEndpoint(getWebSocketHandler());
|
||||
this.upgradeStrategy.upgrade(request, response, protocol, new EndpointRegistration("/dummy", endpoint));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,7 +36,7 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.context.ContextLoader;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.websocket.endpoint.WebSocketHandlerEndpoint;
|
||||
import org.springframework.websocket.support.WebSocketHandlerEndpoint;
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,7 +36,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
*/
|
||||
public class TomcatRequestUpgradeStrategy implements EndpointRequestUpgradeStrategy {
|
||||
public class TomcatRequestUpgradeStrategy implements WebSocketRequestUpgradeStrategy {
|
||||
|
||||
|
||||
@Override
|
||||
|
||||
@@ -21,14 +21,16 @@ import org.springframework.http.server.ServerHttpResponse;
|
||||
|
||||
|
||||
/**
|
||||
* A strategy for performing the actual request upgrade after the handshake checks have
|
||||
* passed, encapsulating runtime-specific steps of the handshake.
|
||||
* A strategy for performing the runtime-specific parts of a WebSocket upgrade request.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
*/
|
||||
public interface EndpointRequestUpgradeStrategy {
|
||||
public interface WebSocketRequestUpgradeStrategy {
|
||||
|
||||
/**
|
||||
* Invoked after the handshake checks have been performed and succeeded.
|
||||
*/
|
||||
void upgrade(ServerHttpRequest request, ServerHttpResponse response, String protocol,
|
||||
EndpointRegistration registration) throws Exception;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.websocket.endpoint;
|
||||
package org.springframework.websocket.support;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.websocket.endpoint;
|
||||
package org.springframework.websocket.support;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -43,7 +43,7 @@ public class WebSocketHandlerEndpoint extends Endpoint {
|
||||
|
||||
private final WebSocketHandler webSocketHandler;
|
||||
|
||||
private final Map<String, WebSocketSession> sessionMap = new ConcurrentHashMap<String, WebSocketSession>();
|
||||
private final Map<String, WebSocketSession> sessions = new ConcurrentHashMap<String, WebSocketSession>();
|
||||
|
||||
|
||||
public WebSocketHandlerEndpoint(WebSocketHandler webSocketHandler) {
|
||||
@@ -57,7 +57,7 @@ public class WebSocketHandlerEndpoint extends Endpoint {
|
||||
}
|
||||
try {
|
||||
WebSocketSession webSocketSession = new StandardWebSocketSession(session);
|
||||
this.sessionMap.put(session.getId(), webSocketSession);
|
||||
this.sessions.put(session.getId(), webSocketSession);
|
||||
session.addMessageHandler(new StandardMessageHandler(session.getId()));
|
||||
this.webSocketHandler.newSession(webSocketSession);
|
||||
}
|
||||
@@ -75,7 +75,7 @@ public class WebSocketHandlerEndpoint extends Endpoint {
|
||||
}
|
||||
try {
|
||||
WebSocketSession webSocketSession = getSession(id);
|
||||
this.sessionMap.remove(id);
|
||||
this.sessions.remove(id);
|
||||
int code = closeReason.getCloseCode().getCode();
|
||||
String reason = closeReason.getReasonPhrase();
|
||||
this.webSocketHandler.sessionClosed(webSocketSession, code, reason);
|
||||
@@ -100,7 +100,7 @@ public class WebSocketHandlerEndpoint extends Endpoint {
|
||||
}
|
||||
|
||||
private WebSocketSession getSession(String sourceSessionId) {
|
||||
WebSocketSession webSocketSession = this.sessionMap.get(sourceSessionId);
|
||||
WebSocketSession webSocketSession = this.sessions.get(sourceSessionId);
|
||||
Assert.notNull(webSocketSession, "No session");
|
||||
return webSocketSession;
|
||||
}
|
||||
@@ -4,5 +4,5 @@
|
||||
* Support for working with standard Java WebSocket Endpoint's.
|
||||
*
|
||||
*/
|
||||
package org.springframework.websocket.endpoint;
|
||||
package org.springframework.websocket.support;
|
||||
|
||||
Reference in New Issue
Block a user