Remove IOException from HandshakeHandler

This commit is contained in:
Rossen Stoyanchev 2013-08-17 09:37:20 -04:00
parent 2397b21096
commit 13ce20b1ca
11 changed files with 110 additions and 84 deletions

View File

@ -132,50 +132,55 @@ public class DefaultHandshakeHandler implements HandshakeHandler {
@Override
public final boolean doHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler webSocketHandler, Map<String, Object> attributes) throws IOException, HandshakeFailureException {
WebSocketHandler wsHandler, Map<String, Object> attributes) throws HandshakeFailureException {
if (logger.isDebugEnabled()) {
logger.debug("Initiating handshake for " + request.getURI() + ", headers=" + request.getHeaders());
}
if (!HttpMethod.GET.equals(request.getMethod())) {
response.setStatusCode(HttpStatus.METHOD_NOT_ALLOWED);
response.getHeaders().setAllow(Collections.singleton(HttpMethod.GET));
logger.debug("Only HTTP GET is allowed, current method is " + request.getMethod());
return false;
try {
if (!HttpMethod.GET.equals(request.getMethod())) {
response.setStatusCode(HttpStatus.METHOD_NOT_ALLOWED);
response.getHeaders().setAllow(Collections.singleton(HttpMethod.GET));
logger.debug("Only HTTP GET is allowed, current method is " + request.getMethod());
return false;
}
if (!"WebSocket".equalsIgnoreCase(request.getHeaders().getUpgrade())) {
handleInvalidUpgradeHeader(request, response);
return false;
}
if (!request.getHeaders().getConnection().contains("Upgrade") &&
!request.getHeaders().getConnection().contains("upgrade")) {
handleInvalidConnectHeader(request, response);
return false;
}
if (!isWebSocketVersionSupported(request)) {
handleWebSocketVersionNotSupported(request, response);
return false;
}
if (!isValidOrigin(request)) {
response.setStatusCode(HttpStatus.FORBIDDEN);
return false;
}
String wsKey = request.getHeaders().getSecWebSocketKey();
if (wsKey == null) {
logger.debug("Missing \"Sec-WebSocket-Key\" header");
response.setStatusCode(HttpStatus.BAD_REQUEST);
return false;
}
}
if (!"WebSocket".equalsIgnoreCase(request.getHeaders().getUpgrade())) {
handleInvalidUpgradeHeader(request, response);
return false;
}
if (!request.getHeaders().getConnection().contains("Upgrade") &&
!request.getHeaders().getConnection().contains("upgrade")) {
handleInvalidConnectHeader(request, response);
return false;
}
if (!isWebSocketVersionSupported(request)) {
handleWebSocketVersionNotSupported(request, response);
return false;
}
if (!isValidOrigin(request)) {
response.setStatusCode(HttpStatus.FORBIDDEN);
return false;
}
String wsKey = request.getHeaders().getSecWebSocketKey();
if (wsKey == null) {
logger.debug("Missing \"Sec-WebSocket-Key\" header");
response.setStatusCode(HttpStatus.BAD_REQUEST);
return false;
catch (IOException ex) {
throw new HandshakeFailureException(
"Response update failed during upgrade to WebSocket, uri=" + request.getURI(), ex);
}
String selectedProtocol = selectProtocol(request.getHeaders().getSecWebSocketProtocol());
// TODO: select extensions
String subProtocol = selectProtocol(request.getHeaders().getSecWebSocketProtocol());
if (logger.isDebugEnabled()) {
logger.debug("Upgrading request");
logger.debug("Upgrading request, sub-protocol=" + subProtocol);
}
this.requestUpgradeStrategy.upgrade(request, response, selectedProtocol, webSocketHandler, attributes);
this.requestUpgradeStrategy.upgrade(request, response, subProtocol, wsHandler, attributes);
return true;
}

View File

@ -34,12 +34,19 @@ import org.springframework.core.NestedRuntimeException;
@SuppressWarnings("serial")
public class HandshakeFailureException extends NestedRuntimeException {
public HandshakeFailureException(String msg, Throwable cause) {
super(msg, cause);
/**
* Constructor with message and root cause.
*/
public HandshakeFailureException(String message, Throwable cause) {
super(message, cause);
}
public HandshakeFailureException(String msg) {
super(msg);
/**
* Constructor without a message.
*/
public HandshakeFailureException(String message) {
super(message);
}
}

View File

@ -16,7 +16,6 @@
package org.springframework.web.socket.server;
import java.io.IOException;
import java.util.Map;
import org.springframework.http.server.ServerHttpRequest;
@ -36,6 +35,7 @@ import org.springframework.web.socket.support.PerConnectionWebSocketHandler;
*/
public interface HandshakeHandler {
/**
* Initiate the handshake.
*
@ -51,14 +51,11 @@ public interface HandshakeHandler {
* response status, headers, and body will have been updated to reflect the
* result of the negotiation
*
* @throws IOException thrown when accessing or setting the response
*
* @throws HandshakeFailureException thrown when handshake processing failed to
* complete due to an internal, unrecoverable error, i.e. a server error as
* opposed to a failure to successfully negotiate the requirements of the
* handshake request.
* opposed to a failure to successfully negotiate the handshake.
*/
boolean doHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Map<String, Object> attributes) throws IOException, HandshakeFailureException;
Map<String, Object> attributes) throws HandshakeFailureException;
}

View File

@ -16,7 +16,6 @@
package org.springframework.web.socket.server;
import java.io.IOException;
import java.util.Map;
import org.springframework.http.server.ServerHttpRequest;
@ -53,6 +52,6 @@ public interface RequestUpgradeStrategy {
* handshake request.
*/
void upgrade(ServerHttpRequest request, ServerHttpResponse response, String acceptedProtocol,
WebSocketHandler wsHandler, Map<String, Object> attributes) throws IOException, HandshakeFailureException;
WebSocketHandler wsHandler, Map<String, Object> attributes) throws HandshakeFailureException;
}

View File

@ -16,7 +16,6 @@
package org.springframework.web.socket.server.support;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Map;
@ -34,7 +33,8 @@ import org.springframework.web.socket.server.HandshakeFailureException;
import org.springframework.web.socket.server.RequestUpgradeStrategy;
/**
* A {@link RequestUpgradeStrategy} for containers that support standard Java WebSocket.
* A base class for {@link RequestUpgradeStrategy} implementations that build on the
* standard WebSocket API for Java.
*
* @author Rossen Stoyanchev
* @since 4.0
@ -46,20 +46,20 @@ public abstract class AbstractStandardUpgradeStrategy implements RequestUpgradeS
@Override
public void upgrade(ServerHttpRequest request, ServerHttpResponse response, String acceptedProtocol,
WebSocketHandler wsHandler, Map<String, Object> attributes)
throws IOException, HandshakeFailureException {
WebSocketHandler wsHandler, Map<String, Object> attributes) throws HandshakeFailureException {
HttpHeaders headers = request.getHeaders();
InetSocketAddress localAddr = request.getLocalAddress();
InetSocketAddress remoteAddr = request.getRemoteAddress();
StandardWebSocketSession wsSession = new StandardWebSocketSession(headers, attributes, localAddr, remoteAddr);
StandardWebSocketHandlerAdapter endpoint = new StandardWebSocketHandlerAdapter(wsHandler, wsSession);
StandardWebSocketSession session = new StandardWebSocketSession(headers, attributes, localAddr, remoteAddr);
StandardWebSocketHandlerAdapter endpoint = new StandardWebSocketHandlerAdapter(wsHandler, session);
upgradeInternal(request, response, acceptedProtocol, endpoint);
}
protected abstract void upgradeInternal(ServerHttpRequest request, ServerHttpResponse response,
String selectedProtocol, Endpoint endpoint) throws IOException, HandshakeFailureException;
String selectedProtocol, Endpoint endpoint) throws HandshakeFailureException;
}

View File

@ -70,7 +70,7 @@ public class GlassFishRequestUpgradeStrategy extends AbstractStandardUpgradeStra
@Override
public void upgradeInternal(ServerHttpRequest request, ServerHttpResponse response,
String selectedProtocol, Endpoint endpoint) throws IOException, HandshakeFailureException {
String selectedProtocol, Endpoint endpoint) throws HandshakeFailureException {
Assert.isTrue(request instanceof ServletServerHttpRequest);
HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
@ -85,12 +85,16 @@ public class GlassFishRequestUpgradeStrategy extends AbstractStandardUpgradeStra
webSocketEngine.register(webSocketApplication);
}
catch (DeploymentException ex) {
throw new HandshakeFailureException("Failed to deploy endpoint in GlassFish", ex);
throw new HandshakeFailureException("Failed to configure endpoint in GlassFish", ex);
}
try {
performUpgrade(servletRequest, servletResponse, request.getHeaders(), webSocketApplication);
}
catch (IOException ex) {
throw new HandshakeFailureException(
"Response update failed during upgrade to WebSocket, uri=" + request.getURI(), ex);
}
finally {
webSocketEngine.unregister(webSocketApplication);
}

View File

@ -44,30 +44,26 @@ import org.springframework.web.socket.server.RequestUpgradeStrategy;
* {@code org.eclipse.jetty.websocket.server.WebSocketHandler} class.
*
* @author Phillip Webb
* @author Rossen Stoyanchev
* @since 4.0
*/
public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy {
// FIXME jetty has options, timeouts etc. Do we need a common abstraction
// FIXME need a way for someone to plug their own RequestUpgradeStrategy or override
// Jetty settings
// FIXME when to call factory.cleanup();
private static final String WEBSOCKET_LISTENER_ATTR_NAME = JettyRequestUpgradeStrategy.class.getName()
+ ".HANDLER_PROVIDER";
private static final String WS_HANDLER_ATTR_NAME = JettyRequestUpgradeStrategy.class.getName() + ".WS_LISTENER";
private WebSocketServerFactory factory;
/**
* Default constructor.
*/
public JettyRequestUpgradeStrategy() {
this.factory = new WebSocketServerFactory();
this.factory.setCreator(new WebSocketCreator() {
@Override
public Object createWebSocket(UpgradeRequest request, UpgradeResponse response) {
Assert.isInstanceOf(ServletUpgradeRequest.class, request);
return ((ServletUpgradeRequest) request).getServletAttributes().get(WEBSOCKET_LISTENER_ATTR_NAME);
return ((ServletUpgradeRequest) request).getServletAttributes().get(WS_HANDLER_ATTR_NAME);
}
});
try {
@ -86,7 +82,7 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy {
@Override
public void upgrade(ServerHttpRequest request, ServerHttpResponse response,
String protocol, WebSocketHandler wsHandler, Map<String, Object> attrs) throws IOException {
String protocol, WebSocketHandler wsHandler, Map<String, Object> attrs) throws HandshakeFailureException {
Assert.isInstanceOf(ServletServerHttpRequest.class, request);
HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
@ -94,19 +90,21 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy {
Assert.isInstanceOf(ServletServerHttpResponse.class, response);
HttpServletResponse servletResponse = ((ServletServerHttpResponse) response).getServletResponse();
if (!this.factory.isUpgradeRequest(servletRequest, servletResponse)) {
// should never happen
throw new HandshakeFailureException("Not a WebSocket request");
Assert.isTrue(this.factory.isUpgradeRequest(servletRequest, servletResponse), "Not a WebSocket handshake");
JettyWebSocketSession session = new JettyWebSocketSession(request.getPrincipal(), attrs);
JettyWebSocketHandlerAdapter handlerAdapter = new JettyWebSocketHandlerAdapter(wsHandler, session);
try {
servletRequest.setAttribute(WS_HANDLER_ATTR_NAME, handlerAdapter);
this.factory.acceptWebSocket(servletRequest, servletResponse);
}
JettyWebSocketSession wsSession = new JettyWebSocketSession(request.getPrincipal(), attrs);
JettyWebSocketHandlerAdapter wsListener = new JettyWebSocketHandlerAdapter(wsHandler, wsSession);
servletRequest.setAttribute(WEBSOCKET_LISTENER_ATTR_NAME, wsListener);
if (!this.factory.acceptWebSocket(servletRequest, servletResponse)) {
// should not happen
throw new HandshakeFailureException("WebSocket request not accepted by Jetty");
catch (IOException ex) {
throw new HandshakeFailureException(
"Response update failed during upgrade to WebSocket, uri=" + request.getURI(), ex);
}
finally {
servletRequest.removeAttribute(WS_HANDLER_ATTR_NAME);
}
}

View File

@ -16,11 +16,13 @@
package org.springframework.web.socket.server.support;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.websocket.Endpoint;
@ -67,8 +69,13 @@ public class TomcatRequestUpgradeStrategy extends AbstractStandardUpgradeStrateg
try {
getContainer(servletRequest).doUpgrade(servletRequest, servletResponse, endpointConfig, pathParams);
}
catch (Exception ex) {
throw new HandshakeFailureException("Failed to upgrade HttpServletRequest", ex);
catch (ServletException ex) {
throw new HandshakeFailureException(
"Servlet request failed to upgrade to WebSocket, uri=" + request.getURI(), ex);
}
catch (IOException ex) {
throw new HandshakeFailureException(
"Response update failed during upgrade to WebSocket, uri=" + request.getURI(), ex);
}
}

View File

@ -30,6 +30,10 @@ public class SockJsException extends NestedRuntimeException {
private final String sessionId;
public SockJsException(String message, Throwable cause) {
this(message, null, cause);
}
public SockJsException(String message, String sessionId, Throwable cause) {
super(message, cause);
this.sessionId = sessionId;

View File

@ -74,10 +74,15 @@ public class SockJsHttpRequestHandler implements HttpRequestHandler {
public void handleRequest(HttpServletRequest servletRequest, HttpServletResponse servletResponse)
throws ServletException, IOException {
ServerHttpRequest serverRequest = new ServletServerHttpRequest(servletRequest);
ServerHttpResponse serverResponse = new ServletServerHttpResponse(servletResponse);
ServerHttpRequest request = new ServletServerHttpRequest(servletRequest);
ServerHttpResponse response = new ServletServerHttpResponse(servletResponse);
this.sockJsService.handleRequest(serverRequest, serverResponse, this.webSocketHandler);
try {
this.sockJsService.handleRequest(request, response, this.webSocketHandler);
}
catch (Throwable t) {
throw new SockJsException("Uncaught failure in SockJS request, uri=" + request.getURI(), t);
}
}
}

View File

@ -16,7 +16,6 @@
package org.springframework.web.socket.sockjs.transport.handler;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
@ -26,6 +25,7 @@ import org.springframework.util.Assert;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.server.HandshakeFailureException;
import org.springframework.web.socket.server.HandshakeHandler;
import org.springframework.web.socket.sockjs.SockJsException;
import org.springframework.web.socket.sockjs.SockJsTransportFailureException;
@ -35,7 +35,7 @@ import org.springframework.web.socket.sockjs.transport.session.AbstractSockJsSes
import org.springframework.web.socket.sockjs.transport.session.WebSocketServerSockJsSession;
/**
* A WebSocket {@link TransportHandler}. Uses {@link SockJsWebSocketHandler} and
* WebSocket-based {@link TransportHandler}. Uses {@link SockJsWebSocketHandler} and
* {@link WebSocketServerSockJsSession} to add SockJS processing.
*
* <p>Also implements {@link HandshakeHandler} to support raw WebSocket communication at
@ -87,7 +87,7 @@ public class WebSocketTransportHandler extends TransportHandlerSupport
@Override
public boolean doHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler handler, Map<String, Object> attributes) throws IOException {
WebSocketHandler handler, Map<String, Object> attributes) throws HandshakeFailureException {
return this.handshakeHandler.doHandshake(request, response, handler, attributes);
}