Add WebSocketSession attributes + initialization
In addition to adding the attributes, there is now mechanism for initializing WebSocketSession instances from attributes of the handshake request.
This commit is contained in:
parent
166ca7a5a3
commit
2a7935a913
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.http.server;
|
||||
|
||||
import java.security.Principal;
|
||||
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
|
@ -33,4 +35,21 @@ public interface ServerHttpRequest extends HttpRequest, HttpInputMessage {
|
|||
*/
|
||||
MultiValueMap<String, String> getQueryParams();
|
||||
|
||||
/**
|
||||
* Return a {@link java.security.Principal} instance containing the name of the
|
||||
* authenticated user. If the user has not been authenticated, the method returns
|
||||
* <code>null</code>.
|
||||
*/
|
||||
Principal getPrincipal();
|
||||
|
||||
/**
|
||||
* Return the host name of the endpoint on the other end.
|
||||
*/
|
||||
String getRemoteHostName();
|
||||
|
||||
/**
|
||||
* Return the IP address of the endpoint on the other end.
|
||||
*/
|
||||
String getRemoteAddress();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import java.net.URI;
|
|||
import java.net.URISyntaxException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.Principal;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
|
|
@ -131,6 +132,21 @@ public class ServletServerHttpRequest implements ServerHttpRequest {
|
|||
return this.headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Principal getPrincipal() {
|
||||
return this.servletRequest.getUserPrincipal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRemoteHostName() {
|
||||
return this.servletRequest.getRemoteHost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRemoteAddress() {
|
||||
return this.servletRequest.getRemoteAddr();
|
||||
}
|
||||
|
||||
public Cookies getCookies() {
|
||||
if (this.cookies == null) {
|
||||
this.cookies = new Cookies();
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package org.springframework.sockjs;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.security.Principal;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
|
@ -25,7 +26,7 @@ import org.springframework.util.Assert;
|
|||
import org.springframework.websocket.CloseStatus;
|
||||
import org.springframework.websocket.TextMessage;
|
||||
import org.springframework.websocket.WebSocketHandler;
|
||||
import org.springframework.websocket.WebSocketSession;
|
||||
import org.springframework.websocket.adapter.ConfigurableWebSocketSession;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -34,12 +35,20 @@ import org.springframework.websocket.WebSocketSession;
|
|||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
*/
|
||||
public abstract class AbstractSockJsSession implements WebSocketSession {
|
||||
public abstract class AbstractSockJsSession implements ConfigurableWebSocketSession {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
|
||||
private final String sessionId;
|
||||
private final String id;
|
||||
|
||||
private URI uri;
|
||||
|
||||
private String remoteHostName;
|
||||
|
||||
private String remoteAddress;
|
||||
|
||||
private Principal principal;
|
||||
|
||||
private WebSocketHandler handler;
|
||||
|
||||
|
|
@ -57,24 +66,51 @@ public abstract class AbstractSockJsSession implements WebSocketSession {
|
|||
public AbstractSockJsSession(String sessionId, WebSocketHandler webSocketHandler) {
|
||||
Assert.notNull(sessionId, "sessionId is required");
|
||||
Assert.notNull(webSocketHandler, "webSocketHandler is required");
|
||||
this.sessionId = sessionId;
|
||||
this.id = sessionId;
|
||||
this.handler = webSocketHandler;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.sessionId;
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getUri() {
|
||||
return this.uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUri(URI uri) {
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecure() {
|
||||
// TODO
|
||||
return false;
|
||||
return "wss".equals(this.uri.getSchemeSpecificPart());
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getURI() {
|
||||
// TODO
|
||||
return null;
|
||||
public String getRemoteHostName() {
|
||||
return this.remoteHostName;
|
||||
}
|
||||
|
||||
public void setRemoteHostName(String remoteHostName) {
|
||||
this.remoteHostName = remoteHostName;
|
||||
}
|
||||
|
||||
public String getRemoteAddress() {
|
||||
return this.remoteAddress;
|
||||
}
|
||||
|
||||
public void setRemoteAddress(String remoteAddress) {
|
||||
this.remoteAddress = remoteAddress;
|
||||
}
|
||||
|
||||
public Principal getPrincipal() {
|
||||
return this.principal;
|
||||
}
|
||||
|
||||
public void setPrincipal(Principal principal) {
|
||||
this.principal = principal;
|
||||
}
|
||||
|
||||
public boolean isNew() {
|
||||
|
|
@ -213,7 +249,7 @@ public abstract class AbstractSockJsSession implements WebSocketSession {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SockJS session id=" + this.sessionId;
|
||||
return "SockJS session id=" + this.id;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
package org.springframework.sockjs;
|
||||
|
||||
import org.springframework.websocket.WebSocketHandler;
|
||||
import org.springframework.websocket.WebSocketSession;
|
||||
|
||||
/**
|
||||
* A factory for creating a SockJS session.
|
||||
|
|
@ -26,7 +25,7 @@ import org.springframework.websocket.WebSocketSession;
|
|||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
*/
|
||||
public interface SockJsSessionFactory<S extends WebSocketSession>{
|
||||
public interface SockJsSessionFactory {
|
||||
|
||||
/**
|
||||
* Create a new SockJS session.
|
||||
|
|
@ -34,6 +33,6 @@ public interface SockJsSessionFactory<S extends WebSocketSession>{
|
|||
* @param webSocketHandler the underlying {@link WebSocketHandler}
|
||||
* @return a new non-null session
|
||||
*/
|
||||
S createSession(String sessionId, WebSocketHandler webSocketHandler);
|
||||
AbstractSockJsSession createSession(String sessionId, WebSocketHandler webSocketHandler);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ public abstract class AbstractSockJsService implements SockJsService, SockJsConf
|
|||
private static final int ONE_YEAR = 365 * 24 * 60 * 60;
|
||||
|
||||
|
||||
private String name = getClass().getSimpleName() + "@" + ObjectUtils.getIdentityHexString(this);
|
||||
private String name = "SockJS Service " + ObjectUtils.getIdentityHexString(this);
|
||||
|
||||
private String clientLibraryUrl = "https://d1fxtkz8shb9d2.cloudfront.net/sockjs-0.3.4.min.js";
|
||||
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ public enum TransportType {
|
|||
return this.httpMethod;
|
||||
}
|
||||
|
||||
public boolean setsNoCacheHeader() {
|
||||
public boolean setsNoCache() {
|
||||
return this.headerHints.contains("no_cache");
|
||||
}
|
||||
|
||||
|
|
@ -76,7 +76,7 @@ public enum TransportType {
|
|||
return this.headerHints.contains("cors");
|
||||
}
|
||||
|
||||
public boolean setsJsessionIdCookie() {
|
||||
public boolean setsJsessionId() {
|
||||
return this.headerHints.contains("jsessionid");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ import org.springframework.util.CollectionUtils;
|
|||
import org.springframework.websocket.WebSocketHandler;
|
||||
import org.springframework.websocket.server.DefaultHandshakeHandler;
|
||||
import org.springframework.websocket.server.HandshakeHandler;
|
||||
import org.springframework.websocket.server.support.ServerWebSocketSessionInitializer;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -69,6 +70,8 @@ public class DefaultSockJsService extends AbstractSockJsService {
|
|||
|
||||
private final Map<String, AbstractSockJsSession> sessions = new ConcurrentHashMap<String, AbstractSockJsSession>();
|
||||
|
||||
private final ServerWebSocketSessionInitializer sessionInitializer = new ServerWebSocketSessionInitializer();
|
||||
|
||||
private ScheduledFuture sessionCleanupTask;
|
||||
|
||||
|
||||
|
|
@ -187,14 +190,15 @@ public class DefaultSockJsService extends AbstractSockJsService {
|
|||
return;
|
||||
}
|
||||
|
||||
AbstractSockJsSession session = getSockJsSession(sessionId, webSocketHandler, transportHandler);
|
||||
AbstractSockJsSession session = getSockJsSession(sessionId, webSocketHandler,
|
||||
transportHandler, request, response);
|
||||
|
||||
if (session != null) {
|
||||
if (transportType.setsNoCacheHeader()) {
|
||||
if (transportType.setsNoCache()) {
|
||||
addNoCacheHeaders(response);
|
||||
}
|
||||
|
||||
if (transportType.setsJsessionIdCookie() && isJsessionIdCookieRequired()) {
|
||||
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)
|
||||
|
|
@ -209,8 +213,8 @@ public class DefaultSockJsService extends AbstractSockJsService {
|
|||
transportHandler.handleRequest(request, response, webSocketHandler, session);
|
||||
}
|
||||
|
||||
public AbstractSockJsSession getSockJsSession(String sessionId,
|
||||
WebSocketHandler webSocketHandler, TransportHandler transportHandler) {
|
||||
protected AbstractSockJsSession getSockJsSession(String sessionId, WebSocketHandler handler,
|
||||
TransportHandler transportHandler, ServerHttpRequest request, ServerHttpResponse response) {
|
||||
|
||||
AbstractSockJsSession session = this.sessions.get(sessionId);
|
||||
if (session != null) {
|
||||
|
|
@ -218,7 +222,7 @@ public class DefaultSockJsService extends AbstractSockJsService {
|
|||
}
|
||||
|
||||
if (transportHandler instanceof SockJsSessionFactory) {
|
||||
SockJsSessionFactory<?> sessionFactory = (SockJsSessionFactory<?>) transportHandler;
|
||||
SockJsSessionFactory sessionFactory = (SockJsSessionFactory) transportHandler;
|
||||
|
||||
synchronized (this.sessions) {
|
||||
session = this.sessions.get(sessionId);
|
||||
|
|
@ -229,7 +233,8 @@ public class DefaultSockJsService extends AbstractSockJsService {
|
|||
scheduleSessionTask();
|
||||
}
|
||||
logger.debug("Creating new session with session id \"" + sessionId + "\"");
|
||||
session = (AbstractSockJsSession) sessionFactory.createSession(sessionId, webSocketHandler);
|
||||
session = sessionFactory.createSession(sessionId, handler);
|
||||
this.sessionInitializer.initialize(request, response, session);
|
||||
this.sessions.put(sessionId, session);
|
||||
return session;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ import org.springframework.websocket.WebSocketHandler;
|
|||
* @since 4.0
|
||||
*/
|
||||
public abstract class AbstractHttpSendingTransportHandler
|
||||
implements ConfigurableTransportHandler, SockJsSessionFactory<AbstractSockJsSession> {
|
||||
implements ConfigurableTransportHandler, SockJsSessionFactory {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
|
|
|
|||
|
|
@ -16,22 +16,16 @@
|
|||
|
||||
package org.springframework.sockjs.server.transport;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.springframework.sockjs.server.AbstractServerSockJsSession;
|
||||
import org.springframework.sockjs.server.SockJsConfiguration;
|
||||
import org.springframework.sockjs.server.SockJsFrame;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.websocket.CloseStatus;
|
||||
import org.springframework.websocket.TextMessage;
|
||||
import org.springframework.websocket.WebSocketHandler;
|
||||
import org.springframework.websocket.WebSocketSession;
|
||||
import org.springframework.websocket.adapter.TextWebSocketHandlerAdapter;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
|
||||
/**
|
||||
* A wrapper around a {@link WebSocketHandler} instance that parses and adds SockJS
|
||||
|
|
@ -52,21 +46,20 @@ public class SockJsWebSocketHandler extends TextWebSocketHandlerAdapter {
|
|||
|
||||
private final SockJsConfiguration sockJsConfig;
|
||||
|
||||
private final WebSocketHandler webSocketHandler;
|
||||
|
||||
private WebSocketServerSockJsSession sockJsSession;
|
||||
private WebSocketServerSockJsSession session;
|
||||
|
||||
private final AtomicInteger sessionCount = new AtomicInteger(0);
|
||||
|
||||
// TODO: JSON library used must be configurable
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
public SockJsWebSocketHandler(SockJsConfiguration config,
|
||||
WebSocketHandler webSocketHandler, WebSocketServerSockJsSession session) {
|
||||
|
||||
public SockJsWebSocketHandler(SockJsConfiguration config, WebSocketHandler webSocketHandler) {
|
||||
Assert.notNull(config, "sockJsConfig is required");
|
||||
Assert.notNull(webSocketHandler, "webSocketHandler is required");
|
||||
Assert.notNull(session, "session is required");
|
||||
|
||||
this.sockJsConfig = config;
|
||||
this.webSocketHandler = webSocketHandler;
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
protected SockJsConfiguration getSockJsConfig() {
|
||||
|
|
@ -76,100 +69,22 @@ public class SockJsWebSocketHandler extends TextWebSocketHandlerAdapter {
|
|||
@Override
|
||||
public void afterConnectionEstablished(WebSocketSession wsSession) throws Exception {
|
||||
Assert.isTrue(this.sessionCount.compareAndSet(0, 1), "Unexpected connection");
|
||||
this.sockJsSession = new WebSocketServerSockJsSession(getSockJsSessionId(wsSession), getSockJsConfig());
|
||||
this.sockJsSession.initWebSocketSession(wsSession);
|
||||
this.session.initWebSocketSession(wsSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleTextMessage(WebSocketSession wsSession, TextMessage message) throws Exception {
|
||||
this.sockJsSession.handleMessage(message, wsSession);
|
||||
this.session.handleMessage(message, wsSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterConnectionClosed(WebSocketSession wsSession, CloseStatus status) throws Exception {
|
||||
this.sockJsSession.delegateConnectionClosed(status);
|
||||
this.session.delegateConnectionClosed(status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleTransportError(WebSocketSession webSocketSession, Throwable exception) throws Exception {
|
||||
this.sockJsSession.delegateError(exception);
|
||||
}
|
||||
|
||||
private static String getSockJsSessionId(WebSocketSession wsSession) {
|
||||
Assert.notNull(wsSession, "wsSession is required");
|
||||
String path = wsSession.getURI().getPath();
|
||||
String[] segments = StringUtils.tokenizeToStringArray(path, "/");
|
||||
Assert.isTrue(segments.length > 3, "SockJS request should have at least 3 path segments: " + path);
|
||||
return segments[segments.length-2];
|
||||
}
|
||||
|
||||
|
||||
private class WebSocketServerSockJsSession extends AbstractServerSockJsSession {
|
||||
|
||||
private WebSocketSession wsSession;
|
||||
|
||||
|
||||
public WebSocketServerSockJsSession(String sessionId, SockJsConfiguration config) {
|
||||
super(sessionId, config, SockJsWebSocketHandler.this.webSocketHandler);
|
||||
}
|
||||
|
||||
public void initWebSocketSession(WebSocketSession wsSession) throws Exception {
|
||||
this.wsSession = wsSession;
|
||||
try {
|
||||
TextMessage message = new TextMessage(SockJsFrame.openFrame().getContent());
|
||||
this.wsSession.sendMessage(message);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
tryCloseWithSockJsTransportError(ex, null);
|
||||
return;
|
||||
}
|
||||
scheduleHeartbeat();
|
||||
delegateConnectionEstablished();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return this.wsSession.isOpen();
|
||||
}
|
||||
|
||||
public void handleMessage(TextMessage message, WebSocketSession wsSession) throws Exception {
|
||||
String payload = message.getPayload();
|
||||
if (StringUtils.isEmpty(payload)) {
|
||||
logger.trace("Ignoring empty message");
|
||||
return;
|
||||
}
|
||||
String[] messages;
|
||||
try {
|
||||
messages = objectMapper.readValue(payload, String[].class);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
logger.error("Broken data received. Terminating WebSocket connection abruptly", ex);
|
||||
tryCloseWithSockJsTransportError(ex, CloseStatus.BAD_DATA);
|
||||
return;
|
||||
}
|
||||
delegateMessages(messages);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessageInternal(String message) throws IOException {
|
||||
cancelHeartbeat();
|
||||
writeFrame(SockJsFrame.messageFrame(message));
|
||||
scheduleHeartbeat();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeFrameInternal(SockJsFrame frame) throws IOException {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Write " + frame);
|
||||
}
|
||||
TextMessage message = new TextMessage(frame.getContent());
|
||||
this.wsSession.sendMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void disconnect(CloseStatus status) throws IOException {
|
||||
this.wsSession.close(status);
|
||||
}
|
||||
this.session.delegateError(exception);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* 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.server.AbstractServerSockJsSession;
|
||||
import org.springframework.sockjs.server.SockJsConfiguration;
|
||||
import org.springframework.sockjs.server.SockJsFrame;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.websocket.CloseStatus;
|
||||
import org.springframework.websocket.TextMessage;
|
||||
import org.springframework.websocket.WebSocketHandler;
|
||||
import org.springframework.websocket.WebSocketSession;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
|
||||
/**
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
*/
|
||||
public class WebSocketServerSockJsSession extends AbstractServerSockJsSession {
|
||||
|
||||
private WebSocketSession webSocketSession;
|
||||
|
||||
// TODO: JSON library used must be configurable
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
|
||||
public WebSocketServerSockJsSession(String sessionId, SockJsConfiguration config, WebSocketHandler handler) {
|
||||
super(sessionId, config, handler);
|
||||
}
|
||||
|
||||
public void initWebSocketSession(WebSocketSession session) throws Exception {
|
||||
this.webSocketSession = session;
|
||||
try {
|
||||
TextMessage message = new TextMessage(SockJsFrame.openFrame().getContent());
|
||||
this.webSocketSession.sendMessage(message);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
tryCloseWithSockJsTransportError(ex, null);
|
||||
return;
|
||||
}
|
||||
scheduleHeartbeat();
|
||||
delegateConnectionEstablished();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return this.webSocketSession.isOpen();
|
||||
}
|
||||
|
||||
public void handleMessage(TextMessage message, WebSocketSession wsSession) throws Exception {
|
||||
String payload = message.getPayload();
|
||||
if (StringUtils.isEmpty(payload)) {
|
||||
logger.trace("Ignoring empty message");
|
||||
return;
|
||||
}
|
||||
String[] messages;
|
||||
try {
|
||||
messages = objectMapper.readValue(payload, String[].class);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
logger.error("Broken data received. Terminating WebSocket connection abruptly", ex);
|
||||
tryCloseWithSockJsTransportError(ex, CloseStatus.BAD_DATA);
|
||||
return;
|
||||
}
|
||||
delegateMessages(messages);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessageInternal(String message) throws IOException {
|
||||
cancelHeartbeat();
|
||||
writeFrame(SockJsFrame.messageFrame(message));
|
||||
scheduleHeartbeat();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeFrameInternal(SockJsFrame frame) throws IOException {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Write " + frame);
|
||||
}
|
||||
TextMessage message = new TextMessage(frame.getContent());
|
||||
this.webSocketSession.sendMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void disconnect(CloseStatus status) throws IOException {
|
||||
this.webSocketSession.close(status);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -21,6 +21,7 @@ import java.io.IOException;
|
|||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.sockjs.AbstractSockJsSession;
|
||||
import org.springframework.sockjs.SockJsSessionFactory;
|
||||
import org.springframework.sockjs.server.ConfigurableTransportHandler;
|
||||
import org.springframework.sockjs.server.SockJsConfiguration;
|
||||
import org.springframework.sockjs.server.TransportErrorException;
|
||||
|
|
@ -39,7 +40,8 @@ import org.springframework.websocket.server.HandshakeHandler;
|
|||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
*/
|
||||
public class WebSocketTransportHandler implements ConfigurableTransportHandler, HandshakeHandler {
|
||||
public class WebSocketTransportHandler implements ConfigurableTransportHandler,
|
||||
HandshakeHandler, SockJsSessionFactory {
|
||||
|
||||
private final HandshakeHandler handshakeHandler;
|
||||
|
||||
|
|
@ -61,12 +63,18 @@ public class WebSocketTransportHandler implements ConfigurableTransportHandler,
|
|||
this.sockJsConfig = sockJsConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractSockJsSession createSession(String sessionId, WebSocketHandler webSocketHandler) {
|
||||
return new WebSocketServerSockJsSession(sessionId, this.sockJsConfig, webSocketHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
|
||||
WebSocketHandler webSocketHandler, AbstractSockJsSession session) throws TransportErrorException {
|
||||
|
||||
try {
|
||||
WebSocketHandler sockJsWrapper = new SockJsWebSocketHandler(this.sockJsConfig, webSocketHandler);
|
||||
WebSocketServerSockJsSession wsSession = (WebSocketServerSockJsSession) session;
|
||||
WebSocketHandler sockJsWrapper = new SockJsWebSocketHandler(this.sockJsConfig, webSocketHandler, wsSession);
|
||||
this.handshakeHandler.doHandshake(request, response, sockJsWrapper);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,9 @@
|
|||
package org.springframework.websocket;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.security.Principal;
|
||||
|
||||
/**
|
||||
* Allows sending messages over a WebSocket connection as well as closing it.
|
||||
|
|
@ -33,9 +35,9 @@ public interface WebSocketSession {
|
|||
String getId();
|
||||
|
||||
/**
|
||||
* Return whether the connection is still open.
|
||||
* Return the URI used to open the WebSocket connection.
|
||||
*/
|
||||
boolean isOpen();
|
||||
URI getUri();
|
||||
|
||||
/**
|
||||
* Return whether the underlying socket is using a secure transport.
|
||||
|
|
@ -43,9 +45,26 @@ public interface WebSocketSession {
|
|||
boolean isSecure();
|
||||
|
||||
/**
|
||||
* Return the URI used to open the WebSocket connection.
|
||||
* Return a {@link java.security.Principal} instance containing the name of the
|
||||
* authenticated user. If the user has not been authenticated, the method returns
|
||||
* <code>null</code>.
|
||||
*/
|
||||
URI getURI();
|
||||
Principal getPrincipal();
|
||||
|
||||
/**
|
||||
* Return the host name of the endpoint on the other end.
|
||||
*/
|
||||
String getRemoteHostName();
|
||||
|
||||
/**
|
||||
* Return the IP address of the endpoint on the other end.
|
||||
*/
|
||||
String getRemoteAddress();
|
||||
|
||||
/**
|
||||
* Return whether the connection is still open.
|
||||
*/
|
||||
boolean isOpen();
|
||||
|
||||
/**
|
||||
* Send a WebSocket message either {@link TextMessage} or
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
/*
|
||||
* 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.
|
||||
|
|
@ -34,11 +33,13 @@ import org.springframework.websocket.WebSocketSession;
|
|||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
*/
|
||||
public abstract class AbstractWebSocketSesssionAdapter implements WebSocketSession {
|
||||
public abstract class AbstractWebSocketSesssionAdapter<T> implements ConfigurableWebSocketSession {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
|
||||
public abstract void initSession(T session);
|
||||
|
||||
@Override
|
||||
public final void sendMessage(WebSocketMessage message) throws IOException {
|
||||
if (logger.isTraceEnabled()) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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.websocket.adapter;
|
||||
|
||||
import java.net.URI;
|
||||
import java.security.Principal;
|
||||
|
||||
import org.springframework.websocket.WebSocketSession;
|
||||
|
||||
|
||||
/**
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
*/
|
||||
public interface ConfigurableWebSocketSession extends WebSocketSession {
|
||||
|
||||
void setUri(URI uri);
|
||||
|
||||
void setRemoteHostName(String name);
|
||||
|
||||
void setRemoteAddress(String address);
|
||||
|
||||
void setPrincipal(Principal principal);
|
||||
|
||||
}
|
||||
|
|
@ -25,7 +25,6 @@ import org.springframework.websocket.BinaryMessage;
|
|||
import org.springframework.websocket.CloseStatus;
|
||||
import org.springframework.websocket.TextMessage;
|
||||
import org.springframework.websocket.WebSocketHandler;
|
||||
import org.springframework.websocket.WebSocketSession;
|
||||
import org.springframework.websocket.support.ExceptionWebSocketHandlerDecorator;
|
||||
|
||||
/**
|
||||
|
|
@ -40,18 +39,20 @@ public class JettyWebSocketListenerAdapter implements WebSocketListener {
|
|||
|
||||
private final WebSocketHandler webSocketHandler;
|
||||
|
||||
private WebSocketSession wsSession;
|
||||
private JettyWebSocketSessionAdapter wsSession;
|
||||
|
||||
|
||||
public JettyWebSocketListenerAdapter(WebSocketHandler webSocketHandler) {
|
||||
public JettyWebSocketListenerAdapter(WebSocketHandler webSocketHandler, JettyWebSocketSessionAdapter wsSession) {
|
||||
Assert.notNull(webSocketHandler, "webSocketHandler is required");
|
||||
Assert.notNull(wsSession, "wsSession is required");
|
||||
this.webSocketHandler = webSocketHandler;
|
||||
this.wsSession = wsSession;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onWebSocketConnect(Session session) {
|
||||
this.wsSession = new JettyWebSocketSessionAdapter(session);
|
||||
this.wsSession.initSession(session);
|
||||
try {
|
||||
this.webSocketHandler.afterConnectionEstablished(this.wsSession);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,9 +17,12 @@
|
|||
package org.springframework.websocket.adapter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.security.Principal;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.websocket.BinaryMessage;
|
||||
import org.springframework.websocket.CloseStatus;
|
||||
|
|
@ -31,38 +34,78 @@ import org.springframework.websocket.WebSocketSession;
|
|||
* Adapts Jetty's {@link Session} to Spring's {@link WebSocketSession}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
*/
|
||||
public class JettyWebSocketSessionAdapter extends AbstractWebSocketSesssionAdapter {
|
||||
public class JettyWebSocketSessionAdapter
|
||||
extends AbstractWebSocketSesssionAdapter<org.eclipse.jetty.websocket.api.Session> {
|
||||
|
||||
private Session session;
|
||||
|
||||
private Principal principal;
|
||||
|
||||
public JettyWebSocketSessionAdapter(Session session) {
|
||||
|
||||
@Override
|
||||
public void initSession(Session session) {
|
||||
Assert.notNull(session, "session is required");
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return ObjectUtils.getIdentityHexString(this.session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return this.session.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecure() {
|
||||
return this.session.isSecure();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getURI() {
|
||||
public URI getUri() {
|
||||
return this.session.getUpgradeRequest().getRequestURI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUri(URI uri) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Principal getPrincipal() {
|
||||
return this.principal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrincipal(Principal principal) {
|
||||
this.principal = principal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRemoteHostName() {
|
||||
return this.session.getRemoteAddress().getHostName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRemoteHostName(String address) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRemoteAddress() {
|
||||
InetSocketAddress address = this.session.getRemoteAddress();
|
||||
return address.isUnresolved() ? null : address.getAddress().getHostAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRemoteAddress(String address) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return this.session.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendTextMessage(TextMessage message) throws IOException {
|
||||
this.session.getRemote().sendString(message.getPayload());
|
||||
|
|
|
|||
|
|
@ -30,12 +30,11 @@ import org.springframework.websocket.BinaryMessage;
|
|||
import org.springframework.websocket.CloseStatus;
|
||||
import org.springframework.websocket.TextMessage;
|
||||
import org.springframework.websocket.WebSocketHandler;
|
||||
import org.springframework.websocket.WebSocketSession;
|
||||
import org.springframework.websocket.support.ExceptionWebSocketHandlerDecorator;
|
||||
|
||||
|
||||
/**
|
||||
* An {@link Endpoint} that delegates to a {@link WebSocketHandler}.
|
||||
* A wrapper around a {@link WebSocketHandler} that adapts it to {@link Endpoint}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
|
|
@ -46,19 +45,22 @@ public class StandardEndpointAdapter extends Endpoint {
|
|||
|
||||
private final WebSocketHandler handler;
|
||||
|
||||
private WebSocketSession wsSession;
|
||||
private final StandardWebSocketSessionAdapter wsSession;
|
||||
|
||||
|
||||
public StandardEndpointAdapter(WebSocketHandler webSocketHandler) {
|
||||
Assert.notNull(webSocketHandler, "webSocketHandler is required");
|
||||
this.handler = webSocketHandler;
|
||||
public StandardEndpointAdapter(WebSocketHandler handler, StandardWebSocketSessionAdapter wsSession) {
|
||||
Assert.notNull(handler, "handler is required");
|
||||
Assert.notNull(wsSession, "wsSession is required");
|
||||
this.handler = handler;
|
||||
this.wsSession = wsSession;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onOpen(final javax.websocket.Session session, EndpointConfig config) {
|
||||
|
||||
this.wsSession = new StandardWebSocketSessionAdapter(session);
|
||||
this.wsSession.initSession(session);
|
||||
|
||||
try {
|
||||
this.handler.afterConnectionEstablished(this.wsSession);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package org.springframework.websocket.adapter;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.security.Principal;
|
||||
|
||||
import javax.websocket.CloseReason;
|
||||
import javax.websocket.CloseReason.CloseCodes;
|
||||
|
|
@ -35,35 +36,76 @@ import org.springframework.websocket.WebSocketSession;
|
|||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
*/
|
||||
public class StandardWebSocketSessionAdapter extends AbstractWebSocketSesssionAdapter {
|
||||
public class StandardWebSocketSessionAdapter extends AbstractWebSocketSesssionAdapter<javax.websocket.Session> {
|
||||
|
||||
private final javax.websocket.Session session;
|
||||
private javax.websocket.Session session;
|
||||
|
||||
private URI uri;
|
||||
|
||||
private String remoteHostName;
|
||||
|
||||
private String remoteAddress;
|
||||
|
||||
|
||||
public StandardWebSocketSessionAdapter(javax.websocket.Session session) {
|
||||
public void initSession(javax.websocket.Session session) {
|
||||
Assert.notNull(session, "session is required");
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return this.session.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return this.session.isOpen();
|
||||
public URI getUri() {
|
||||
return this.uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUri(URI uri) {
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isSecure() {
|
||||
return this.session.isSecure();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getURI() {
|
||||
return this.session.getRequestURI();
|
||||
public Principal getPrincipal() {
|
||||
return this.session.getUserPrincipal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrincipal(Principal principal) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRemoteHostName() {
|
||||
return this.remoteHostName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRemoteHostName(String name) {
|
||||
this.remoteHostName = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRemoteAddress() {
|
||||
return this.remoteAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRemoteAddress(String address) {
|
||||
this.remoteAddress = address;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return this.session.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -28,12 +28,12 @@ import javax.websocket.ClientEndpointConfig.Configurator;
|
|||
import javax.websocket.ContainerProvider;
|
||||
import javax.websocket.Endpoint;
|
||||
import javax.websocket.HandshakeResponse;
|
||||
import javax.websocket.Session;
|
||||
import javax.websocket.WebSocketContainer;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
import org.springframework.websocket.WebSocketHandler;
|
||||
import org.springframework.websocket.WebSocketSession;
|
||||
|
|
@ -67,15 +67,26 @@ public class StandardWebSocketClient implements WebSocketClient {
|
|||
public WebSocketSession doHandshake(WebSocketHandler webSocketHandler, String uriTemplate, Object... uriVariables)
|
||||
throws WebSocketConnectFailureException {
|
||||
|
||||
URI uri = UriComponentsBuilder.fromUriString(uriTemplate).buildAndExpand(uriVariables).encode().toUri();
|
||||
return doHandshake(webSocketHandler, null, uri);
|
||||
UriComponents uriComponents = UriComponentsBuilder.fromUriString(uriTemplate).buildAndExpand(uriVariables).encode();
|
||||
return doHandshake(webSocketHandler, null, uriComponents);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebSocketSession doHandshake(WebSocketHandler webSocketHandler,
|
||||
final HttpHeaders httpHeaders, URI uri) throws WebSocketConnectFailureException {
|
||||
|
||||
Endpoint endpoint = new StandardEndpointAdapter(webSocketHandler);
|
||||
return doHandshake(webSocketHandler, httpHeaders, UriComponentsBuilder.fromUri(uri).build());
|
||||
}
|
||||
|
||||
public WebSocketSession doHandshake(WebSocketHandler webSocketHandler,
|
||||
final HttpHeaders httpHeaders, UriComponents uriComponents) throws WebSocketConnectFailureException {
|
||||
|
||||
URI uri = uriComponents.toUri();
|
||||
|
||||
StandardWebSocketSessionAdapter session = new StandardWebSocketSessionAdapter();
|
||||
session.setUri(uri);
|
||||
session.setRemoteHostName(uriComponents.getHost());
|
||||
Endpoint endpoint = new StandardEndpointAdapter(webSocketHandler, session);
|
||||
|
||||
ClientEndpointConfig.Builder configBuidler = ClientEndpointConfig.Builder.create();
|
||||
if (httpHeaders != null) {
|
||||
|
|
@ -109,8 +120,9 @@ public class StandardWebSocketClient implements WebSocketClient {
|
|||
}
|
||||
|
||||
try {
|
||||
Session session = this.webSocketContainer.connectToServer(endpoint, configBuidler.build(), uri);
|
||||
return new StandardWebSocketSessionAdapter(session);
|
||||
// TODO: do not block
|
||||
this.webSocketContainer.connectToServer(endpoint, configBuidler.build(), uri);
|
||||
return session;
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new WebSocketConnectFailureException("Failed to connect to " + uri, e);
|
||||
|
|
|
|||
|
|
@ -17,13 +17,12 @@
|
|||
package org.springframework.websocket.client.jetty;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
import org.springframework.websocket.WebSocketHandler;
|
||||
import org.springframework.websocket.WebSocketSession;
|
||||
|
|
@ -126,23 +125,34 @@ public class JettyWebSocketClient implements WebSocketClient, SmartLifecycle {
|
|||
public WebSocketSession doHandshake(WebSocketHandler webSocketHandler, String uriTemplate, Object... uriVariables)
|
||||
throws WebSocketConnectFailureException {
|
||||
|
||||
URI uri = UriComponentsBuilder.fromUriString(uriTemplate).buildAndExpand(uriVariables).encode().toUri();
|
||||
return doHandshake(webSocketHandler, null, uri);
|
||||
UriComponents uriComponents = UriComponentsBuilder.fromUriString(uriTemplate).buildAndExpand(uriVariables).encode();
|
||||
return doHandshake(webSocketHandler, null, uriComponents);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebSocketSession doHandshake(WebSocketHandler webSocketHandler, HttpHeaders headers, URI uri)
|
||||
throws WebSocketConnectFailureException {
|
||||
|
||||
return doHandshake(webSocketHandler, headers, UriComponentsBuilder.fromUri(uri).build());
|
||||
}
|
||||
|
||||
public WebSocketSession doHandshake(WebSocketHandler webSocketHandler, HttpHeaders headers, UriComponents uriComponents)
|
||||
throws WebSocketConnectFailureException {
|
||||
|
||||
// TODO: populate headers
|
||||
|
||||
JettyWebSocketListenerAdapter listener = new JettyWebSocketListenerAdapter(webSocketHandler);
|
||||
URI uri = uriComponents.toUri();
|
||||
|
||||
JettyWebSocketSessionAdapter session = new JettyWebSocketSessionAdapter();
|
||||
session.setUri(uri);
|
||||
session.setRemoteHostName(uriComponents.getHost());
|
||||
|
||||
JettyWebSocketListenerAdapter listener = new JettyWebSocketListenerAdapter(webSocketHandler, session);
|
||||
|
||||
try {
|
||||
// block for now
|
||||
Future<org.eclipse.jetty.websocket.api.Session> future = this.client.connect(listener, uri);
|
||||
Session session = future.get();
|
||||
return new JettyWebSocketSessionAdapter(session);
|
||||
// TODO: do not block
|
||||
this.client.connect(listener, uri).get();
|
||||
return session;
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new WebSocketConnectFailureException("Failed to connect to " + uri, e);
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ import org.springframework.http.server.ServerHttpRequest;
|
|||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.websocket.WebSocketHandler;
|
||||
import org.springframework.websocket.adapter.StandardEndpointAdapter;
|
||||
import org.springframework.websocket.adapter.StandardWebSocketSessionAdapter;
|
||||
import org.springframework.websocket.server.HandshakeFailureException;
|
||||
import org.springframework.websocket.server.RequestUpgradeStrategy;
|
||||
|
||||
/**
|
||||
|
|
@ -39,15 +41,20 @@ public abstract class AbstractEndpointUpgradeStrategy implements RequestUpgradeS
|
|||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final ServerWebSocketSessionInitializer wsSessionInitializer = new ServerWebSocketSessionInitializer();
|
||||
|
||||
|
||||
@Override
|
||||
public void upgrade(ServerHttpRequest request, ServerHttpResponse response,
|
||||
String protocol, WebSocketHandler webSocketHandler) throws IOException {
|
||||
String protocol, WebSocketHandler handler) throws IOException, HandshakeFailureException {
|
||||
|
||||
upgradeInternal(request, response, protocol, new StandardEndpointAdapter(webSocketHandler));
|
||||
StandardWebSocketSessionAdapter session = new StandardWebSocketSessionAdapter();
|
||||
this.wsSessionInitializer.initialize(request, response, session);
|
||||
StandardEndpointAdapter endpoint = new StandardEndpointAdapter(handler, session);
|
||||
upgradeInternal(request, response, protocol, endpoint);
|
||||
}
|
||||
|
||||
protected abstract void upgradeInternal(ServerHttpRequest request, ServerHttpResponse response,
|
||||
String selectedProtocol, Endpoint endpoint) throws IOException;
|
||||
String selectedProtocol, Endpoint endpoint) throws IOException, HandshakeFailureException;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import org.springframework.http.server.ServletServerHttpResponse;
|
|||
import org.springframework.util.Assert;
|
||||
import org.springframework.websocket.WebSocketHandler;
|
||||
import org.springframework.websocket.adapter.JettyWebSocketListenerAdapter;
|
||||
import org.springframework.websocket.adapter.JettyWebSocketSessionAdapter;
|
||||
import org.springframework.websocket.server.HandshakeFailureException;
|
||||
import org.springframework.websocket.server.RequestUpgradeStrategy;
|
||||
|
||||
|
|
@ -53,11 +54,13 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy {
|
|||
|
||||
// FIXME when to call factory.cleanup();
|
||||
|
||||
private static final String HANDLER_PROVIDER_ATTR_NAME = JettyRequestUpgradeStrategy.class.getName()
|
||||
private static final String WEBSOCKET_LISTENER_ATTR_NAME = JettyRequestUpgradeStrategy.class.getName()
|
||||
+ ".HANDLER_PROVIDER";
|
||||
|
||||
private WebSocketServerFactory factory;
|
||||
|
||||
private final ServerWebSocketSessionInitializer wsSessionInitializer = new ServerWebSocketSessionInitializer();
|
||||
|
||||
|
||||
public JettyRequestUpgradeStrategy() {
|
||||
this.factory = new WebSocketServerFactory();
|
||||
|
|
@ -65,17 +68,14 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy {
|
|||
@Override
|
||||
public Object createWebSocket(UpgradeRequest request, UpgradeResponse response) {
|
||||
Assert.isInstanceOf(ServletWebSocketRequest.class, request);
|
||||
ServletWebSocketRequest servletRequest = (ServletWebSocketRequest) request;
|
||||
WebSocketHandler webSocketHandler =
|
||||
(WebSocketHandler) servletRequest.getServletAttributes().get(HANDLER_PROVIDER_ATTR_NAME);
|
||||
return new JettyWebSocketListenerAdapter(webSocketHandler);
|
||||
return ((ServletWebSocketRequest) request).getServletAttributes().get(WEBSOCKET_LISTENER_ATTR_NAME);
|
||||
}
|
||||
});
|
||||
try {
|
||||
this.factory.init();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
throw new IllegalStateException("Unable to initialize Jetty WebSocketServerFactory", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -95,17 +95,18 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy {
|
|||
Assert.isInstanceOf(ServletServerHttpResponse.class, response);
|
||||
HttpServletResponse servletResponse = ((ServletServerHttpResponse) response).getServletResponse();
|
||||
|
||||
upgrade(servletRequest, servletResponse, selectedProtocol, webSocketHandler);
|
||||
}
|
||||
if (!this.factory.isUpgradeRequest(servletRequest, servletResponse)) {
|
||||
// should never happen
|
||||
throw new HandshakeFailureException("Not a WebSocket request");
|
||||
}
|
||||
|
||||
private void upgrade(HttpServletRequest request, HttpServletResponse response,
|
||||
String selectedProtocol, final WebSocketHandler webSocketHandler) throws IOException {
|
||||
JettyWebSocketSessionAdapter session = new JettyWebSocketSessionAdapter();
|
||||
this.wsSessionInitializer.initialize(request, response, session);
|
||||
JettyWebSocketListenerAdapter listener = new JettyWebSocketListenerAdapter(webSocketHandler, session);
|
||||
|
||||
Assert.state(this.factory.isUpgradeRequest(request, response), "Expected websocket upgrade request");
|
||||
servletRequest.setAttribute(WEBSOCKET_LISTENER_ATTR_NAME, listener);
|
||||
|
||||
request.setAttribute(HANDLER_PROVIDER_ATTR_NAME, webSocketHandler);
|
||||
|
||||
if (!this.factory.acceptWebSocket(request, response)) {
|
||||
if (!this.factory.acceptWebSocket(servletRequest, servletResponse)) {
|
||||
// should never happen
|
||||
throw new HandshakeFailureException("WebSocket request not accepted by Jetty");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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.websocket.server.support;
|
||||
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.websocket.adapter.ConfigurableWebSocketSession;
|
||||
|
||||
|
||||
/**
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
*/
|
||||
public class ServerWebSocketSessionInitializer {
|
||||
|
||||
|
||||
public void initialize(ServerHttpRequest request, ServerHttpResponse response, ConfigurableWebSocketSession session) {
|
||||
session.setUri(request.getURI());
|
||||
session.setRemoteHostName(request.getRemoteHostName());
|
||||
session.setRemoteAddress(request.getRemoteAddress());
|
||||
session.setPrincipal(request.getPrincipal());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -41,7 +41,7 @@ public class LoggingWebSocketHandlerDecorator extends WebSocketHandlerDecorator
|
|||
@Override
|
||||
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Connection established, " + session + ", uri=" + session.getURI());
|
||||
logger.debug("Connection established, " + session + ", uri=" + session.getUri());
|
||||
}
|
||||
super.afterConnectionEstablished(session);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue