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:
Rossen Stoyanchev 2013-05-02 13:47:18 -04:00
parent 166ca7a5a3
commit 2a7935a913
24 changed files with 510 additions and 190 deletions

View File

@ -16,6 +16,8 @@
package org.springframework.http.server; package org.springframework.http.server;
import java.security.Principal;
import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpRequest; import org.springframework.http.HttpRequest;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
@ -33,4 +35,21 @@ public interface ServerHttpRequest extends HttpRequest, HttpInputMessage {
*/ */
MultiValueMap<String, String> getQueryParams(); 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();
} }

View File

@ -26,6 +26,7 @@ import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.security.Principal;
import java.util.Arrays; import java.util.Arrays;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.HashMap; import java.util.HashMap;
@ -131,6 +132,21 @@ public class ServletServerHttpRequest implements ServerHttpRequest {
return this.headers; 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() { public Cookies getCookies() {
if (this.cookies == null) { if (this.cookies == null) {
this.cookies = new Cookies(); this.cookies = new Cookies();

View File

@ -18,6 +18,7 @@ package org.springframework.sockjs;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.security.Principal;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@ -25,7 +26,7 @@ import org.springframework.util.Assert;
import org.springframework.websocket.CloseStatus; import org.springframework.websocket.CloseStatus;
import org.springframework.websocket.TextMessage; import org.springframework.websocket.TextMessage;
import org.springframework.websocket.WebSocketHandler; 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 * @author Rossen Stoyanchev
* @since 4.0 * @since 4.0
*/ */
public abstract class AbstractSockJsSession implements WebSocketSession { public abstract class AbstractSockJsSession implements ConfigurableWebSocketSession {
protected final Log logger = LogFactory.getLog(getClass()); 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; private WebSocketHandler handler;
@ -57,24 +66,51 @@ public abstract class AbstractSockJsSession implements WebSocketSession {
public AbstractSockJsSession(String sessionId, WebSocketHandler webSocketHandler) { public AbstractSockJsSession(String sessionId, WebSocketHandler webSocketHandler) {
Assert.notNull(sessionId, "sessionId is required"); Assert.notNull(sessionId, "sessionId is required");
Assert.notNull(webSocketHandler, "webSocketHandler is required"); Assert.notNull(webSocketHandler, "webSocketHandler is required");
this.sessionId = sessionId; this.id = sessionId;
this.handler = webSocketHandler; this.handler = webSocketHandler;
} }
public String getId() { 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 @Override
public boolean isSecure() { public boolean isSecure() {
// TODO return "wss".equals(this.uri.getSchemeSpecificPart());
return false;
} }
@Override public String getRemoteHostName() {
public URI getURI() { return this.remoteHostName;
// TODO }
return null;
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() { public boolean isNew() {
@ -213,7 +249,7 @@ public abstract class AbstractSockJsSession implements WebSocketSession {
@Override @Override
public String toString() { public String toString() {
return "SockJS session id=" + this.sessionId; return "SockJS session id=" + this.id;
} }

View File

@ -17,7 +17,6 @@
package org.springframework.sockjs; package org.springframework.sockjs;
import org.springframework.websocket.WebSocketHandler; import org.springframework.websocket.WebSocketHandler;
import org.springframework.websocket.WebSocketSession;
/** /**
* A factory for creating a SockJS session. * A factory for creating a SockJS session.
@ -26,7 +25,7 @@ import org.springframework.websocket.WebSocketSession;
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 4.0 * @since 4.0
*/ */
public interface SockJsSessionFactory<S extends WebSocketSession>{ public interface SockJsSessionFactory {
/** /**
* Create a new SockJS session. * Create a new SockJS session.
@ -34,6 +33,6 @@ public interface SockJsSessionFactory<S extends WebSocketSession>{
* @param webSocketHandler the underlying {@link WebSocketHandler} * @param webSocketHandler the underlying {@link WebSocketHandler}
* @return a new non-null session * @return a new non-null session
*/ */
S createSession(String sessionId, WebSocketHandler webSocketHandler); AbstractSockJsSession createSession(String sessionId, WebSocketHandler webSocketHandler);
} }

View File

@ -51,7 +51,7 @@ public abstract class AbstractSockJsService implements SockJsService, SockJsConf
private static final int ONE_YEAR = 365 * 24 * 60 * 60; 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"; private String clientLibraryUrl = "https://d1fxtkz8shb9d2.cloudfront.net/sockjs-0.3.4.min.js";

View File

@ -68,7 +68,7 @@ public enum TransportType {
return this.httpMethod; return this.httpMethod;
} }
public boolean setsNoCacheHeader() { public boolean setsNoCache() {
return this.headerHints.contains("no_cache"); return this.headerHints.contains("no_cache");
} }
@ -76,7 +76,7 @@ public enum TransportType {
return this.headerHints.contains("cors"); return this.headerHints.contains("cors");
} }
public boolean setsJsessionIdCookie() { public boolean setsJsessionId() {
return this.headerHints.contains("jsessionid"); return this.headerHints.contains("jsessionid");
} }

View File

@ -54,6 +54,7 @@ import org.springframework.util.CollectionUtils;
import org.springframework.websocket.WebSocketHandler; import org.springframework.websocket.WebSocketHandler;
import org.springframework.websocket.server.DefaultHandshakeHandler; import org.springframework.websocket.server.DefaultHandshakeHandler;
import org.springframework.websocket.server.HandshakeHandler; 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 Map<String, AbstractSockJsSession> sessions = new ConcurrentHashMap<String, AbstractSockJsSession>();
private final ServerWebSocketSessionInitializer sessionInitializer = new ServerWebSocketSessionInitializer();
private ScheduledFuture sessionCleanupTask; private ScheduledFuture sessionCleanupTask;
@ -187,14 +190,15 @@ public class DefaultSockJsService extends AbstractSockJsService {
return; return;
} }
AbstractSockJsSession session = getSockJsSession(sessionId, webSocketHandler, transportHandler); AbstractSockJsSession session = getSockJsSession(sessionId, webSocketHandler,
transportHandler, request, response);
if (session != null) { if (session != null) {
if (transportType.setsNoCacheHeader()) { if (transportType.setsNoCache()) {
addNoCacheHeaders(response); addNoCacheHeaders(response);
} }
if (transportType.setsJsessionIdCookie() && isJsessionIdCookieRequired()) { if (transportType.setsJsessionId() && isJsessionIdCookieRequired()) {
Cookie cookie = request.getCookies().getCookie("JSESSIONID"); Cookie cookie = request.getCookies().getCookie("JSESSIONID");
String jsid = (cookie != null) ? cookie.getValue() : "dummy"; String jsid = (cookie != null) ? cookie.getValue() : "dummy";
// TODO: bypass use of Cookie object (causes Jetty to set Expires header) // 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); transportHandler.handleRequest(request, response, webSocketHandler, session);
} }
public AbstractSockJsSession getSockJsSession(String sessionId, protected AbstractSockJsSession getSockJsSession(String sessionId, WebSocketHandler handler,
WebSocketHandler webSocketHandler, TransportHandler transportHandler) { TransportHandler transportHandler, ServerHttpRequest request, ServerHttpResponse response) {
AbstractSockJsSession session = this.sessions.get(sessionId); AbstractSockJsSession session = this.sessions.get(sessionId);
if (session != null) { if (session != null) {
@ -218,7 +222,7 @@ public class DefaultSockJsService extends AbstractSockJsService {
} }
if (transportHandler instanceof SockJsSessionFactory) { if (transportHandler instanceof SockJsSessionFactory) {
SockJsSessionFactory<?> sessionFactory = (SockJsSessionFactory<?>) transportHandler; SockJsSessionFactory sessionFactory = (SockJsSessionFactory) transportHandler;
synchronized (this.sessions) { synchronized (this.sessions) {
session = this.sessions.get(sessionId); session = this.sessions.get(sessionId);
@ -229,7 +233,8 @@ public class DefaultSockJsService extends AbstractSockJsService {
scheduleSessionTask(); scheduleSessionTask();
} }
logger.debug("Creating new session with session id \"" + sessionId + "\""); 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); this.sessions.put(sessionId, session);
return session; return session;
} }

View File

@ -39,7 +39,7 @@ import org.springframework.websocket.WebSocketHandler;
* @since 4.0 * @since 4.0
*/ */
public abstract class AbstractHttpSendingTransportHandler public abstract class AbstractHttpSendingTransportHandler
implements ConfigurableTransportHandler, SockJsSessionFactory<AbstractSockJsSession> { implements ConfigurableTransportHandler, SockJsSessionFactory {
protected final Log logger = LogFactory.getLog(this.getClass()); protected final Log logger = LogFactory.getLog(this.getClass());

View File

@ -16,22 +16,16 @@
package org.springframework.sockjs.server.transport; package org.springframework.sockjs.server.transport;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.sockjs.server.AbstractServerSockJsSession;
import org.springframework.sockjs.server.SockJsConfiguration; import org.springframework.sockjs.server.SockJsConfiguration;
import org.springframework.sockjs.server.SockJsFrame;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.websocket.CloseStatus; import org.springframework.websocket.CloseStatus;
import org.springframework.websocket.TextMessage; import org.springframework.websocket.TextMessage;
import org.springframework.websocket.WebSocketHandler; import org.springframework.websocket.WebSocketHandler;
import org.springframework.websocket.WebSocketSession; import org.springframework.websocket.WebSocketSession;
import org.springframework.websocket.adapter.TextWebSocketHandlerAdapter; import org.springframework.websocket.adapter.TextWebSocketHandlerAdapter;
import com.fasterxml.jackson.databind.ObjectMapper;
/** /**
* A wrapper around a {@link WebSocketHandler} instance that parses and adds SockJS * 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 SockJsConfiguration sockJsConfig;
private final WebSocketHandler webSocketHandler; private WebSocketServerSockJsSession session;
private WebSocketServerSockJsSession sockJsSession;
private final AtomicInteger sessionCount = new AtomicInteger(0); 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(config, "sockJsConfig is required");
Assert.notNull(webSocketHandler, "webSocketHandler is required"); Assert.notNull(webSocketHandler, "webSocketHandler is required");
Assert.notNull(session, "session is required");
this.sockJsConfig = config; this.sockJsConfig = config;
this.webSocketHandler = webSocketHandler; this.session = session;
} }
protected SockJsConfiguration getSockJsConfig() { protected SockJsConfiguration getSockJsConfig() {
@ -76,100 +69,22 @@ public class SockJsWebSocketHandler extends TextWebSocketHandlerAdapter {
@Override @Override
public void afterConnectionEstablished(WebSocketSession wsSession) throws Exception { public void afterConnectionEstablished(WebSocketSession wsSession) throws Exception {
Assert.isTrue(this.sessionCount.compareAndSet(0, 1), "Unexpected connection"); Assert.isTrue(this.sessionCount.compareAndSet(0, 1), "Unexpected connection");
this.sockJsSession = new WebSocketServerSockJsSession(getSockJsSessionId(wsSession), getSockJsConfig()); this.session.initWebSocketSession(wsSession);
this.sockJsSession.initWebSocketSession(wsSession);
} }
@Override @Override
public void handleTextMessage(WebSocketSession wsSession, TextMessage message) throws Exception { public void handleTextMessage(WebSocketSession wsSession, TextMessage message) throws Exception {
this.sockJsSession.handleMessage(message, wsSession); this.session.handleMessage(message, wsSession);
} }
@Override @Override
public void afterConnectionClosed(WebSocketSession wsSession, CloseStatus status) throws Exception { public void afterConnectionClosed(WebSocketSession wsSession, CloseStatus status) throws Exception {
this.sockJsSession.delegateConnectionClosed(status); this.session.delegateConnectionClosed(status);
} }
@Override @Override
public void handleTransportError(WebSocketSession webSocketSession, Throwable exception) throws Exception { public void handleTransportError(WebSocketSession webSocketSession, Throwable exception) throws Exception {
this.sockJsSession.delegateError(exception); this.session.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);
}
} }
} }

View File

@ -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);
}
}

View File

@ -21,6 +21,7 @@ import java.io.IOException;
import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse; import org.springframework.http.server.ServerHttpResponse;
import org.springframework.sockjs.AbstractSockJsSession; import org.springframework.sockjs.AbstractSockJsSession;
import org.springframework.sockjs.SockJsSessionFactory;
import org.springframework.sockjs.server.ConfigurableTransportHandler; import org.springframework.sockjs.server.ConfigurableTransportHandler;
import org.springframework.sockjs.server.SockJsConfiguration; import org.springframework.sockjs.server.SockJsConfiguration;
import org.springframework.sockjs.server.TransportErrorException; import org.springframework.sockjs.server.TransportErrorException;
@ -39,7 +40,8 @@ import org.springframework.websocket.server.HandshakeHandler;
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 4.0 * @since 4.0
*/ */
public class WebSocketTransportHandler implements ConfigurableTransportHandler, HandshakeHandler { public class WebSocketTransportHandler implements ConfigurableTransportHandler,
HandshakeHandler, SockJsSessionFactory {
private final HandshakeHandler handshakeHandler; private final HandshakeHandler handshakeHandler;
@ -61,12 +63,18 @@ public class WebSocketTransportHandler implements ConfigurableTransportHandler,
this.sockJsConfig = sockJsConfig; this.sockJsConfig = sockJsConfig;
} }
@Override
public AbstractSockJsSession createSession(String sessionId, WebSocketHandler webSocketHandler) {
return new WebSocketServerSockJsSession(sessionId, this.sockJsConfig, webSocketHandler);
}
@Override @Override
public void handleRequest(ServerHttpRequest request, ServerHttpResponse response, public void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler webSocketHandler, AbstractSockJsSession session) throws TransportErrorException { WebSocketHandler webSocketHandler, AbstractSockJsSession session) throws TransportErrorException {
try { 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); this.handshakeHandler.doHandshake(request, response, sockJsWrapper);
} }
catch (Throwable t) { catch (Throwable t) {

View File

@ -17,7 +17,9 @@
package org.springframework.websocket; package org.springframework.websocket;
import java.io.IOException; import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URI; import java.net.URI;
import java.security.Principal;
/** /**
* Allows sending messages over a WebSocket connection as well as closing it. * Allows sending messages over a WebSocket connection as well as closing it.
@ -33,9 +35,9 @@ public interface WebSocketSession {
String getId(); 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. * Return whether the underlying socket is using a secure transport.
@ -43,9 +45,26 @@ public interface WebSocketSession {
boolean isSecure(); 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 * Send a WebSocket message either {@link TextMessage} or

View File

@ -1,5 +1,4 @@
/* /*
* Copyright 2002-2013 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 * @author Rossen Stoyanchev
* @since 4.0 * @since 4.0
*/ */
public abstract class AbstractWebSocketSesssionAdapter implements WebSocketSession { public abstract class AbstractWebSocketSesssionAdapter<T> implements ConfigurableWebSocketSession {
protected final Log logger = LogFactory.getLog(getClass()); protected final Log logger = LogFactory.getLog(getClass());
public abstract void initSession(T session);
@Override @Override
public final void sendMessage(WebSocketMessage message) throws IOException { public final void sendMessage(WebSocketMessage message) throws IOException {
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {

View File

@ -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);
}

View File

@ -25,7 +25,6 @@ import org.springframework.websocket.BinaryMessage;
import org.springframework.websocket.CloseStatus; import org.springframework.websocket.CloseStatus;
import org.springframework.websocket.TextMessage; import org.springframework.websocket.TextMessage;
import org.springframework.websocket.WebSocketHandler; import org.springframework.websocket.WebSocketHandler;
import org.springframework.websocket.WebSocketSession;
import org.springframework.websocket.support.ExceptionWebSocketHandlerDecorator; import org.springframework.websocket.support.ExceptionWebSocketHandlerDecorator;
/** /**
@ -40,18 +39,20 @@ public class JettyWebSocketListenerAdapter implements WebSocketListener {
private final WebSocketHandler webSocketHandler; 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(webSocketHandler, "webSocketHandler is required");
Assert.notNull(wsSession, "wsSession is required");
this.webSocketHandler = webSocketHandler; this.webSocketHandler = webSocketHandler;
this.wsSession = wsSession;
} }
@Override @Override
public void onWebSocketConnect(Session session) { public void onWebSocketConnect(Session session) {
this.wsSession = new JettyWebSocketSessionAdapter(session); this.wsSession.initSession(session);
try { try {
this.webSocketHandler.afterConnectionEstablished(this.wsSession); this.webSocketHandler.afterConnectionEstablished(this.wsSession);
} }

View File

@ -17,9 +17,12 @@
package org.springframework.websocket.adapter; package org.springframework.websocket.adapter;
import java.io.IOException; import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URI; import java.net.URI;
import java.security.Principal;
import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.Session;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.websocket.BinaryMessage; import org.springframework.websocket.BinaryMessage;
import org.springframework.websocket.CloseStatus; import org.springframework.websocket.CloseStatus;
@ -31,38 +34,78 @@ import org.springframework.websocket.WebSocketSession;
* Adapts Jetty's {@link Session} to Spring's {@link WebSocketSession}. * Adapts Jetty's {@link Session} to Spring's {@link WebSocketSession}.
* *
* @author Phillip Webb * @author Phillip Webb
* @author Rossen Stoyanchev
* @since 4.0 * @since 4.0
*/ */
public class JettyWebSocketSessionAdapter extends AbstractWebSocketSesssionAdapter { public class JettyWebSocketSessionAdapter
extends AbstractWebSocketSesssionAdapter<org.eclipse.jetty.websocket.api.Session> {
private Session 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; this.session = session;
} }
@Override @Override
public String getId() { public String getId() {
return ObjectUtils.getIdentityHexString(this.session); return ObjectUtils.getIdentityHexString(this.session);
} }
@Override
public boolean isOpen() {
return this.session.isOpen();
}
@Override @Override
public boolean isSecure() { public boolean isSecure() {
return this.session.isSecure(); return this.session.isSecure();
} }
@Override @Override
public URI getURI() { public URI getUri() {
return this.session.getUpgradeRequest().getRequestURI(); 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 @Override
protected void sendTextMessage(TextMessage message) throws IOException { protected void sendTextMessage(TextMessage message) throws IOException {
this.session.getRemote().sendString(message.getPayload()); this.session.getRemote().sendString(message.getPayload());

View File

@ -30,12 +30,11 @@ import org.springframework.websocket.BinaryMessage;
import org.springframework.websocket.CloseStatus; import org.springframework.websocket.CloseStatus;
import org.springframework.websocket.TextMessage; import org.springframework.websocket.TextMessage;
import org.springframework.websocket.WebSocketHandler; import org.springframework.websocket.WebSocketHandler;
import org.springframework.websocket.WebSocketSession;
import org.springframework.websocket.support.ExceptionWebSocketHandlerDecorator; 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 * @author Rossen Stoyanchev
* @since 4.0 * @since 4.0
@ -46,19 +45,22 @@ public class StandardEndpointAdapter extends Endpoint {
private final WebSocketHandler handler; private final WebSocketHandler handler;
private WebSocketSession wsSession; private final StandardWebSocketSessionAdapter wsSession;
public StandardEndpointAdapter(WebSocketHandler webSocketHandler) { public StandardEndpointAdapter(WebSocketHandler handler, StandardWebSocketSessionAdapter wsSession) {
Assert.notNull(webSocketHandler, "webSocketHandler is required"); Assert.notNull(handler, "handler is required");
this.handler = webSocketHandler; Assert.notNull(wsSession, "wsSession is required");
this.handler = handler;
this.wsSession = wsSession;
} }
@Override @Override
public void onOpen(final javax.websocket.Session session, EndpointConfig config) { public void onOpen(final javax.websocket.Session session, EndpointConfig config) {
this.wsSession = new StandardWebSocketSessionAdapter(session); this.wsSession.initSession(session);
try { try {
this.handler.afterConnectionEstablished(this.wsSession); this.handler.afterConnectionEstablished(this.wsSession);
} }

View File

@ -18,6 +18,7 @@ package org.springframework.websocket.adapter;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.security.Principal;
import javax.websocket.CloseReason; import javax.websocket.CloseReason;
import javax.websocket.CloseReason.CloseCodes; import javax.websocket.CloseReason.CloseCodes;
@ -35,35 +36,76 @@ import org.springframework.websocket.WebSocketSession;
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 4.0 * @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"); Assert.notNull(session, "session is required");
this.session = session; this.session = session;
} }
@Override @Override
public String getId() { public String getId() {
return this.session.getId(); return this.session.getId();
} }
@Override @Override
public boolean isOpen() { public URI getUri() {
return this.session.isOpen(); return this.uri;
} }
@Override
public void setUri(URI uri) {
this.uri = uri;
}
@Override @Override
public boolean isSecure() { public boolean isSecure() {
return this.session.isSecure(); return this.session.isSecure();
} }
@Override @Override
public URI getURI() { public Principal getPrincipal() {
return this.session.getRequestURI(); 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 @Override

View File

@ -28,12 +28,12 @@ import javax.websocket.ClientEndpointConfig.Configurator;
import javax.websocket.ContainerProvider; import javax.websocket.ContainerProvider;
import javax.websocket.Endpoint; import javax.websocket.Endpoint;
import javax.websocket.HandshakeResponse; import javax.websocket.HandshakeResponse;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer; import javax.websocket.WebSocketContainer;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder; import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.websocket.WebSocketHandler; import org.springframework.websocket.WebSocketHandler;
import org.springframework.websocket.WebSocketSession; import org.springframework.websocket.WebSocketSession;
@ -67,15 +67,26 @@ public class StandardWebSocketClient implements WebSocketClient {
public WebSocketSession doHandshake(WebSocketHandler webSocketHandler, String uriTemplate, Object... uriVariables) public WebSocketSession doHandshake(WebSocketHandler webSocketHandler, String uriTemplate, Object... uriVariables)
throws WebSocketConnectFailureException { throws WebSocketConnectFailureException {
URI uri = UriComponentsBuilder.fromUriString(uriTemplate).buildAndExpand(uriVariables).encode().toUri(); UriComponents uriComponents = UriComponentsBuilder.fromUriString(uriTemplate).buildAndExpand(uriVariables).encode();
return doHandshake(webSocketHandler, null, uri); return doHandshake(webSocketHandler, null, uriComponents);
} }
@Override @Override
public WebSocketSession doHandshake(WebSocketHandler webSocketHandler, public WebSocketSession doHandshake(WebSocketHandler webSocketHandler,
final HttpHeaders httpHeaders, URI uri) throws WebSocketConnectFailureException { 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(); ClientEndpointConfig.Builder configBuidler = ClientEndpointConfig.Builder.create();
if (httpHeaders != null) { if (httpHeaders != null) {
@ -109,8 +120,9 @@ public class StandardWebSocketClient implements WebSocketClient {
} }
try { try {
Session session = this.webSocketContainer.connectToServer(endpoint, configBuidler.build(), uri); // TODO: do not block
return new StandardWebSocketSessionAdapter(session); this.webSocketContainer.connectToServer(endpoint, configBuidler.build(), uri);
return session;
} }
catch (Exception e) { catch (Exception e) {
throw new WebSocketConnectFailureException("Failed to connect to " + uri, e); throw new WebSocketConnectFailureException("Failed to connect to " + uri, e);

View File

@ -17,13 +17,12 @@
package org.springframework.websocket.client.jetty; package org.springframework.websocket.client.jetty;
import java.net.URI; import java.net.URI;
import java.util.concurrent.Future;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.eclipse.jetty.websocket.api.Session;
import org.springframework.context.SmartLifecycle; import org.springframework.context.SmartLifecycle;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder; import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.websocket.WebSocketHandler; import org.springframework.websocket.WebSocketHandler;
import org.springframework.websocket.WebSocketSession; import org.springframework.websocket.WebSocketSession;
@ -126,23 +125,34 @@ public class JettyWebSocketClient implements WebSocketClient, SmartLifecycle {
public WebSocketSession doHandshake(WebSocketHandler webSocketHandler, String uriTemplate, Object... uriVariables) public WebSocketSession doHandshake(WebSocketHandler webSocketHandler, String uriTemplate, Object... uriVariables)
throws WebSocketConnectFailureException { throws WebSocketConnectFailureException {
URI uri = UriComponentsBuilder.fromUriString(uriTemplate).buildAndExpand(uriVariables).encode().toUri(); UriComponents uriComponents = UriComponentsBuilder.fromUriString(uriTemplate).buildAndExpand(uriVariables).encode();
return doHandshake(webSocketHandler, null, uri); return doHandshake(webSocketHandler, null, uriComponents);
} }
@Override @Override
public WebSocketSession doHandshake(WebSocketHandler webSocketHandler, HttpHeaders headers, URI uri) public WebSocketSession doHandshake(WebSocketHandler webSocketHandler, HttpHeaders headers, URI uri)
throws WebSocketConnectFailureException { throws WebSocketConnectFailureException {
return doHandshake(webSocketHandler, headers, UriComponentsBuilder.fromUri(uri).build());
}
public WebSocketSession doHandshake(WebSocketHandler webSocketHandler, HttpHeaders headers, UriComponents uriComponents)
throws WebSocketConnectFailureException {
// TODO: populate headers // 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 { try {
// block for now // TODO: do not block
Future<org.eclipse.jetty.websocket.api.Session> future = this.client.connect(listener, uri); this.client.connect(listener, uri).get();
Session session = future.get(); return session;
return new JettyWebSocketSessionAdapter(session);
} }
catch (Exception e) { catch (Exception e) {
throw new WebSocketConnectFailureException("Failed to connect to " + uri, e); throw new WebSocketConnectFailureException("Failed to connect to " + uri, e);

View File

@ -26,6 +26,8 @@ import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse; import org.springframework.http.server.ServerHttpResponse;
import org.springframework.websocket.WebSocketHandler; import org.springframework.websocket.WebSocketHandler;
import org.springframework.websocket.adapter.StandardEndpointAdapter; import org.springframework.websocket.adapter.StandardEndpointAdapter;
import org.springframework.websocket.adapter.StandardWebSocketSessionAdapter;
import org.springframework.websocket.server.HandshakeFailureException;
import org.springframework.websocket.server.RequestUpgradeStrategy; import org.springframework.websocket.server.RequestUpgradeStrategy;
/** /**
@ -39,15 +41,20 @@ public abstract class AbstractEndpointUpgradeStrategy implements RequestUpgradeS
protected final Log logger = LogFactory.getLog(getClass()); protected final Log logger = LogFactory.getLog(getClass());
private final ServerWebSocketSessionInitializer wsSessionInitializer = new ServerWebSocketSessionInitializer();
@Override @Override
public void upgrade(ServerHttpRequest request, ServerHttpResponse response, 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, protected abstract void upgradeInternal(ServerHttpRequest request, ServerHttpResponse response,
String selectedProtocol, Endpoint endpoint) throws IOException; String selectedProtocol, Endpoint endpoint) throws IOException, HandshakeFailureException;
} }

View File

@ -34,6 +34,7 @@ import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.websocket.WebSocketHandler; import org.springframework.websocket.WebSocketHandler;
import org.springframework.websocket.adapter.JettyWebSocketListenerAdapter; import org.springframework.websocket.adapter.JettyWebSocketListenerAdapter;
import org.springframework.websocket.adapter.JettyWebSocketSessionAdapter;
import org.springframework.websocket.server.HandshakeFailureException; import org.springframework.websocket.server.HandshakeFailureException;
import org.springframework.websocket.server.RequestUpgradeStrategy; import org.springframework.websocket.server.RequestUpgradeStrategy;
@ -53,11 +54,13 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy {
// FIXME when to call factory.cleanup(); // 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"; + ".HANDLER_PROVIDER";
private WebSocketServerFactory factory; private WebSocketServerFactory factory;
private final ServerWebSocketSessionInitializer wsSessionInitializer = new ServerWebSocketSessionInitializer();
public JettyRequestUpgradeStrategy() { public JettyRequestUpgradeStrategy() {
this.factory = new WebSocketServerFactory(); this.factory = new WebSocketServerFactory();
@ -65,17 +68,14 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy {
@Override @Override
public Object createWebSocket(UpgradeRequest request, UpgradeResponse response) { public Object createWebSocket(UpgradeRequest request, UpgradeResponse response) {
Assert.isInstanceOf(ServletWebSocketRequest.class, request); Assert.isInstanceOf(ServletWebSocketRequest.class, request);
ServletWebSocketRequest servletRequest = (ServletWebSocketRequest) request; return ((ServletWebSocketRequest) request).getServletAttributes().get(WEBSOCKET_LISTENER_ATTR_NAME);
WebSocketHandler webSocketHandler =
(WebSocketHandler) servletRequest.getServletAttributes().get(HANDLER_PROVIDER_ATTR_NAME);
return new JettyWebSocketListenerAdapter(webSocketHandler);
} }
}); });
try { try {
this.factory.init(); this.factory.init();
} }
catch (Exception ex) { 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); Assert.isInstanceOf(ServletServerHttpResponse.class, response);
HttpServletResponse servletResponse = ((ServletServerHttpResponse) response).getServletResponse(); 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, JettyWebSocketSessionAdapter session = new JettyWebSocketSessionAdapter();
String selectedProtocol, final WebSocketHandler webSocketHandler) throws IOException { 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(servletRequest, servletResponse)) {
if (!this.factory.acceptWebSocket(request, response)) {
// should never happen // should never happen
throw new HandshakeFailureException("WebSocket request not accepted by Jetty"); throw new HandshakeFailureException("WebSocket request not accepted by Jetty");
} }

View File

@ -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());
}
}

View File

@ -41,7 +41,7 @@ public class LoggingWebSocketHandlerDecorator extends WebSocketHandlerDecorator
@Override @Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception { public void afterConnectionEstablished(WebSocketSession session) throws Exception {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Connection established, " + session + ", uri=" + session.getURI()); logger.debug("Connection established, " + session + ", uri=" + session.getUri());
} }
super.afterConnectionEstablished(session); super.afterConnectionEstablished(session);
} }