diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/EventDriver.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/EventDriver.java index d217937d478..5461df41885 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/EventDriver.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/events/EventDriver.java @@ -163,7 +163,14 @@ public abstract class EventDriver implements IncomingFrames { LOG.debug("openSession({})",session); this.session = session; - this.onConnect(); + try + { + this.onConnect(); + } + catch (Throwable t) + { + unhandled(t); + } } protected void terminateConnection(int statusCode, String rawreason) @@ -177,6 +184,7 @@ public abstract class EventDriver implements IncomingFrames private void unhandled(Throwable t) { LOG.warn("Unhandled Error (closing connection)",t); + onError(t); // Unhandled Error, close the connection. switch (policy.getBehavior()) diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/ServletWebSocketRequest.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/ServletWebSocketRequest.java index e274c963ee2..66140c80731 100644 --- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/ServletWebSocketRequest.java +++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/ServletWebSocketRequest.java @@ -237,17 +237,15 @@ public class ServletWebSocketRequest extends UpgradeRequest { if (protocol == null) { - return new String[] - { null }; + return new String[] {}; } protocol = protocol.trim(); if ((protocol == null) || (protocol.length() == 0)) { - return new String[] - { null }; + return new String[] {}; } String[] passed = protocol.split("\\s*,\\s*"); - String[] protocols = new String[passed.length + 1]; + String[] protocols = new String[passed.length]; System.arraycopy(passed,0,protocols,0,passed.length); return protocols; } diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketCloseTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketCloseTest.java index 00c001bcd2e..aafdea89466 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketCloseTest.java +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketCloseTest.java @@ -28,6 +28,7 @@ import java.util.concurrent.TimeUnit; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.log.StacklessLogging; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.StatusCode; import org.eclipse.jetty.websocket.api.UpgradeRequest; @@ -36,6 +37,7 @@ import org.eclipse.jetty.websocket.api.WebSocketAdapter; import org.eclipse.jetty.websocket.common.CloseInfo; import org.eclipse.jetty.websocket.common.OpCode; import org.eclipse.jetty.websocket.common.WebSocketFrame; +import org.eclipse.jetty.websocket.common.events.EventDriver; import org.eclipse.jetty.websocket.server.blockhead.BlockheadClient; import org.eclipse.jetty.websocket.server.helper.IncomingFramesCapture; import org.eclipse.jetty.websocket.server.helper.RFCSocket; @@ -52,28 +54,7 @@ import org.junit.Test; */ public class WebSocketCloseTest { - @SuppressWarnings("serial") - public static class CloseServlet extends WebSocketServlet implements WebSocketCreator - { - @Override - public void configure(WebSocketServletFactory factory) - { - factory.setCreator(this); - } - - @Override - public Object createWebSocket(UpgradeRequest req, UpgradeResponse resp) - { - if (req.hasSubProtocol("fastclose")) - { - fastcloseSocket = new FastCloseSocket(); - return fastcloseSocket; - } - return new RFCSocket(); - } - } - - public static class FastCloseSocket extends WebSocketAdapter + static class AbstractCloseSocket extends WebSocketAdapter { public CountDownLatch closeLatch = new CountDownLatch(1); public String closeReason = null; @@ -89,6 +70,48 @@ public class WebSocketCloseTest closeLatch.countDown(); } + @Override + public void onWebSocketError(Throwable cause) + { + errors.add(cause); + } + + } + + @SuppressWarnings("serial") + public static class CloseServlet extends WebSocketServlet implements WebSocketCreator + { + @Override + public void configure(WebSocketServletFactory factory) + { + factory.setCreator(this); + } + + @Override + public Object createWebSocket(UpgradeRequest req, UpgradeResponse resp) + { + if (req.hasSubProtocol("fastclose")) + { + closeSocket = new FastCloseSocket(); + return closeSocket; + } + + if (req.hasSubProtocol("fastfail")) + { + closeSocket = new FastFailSocket(); + return closeSocket; + } + return new RFCSocket(); + } + } + + /** + * On Connect, close socket + */ + public static class FastCloseSocket extends AbstractCloseSocket + { + private static final Logger LOG = Log.getLogger(WebSocketCloseTest.FastCloseSocket.class); + @Override public void onWebSocketConnect(Session sess) { @@ -102,6 +125,21 @@ public class WebSocketCloseTest e.printStackTrace(System.err); } } + } + + /** + * On Connect, throw unhandled exception + */ + public static class FastFailSocket extends AbstractCloseSocket + { + private static final Logger LOG = Log.getLogger(WebSocketCloseTest.FastFailSocket.class); + + @Override + public void onWebSocketConnect(Session sess) + { + LOG.debug("onWebSocketConnect({})",sess); + throw new RuntimeException("Intentional FastFail"); + } @Override public void onWebSocketError(Throwable cause) @@ -111,8 +149,9 @@ public class WebSocketCloseTest } private static final Logger LOG = Log.getLogger(WebSocketCloseTest.class); + private static SimpleServletServer server; - private static FastCloseSocket fastcloseSocket; + private static AbstractCloseSocket closeSocket; @BeforeClass public static void startServer() throws Exception @@ -150,8 +189,44 @@ public class WebSocketCloseTest client.write(close.asFrame()); // respond with close - Assert.assertThat("Fast Close Latch",fastcloseSocket.closeLatch.await(1,TimeUnit.SECONDS),is(true)); - Assert.assertThat("Fast Close.statusCode",fastcloseSocket.closeStatusCode,is(StatusCode.NORMAL)); + Assert.assertThat("Fast Close Latch",closeSocket.closeLatch.await(1,TimeUnit.SECONDS),is(true)); + Assert.assertThat("Fast Close.statusCode",closeSocket.closeStatusCode,is(StatusCode.NORMAL)); + } + finally + { + client.close(); + } + } + + /** + * Test fast fail (bug #410537) + */ + @Test + public void testFastFail() throws Exception + { + BlockheadClient client = new BlockheadClient(server.getServerUri()); + client.setProtocols("fastfail"); + client.setTimeout(TimeUnit.SECONDS,1); + try + { + try (StacklessLogging scope = new StacklessLogging(EventDriver.class)) + { + client.connect(); + client.sendStandardRequest(); + client.expectUpgradeResponse(); + + IncomingFramesCapture capture = client.readFrames(1,TimeUnit.SECONDS,1); + WebSocketFrame frame = capture.getFrames().poll(); + Assert.assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE)); + CloseInfo close = new CloseInfo(frame); + Assert.assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.SERVER_ERROR)); + + client.write(close.asFrame()); // respond with close + + Assert.assertThat("Fast Fail Latch",closeSocket.closeLatch.await(1,TimeUnit.SECONDS),is(true)); + Assert.assertThat("Fast Fail.statusCode",closeSocket.closeStatusCode,is(StatusCode.SERVER_ERROR)); + Assert.assertThat("Fast Fail.errors",closeSocket.errors.size(),is(1)); + } } finally {