diff options
Diffstat (limited to 'jetty-websocket')
10 files changed, 707 insertions, 206 deletions
diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/api/StatusCode.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/api/StatusCode.java index 9c3c98cbe6..596dcbb643 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/api/StatusCode.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/api/StatusCode.java @@ -114,7 +114,7 @@ public class StatusCode public final static int SERVER_ERROR = 1011; /** - * 1012 indicates that the service is restarted. a client may reconnect, and if it choses to do, should reconnect using a randomized delay of 5 - 30s. + * 1012 indicates that the service is restarted. a client may reconnect, and if it chooses to do, should reconnect using a randomized delay of 5 - 30s. * <p> * See <a href="https://www.ietf.org/mail-archive/web/hybi/current/msg09649.html">[hybi] Additional WebSocket Close Error Codes</a> */ diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/io/AbstractWebSocketConnection.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/io/AbstractWebSocketConnection.java index fde5bcc1d6..3944be0eb3 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/io/AbstractWebSocketConnection.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/io/AbstractWebSocketConnection.java @@ -23,12 +23,11 @@ import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; import org.eclipse.jetty.io.AbstractConnection; +import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.FutureCallback; @@ -62,7 +61,6 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp private WebSocketSession session; private List<ExtensionConfig> extensions; private boolean flushing; - private AtomicLong writes; public AbstractWebSocketConnection(EndPoint endp, Executor executor, ScheduledExecutorService scheduler, WebSocketPolicy policy, ByteBufferPool bufferPool) { @@ -74,17 +72,9 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp this.scheduler = scheduler; this.extensions = new ArrayList<>(); this.queue = new FrameQueue(); - this.writes = new AtomicLong(0); } @Override - public void onOpen() - { - super.onOpen(); - fillInterested(); - } - - @Override public void close() throws IOException { terminateConnection(StatusCode.NORMAL,null); @@ -136,6 +126,13 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp frameBytes = queue.pop(); + if (!isOpen()) + { + // No longer have an open connection, drop the frame. + queue.clear(); + return; + } + buffer = frameBytes.getByteBuffer(); if (buffer == null) @@ -236,6 +233,13 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp } } + @Override + public void onOpen() + { + super.onOpen(); + fillInterested(); + } + /** * Enqueue internal frame from {@link OutgoingFrames} stack for eventual write out on the physical connection. */ @@ -261,13 +265,17 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp } scheduleTimeout(bytes); - if (frame.getOpCode() == OpCode.PING) - { - queue.prepend(bytes); - } - else + + if (isOpen()) { - queue.append(bytes); + if (frame.getOpCode() == OpCode.PING) + { + queue.prepend(bytes); + } + else + { + queue.append(bytes); + } } } flush(); diff --git a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/protocol/WebSocketFrameTest.java b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/protocol/WebSocketFrameTest.java index 19d46f4599..6bc817774e 100644 --- a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/protocol/WebSocketFrameTest.java +++ b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/protocol/WebSocketFrameTest.java @@ -74,7 +74,7 @@ public class WebSocketFrameTest @Test public void testStrictValidClose() { - CloseInfo close = new CloseInfo(StatusCode.NORMAL,null); + CloseInfo close = new CloseInfo(StatusCode.NORMAL); ByteBuffer actual = strictGenerator.generate(close.asFrame()); ByteBuffer expected = ByteBuffer.allocate(4); expected.put((byte)0x88); diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/AllTests.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/AllTests.java index ffd8e57e1a..e65f7e8eff 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/AllTests.java +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/AllTests.java @@ -13,8 +13,10 @@ import org.junit.runners.Suite; TestABCase5.class, TestABCase6.class, TestABCase6_GoodUTF.class, - TestABCase6_BadUTF.class, - TestABCase7_9.class + TestABCase6_BadUTF.class, + TestABCase7.class, + TestABCase7_GoodStatusCodes.class, + TestABCase7_BadStatusCodes.class, }) // @formatter:on public class AllTests diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/Fuzzer.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/Fuzzer.java index fcf9a7f516..f6a7bbceb0 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/Fuzzer.java +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/Fuzzer.java @@ -127,6 +127,12 @@ public class Fuzzer expect(Collections.singletonList(expect)); } + public void expectNoMoreFrames() + { + // TODO Should test for no more frames. success if connection closed. + + } + public SendMode getSendMode() { return sendMode; diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6_GoodUTF.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6_GoodUTF.java index 14685fdd1d..2d8d5ab16f 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6_GoodUTF.java +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6_GoodUTF.java @@ -99,6 +99,7 @@ public class TestABCase6_GoodUTF extends AbstractABCase return data; } + private final byte[] msg; public TestABCase6_GoodUTF(String testId, String hexMsg) diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7.java new file mode 100644 index 0000000000..29f25be5be --- /dev/null +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7.java @@ -0,0 +1,416 @@ +package org.eclipse.jetty.websocket.server.ab; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.jetty.util.BufferUtil; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.websocket.api.StatusCode; +import org.eclipse.jetty.websocket.protocol.CloseInfo; +import org.eclipse.jetty.websocket.protocol.OpCode; +import org.eclipse.jetty.websocket.protocol.WebSocketFrame; +import org.eclipse.jetty.websocket.server.helper.Hex; +import org.junit.Ignore; +import org.junit.Test; + +/** + * Test of Close Handling + */ +public class TestABCase7 extends AbstractABCase +{ + /** + * Basic message then close frame, normal behavior + */ + @Test + public void testCase7_1_1() throws Exception + { + List<WebSocketFrame> send = new ArrayList<>(); + send.add(WebSocketFrame.text("Hello World")); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List<WebSocketFrame> expect = new ArrayList<>(); + expect.add(WebSocketFrame.text("Hello World")); + expect.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); + } + finally + { + fuzzer.close(); + } + } + + /** + * Close frame, then another close frame (send frame ignored) + */ + @Test + public void testCase7_1_2() throws Exception + { + List<WebSocketFrame> send = new ArrayList<>(); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + send.add(new CloseInfo().asFrame()); + + List<WebSocketFrame> expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); + fuzzer.expectNoMoreFrames(); + } + finally + { + fuzzer.close(); + } + } + + /** + * Close frame, then ping frame (no pong received) + */ + @Test + public void testCase7_1_3() throws Exception + { + List<WebSocketFrame> send = new ArrayList<>(); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + send.add(WebSocketFrame.ping().setPayload("out of band ping")); + + List<WebSocketFrame> expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); + fuzzer.expectNoMoreFrames(); + } + finally + { + fuzzer.close(); + } + } + + /** + * Close frame, then ping frame (no pong received) + */ + @Test + public void testCase7_1_4() throws Exception + { + List<WebSocketFrame> send = new ArrayList<>(); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + send.add(WebSocketFrame.text("out of band text")); + + List<WebSocketFrame> expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); + fuzzer.expectNoMoreFrames(); + } + finally + { + fuzzer.close(); + } + } + + /** + * Text fin=false, close, then continuation fin=true + */ + @Test + public void testCase7_1_5() throws Exception + { + List<WebSocketFrame> send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload("an").setFin(false)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload("ticipation").setFin(true)); + + List<WebSocketFrame> expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); + fuzzer.expectNoMoreFrames(); + } + finally + { + fuzzer.close(); + } + } + + /** + * 256k msg, then close, then ping + */ + @Test + @Ignore("Problematic") + public void testCase7_1_6() throws Exception + { + byte msg[] = new byte[256 * 1024]; + Arrays.fill(msg,(byte)'*'); + + List<WebSocketFrame> send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload(msg)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + send.add(new WebSocketFrame(OpCode.PING).setPayload("out of band")); + + List<WebSocketFrame> expect = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload(msg)); + expect.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); + fuzzer.expectNoMoreFrames(); + } + finally + { + fuzzer.close(); + } + } + + /** + * close with no payload (payload length 0) + */ + @Test + public void testCase7_3_1() throws Exception + { + List<WebSocketFrame> send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.CLOSE)); + + List<WebSocketFrame> expect = new ArrayList<>(); + expect.add(new WebSocketFrame(OpCode.CLOSE)); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); + fuzzer.expectNoMoreFrames(); + } + finally + { + fuzzer.close(); + } + } + + /** + * close with invalid payload (payload length 1) + */ + @Test + public void testCase7_3_2() throws Exception + { + byte payload[] = new byte[] + { 0x00 }; + + List<WebSocketFrame> send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.CLOSE).setPayload(payload)); + + List<WebSocketFrame> expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); + fuzzer.expectNoMoreFrames(); + } + finally + { + fuzzer.close(); + } + } + + /** + * close with valid payload (payload length 2) + */ + @Test + public void testCase7_3_3() throws Exception + { + List<WebSocketFrame> send = new ArrayList<>(); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List<WebSocketFrame> expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); + fuzzer.expectNoMoreFrames(); + } + finally + { + fuzzer.close(); + } + } + + /** + * close with valid payload (with reason) + */ + @Test + public void testCase7_3_4() throws Exception + { + List<WebSocketFrame> send = new ArrayList<>(); + send.add(new CloseInfo(StatusCode.NORMAL,"Hic").asFrame()); + + List<WebSocketFrame> expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.NORMAL,"Hic").asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); + fuzzer.expectNoMoreFrames(); + } + finally + { + fuzzer.close(); + } + } + + /** + * close with valid payload (with 123 byte reason) + */ + @Test + public void testCase7_3_5() throws Exception + { + byte utf[] = new byte[123]; + Arrays.fill(utf,(byte)'!'); + String reason = StringUtil.toUTF8String(utf,0,utf.length); + + List<WebSocketFrame> send = new ArrayList<>(); + send.add(new CloseInfo(StatusCode.NORMAL,reason).asFrame()); + + List<WebSocketFrame> expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.NORMAL,reason).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); + fuzzer.expectNoMoreFrames(); + } + finally + { + fuzzer.close(); + } + } + + /** + * close with invalid payload (124 byte reason) (exceeds total allowed control frame payload bytes) + */ + @Test + public void testCase7_3_6() throws Exception + { + ByteBuffer payload = ByteBuffer.allocate(256); + BufferUtil.clearToFill(payload); + payload.put((byte)0xE8); + payload.put((byte)0x03); + byte reason[] = new byte[124]; // too big + Arrays.fill(reason,(byte)'!'); + payload.put(reason); + BufferUtil.flipToFlush(payload,0); + + List<WebSocketFrame> send = new ArrayList<>(); + WebSocketFrame close = new WebSocketFrame(); + close.setPayload(payload); + close.setOpCode(OpCode.CLOSE); // set opcode after payload (to prevent early bad payload detection) + send.add(close); + + List<WebSocketFrame> expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); + fuzzer.expectNoMoreFrames(); + } + finally + { + fuzzer.close(); + } + } + + /** + * close with invalid payload (124 byte reason) (exceeds total allowed control frame payload bytes) + */ + @Test + public void testCase7_5_1() throws Exception + { + ByteBuffer payload = ByteBuffer.allocate(256); + BufferUtil.clearToFill(payload); + payload.put((byte)0xE8); // normal close + payload.put((byte)0x03); + byte invalidUtf[] = Hex.asByteArray("CEBAE1BDB9CF83CEBCCEB5EDA080656469746564"); + payload.put(invalidUtf); + BufferUtil.flipToFlush(payload,0); + + List<WebSocketFrame> send = new ArrayList<>(); + WebSocketFrame close = new WebSocketFrame(); + close.setPayload(payload); + close.setOpCode(OpCode.CLOSE); // set opcode after payload (to prevent early bad payload detection) + send.add(close); + + List<WebSocketFrame> expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); + fuzzer.expectNoMoreFrames(); + } + finally + { + fuzzer.close(); + } + } +} diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_9.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_9.java deleted file mode 100644 index 6c70281a6b..0000000000 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_9.java +++ /dev/null @@ -1,185 +0,0 @@ -// ======================================================================== -// Copyright 2011-2012 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// -// You may elect to redistribute this code under either of these licenses. -//======================================================================== -package org.eclipse.jetty.websocket.server.ab; - -import static org.hamcrest.Matchers.*; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import org.eclipse.jetty.util.BufferUtil; -import org.eclipse.jetty.util.StringUtil; -import org.eclipse.jetty.websocket.protocol.CloseInfo; -import org.eclipse.jetty.websocket.protocol.Generator; -import org.eclipse.jetty.websocket.protocol.OpCode; -import org.eclipse.jetty.websocket.protocol.WebSocketFrame; -import org.eclipse.jetty.websocket.server.SimpleServletServer; -import org.eclipse.jetty.websocket.server.blockhead.BlockheadClient; -import org.eclipse.jetty.websocket.server.examples.MyEchoServlet; -import org.eclipse.jetty.websocket.server.helper.IncomingFramesCapture; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -@RunWith(value = Parameterized.class) -public class TestABCase7_9 -{ - private static SimpleServletServer server; - - @Parameters - public static Collection<Integer[]> data() - { - List<Integer[]> data = new ArrayList<>(); - // @formatter:off - data.add(new Integer[] { new Integer(0) }); - data.add(new Integer[] { new Integer(999) }); - data.add(new Integer[] { new Integer(1004) }); - data.add(new Integer[] { new Integer(1005) }); - data.add(new Integer[] { new Integer(1006) }); - data.add(new Integer[] { new Integer(1012) }); - data.add(new Integer[] { new Integer(1013) }); - data.add(new Integer[] { new Integer(1014) }); - data.add(new Integer[] { new Integer(1015) }); - data.add(new Integer[] { new Integer(1016) }); - data.add(new Integer[] { new Integer(1100) }); - data.add(new Integer[] { new Integer(2000) }); - data.add(new Integer[] { new Integer(2999) }); - - // @formatter:on - return data; - } - - @BeforeClass - public static void startServer() throws Exception - { - server = new SimpleServletServer(new MyEchoServlet()); - server.start(); - } - - @AfterClass - public static void stopServer() - { - server.stop(); - } - - private int invalidStatusCode; - - public TestABCase7_9(Integer invalidStatusCode) - { - this.invalidStatusCode = invalidStatusCode; - } - - private void remask(ByteBuffer buf, int position, byte[] mask) - { - int end = buf.position(); - int off; - for (int i = position; i < end; i++) - { - off = i - position; - // Mask each byte by its absolute position in the bytebuffer - buf.put(i,(byte)(buf.get(i) ^ mask[off % 4])); - } - } - - /** - * Test the requirement of issuing - */ - @Test - public void testCase7_9_XInvalidCloseStatusCodes() throws Exception - { - BlockheadClient client = new BlockheadClient(server.getServerUri()); - try - { - client.connect(); - client.sendStandardRequest(); - client.expectUpgradeResponse(); - - ByteBuffer buf = ByteBuffer.allocate(Generator.OVERHEAD + 2); - BufferUtil.clearToFill(buf); - - // Create Close Frame manually, as we are testing the server's behavior of a bad client. - buf.put((byte)(0x80 | OpCode.CLOSE)); - buf.put((byte)(0x80 | 2)); - byte mask[] = new byte[] - { 0x44, 0x44, 0x44, 0x44 }; - buf.put(mask); - int position = buf.position(); - buf.putChar((char)this.invalidStatusCode); - remask(buf,position,mask); - BufferUtil.flipToFlush(buf,0); - client.writeRaw(buf); - - // Read frame (hopefully text frame) - IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500); - WebSocketFrame closeFrame = capture.getFrames().pop(); - Assert.assertThat("CloseFrame.status code",new CloseInfo(closeFrame).getStatusCode(),is(1002)); - } - finally - { - client.close(); - } - } - - /** - * Test the requirement of issuing - */ - @Test - public void testCase7_9_XInvalidCloseStatusCodesWithReason() throws Exception - { - String reason = "closing time"; - - BlockheadClient client = new BlockheadClient(server.getServerUri()); - try - { - client.connect(); - client.sendStandardRequest(); - client.expectUpgradeResponse(); - - ByteBuffer buf = ByteBuffer.allocate(Generator.OVERHEAD + 2); - BufferUtil.clearToFill(buf); - - // Create Close Frame manually, as we are testing the server's behavior of a bad client. - buf.put((byte)(0x80 | OpCode.CLOSE)); - buf.put((byte)(0x80 | (2 + reason.length()))); - byte mask[] = new byte[] - { 0x44, 0x44, 0x44, 0x44 }; - buf.put(mask); - int position = buf.position(); - buf.putChar((char)this.invalidStatusCode); - buf.put(reason.getBytes(StringUtil.__UTF8_CHARSET)); - remask(buf,position,mask); - BufferUtil.flipToFlush(buf,0); - client.writeRaw(buf); - - // Read frame (hopefully text frame) - IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500); - WebSocketFrame closeFrame = capture.getFrames().pop(); - Assert.assertThat("CloseFrame.status code",new CloseInfo(closeFrame).getStatusCode(),is(1002)); - } - finally - { - client.close(); - } - } - -} diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_BadStatusCodes.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_BadStatusCodes.java new file mode 100644 index 0000000000..6d490355a6 --- /dev/null +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_BadStatusCodes.java @@ -0,0 +1,129 @@ +package org.eclipse.jetty.websocket.server.ab; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.eclipse.jetty.util.BufferUtil; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.websocket.api.StatusCode; +import org.eclipse.jetty.websocket.protocol.CloseInfo; +import org.eclipse.jetty.websocket.protocol.OpCode; +import org.eclipse.jetty.websocket.protocol.WebSocketFrame; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +/** + * Test Bad Close Status Codes + */ +@RunWith(value = Parameterized.class) +public class TestABCase7_BadStatusCodes extends AbstractABCase +{ + private static final Logger LOG = Log.getLogger(TestABCase7_GoodStatusCodes.class); + + @Parameters + public static Collection<Object[]> data() + { + // The various Good UTF8 sequences as a String (hex form) + List<Object[]> data = new ArrayList<>(); + + // @formatter:off + data.add(new Object[] { "7.9.1", 0 }); + data.add(new Object[] { "7.9.2", 999 }); + data.add(new Object[] { "7.9.3", 1004 }); + data.add(new Object[] { "7.9.4", 1005 }); + data.add(new Object[] { "7.9.5", 1006 }); + data.add(new Object[] { "7.9.6", 1012 }); + data.add(new Object[] { "7.9.7", 1013 }); + data.add(new Object[] { "7.9.8", 1014 }); + data.add(new Object[] { "7.9.9", 1015 }); + data.add(new Object[] { "7.9.10", 1016 }); + data.add(new Object[] { "7.9.11", 1100 }); + data.add(new Object[] { "7.9.12", 2000 }); + data.add(new Object[] { "7.9.13", 2999 }); + // -- close status codes, with undefined events in spec + data.add(new Object[] { "7.13.1", 5000 }); + data.add(new Object[] { "7.13.2", 65536 }); + // @formatter:on + + return data; + } + + private final int statusCode; + + public TestABCase7_BadStatusCodes(String testId, int statusCode) + { + LOG.debug("Test ID: {}",testId); + this.statusCode = statusCode; + } + + /** + * just the close code, no reason + */ + @Test + public void testBadStatusCode() throws Exception + { + ByteBuffer payload = ByteBuffer.allocate(256); + BufferUtil.clearToFill(payload); + payload.putChar((char)statusCode); + BufferUtil.flipToFlush(payload,0); + + List<WebSocketFrame> send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.CLOSE).setPayload(payload.slice())); + + List<WebSocketFrame> expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); + fuzzer.expectNoMoreFrames(); + } + finally + { + fuzzer.close(); + } + } + + /** + * the bad close code, with reason + */ + @Test + public void testBadStatusCodeWithReason() throws Exception + { + ByteBuffer payload = ByteBuffer.allocate(256); + BufferUtil.clearToFill(payload); + payload.putChar((char)statusCode); + payload.put(StringUtil.getBytes("Reason")); + BufferUtil.flipToFlush(payload,0); + + List<WebSocketFrame> send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.CLOSE).setPayload(payload.slice())); + + List<WebSocketFrame> expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); + fuzzer.expectNoMoreFrames(); + } + finally + { + fuzzer.close(); + } + } +} diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_GoodStatusCodes.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_GoodStatusCodes.java new file mode 100644 index 0000000000..cbb94bf0f8 --- /dev/null +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase7_GoodStatusCodes.java @@ -0,0 +1,124 @@ +package org.eclipse.jetty.websocket.server.ab; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.eclipse.jetty.util.BufferUtil; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.websocket.protocol.OpCode; +import org.eclipse.jetty.websocket.protocol.WebSocketFrame; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +/** + * Test Good Close Status Codes + */ +@RunWith(value = Parameterized.class) +public class TestABCase7_GoodStatusCodes extends AbstractABCase +{ + private static final Logger LOG = Log.getLogger(TestABCase7_GoodStatusCodes.class); + + @Parameters + public static Collection<Object[]> data() + { + // The various Good UTF8 sequences as a String (hex form) + List<Object[]> data = new ArrayList<>(); + + // @formatter:off + data.add(new Object[] { "7.7.1", 1000 }); + data.add(new Object[] { "7.7.2", 1001 }); + data.add(new Object[] { "7.7.3", 1002 }); + data.add(new Object[] { "7.7.4", 1003 }); + data.add(new Object[] { "7.7.5", 1007 }); + data.add(new Object[] { "7.7.6", 1008 }); + data.add(new Object[] { "7.7.7", 1009 }); + data.add(new Object[] { "7.7.8", 1010 }); + data.add(new Object[] { "7.7.9", 1011 }); + data.add(new Object[] { "7.7.10", 3000 }); + data.add(new Object[] { "7.7.11", 3999 }); + data.add(new Object[] { "7.7.12", 4000 }); + data.add(new Object[] { "7.7.13", 4999 }); + // @formatter:on + + return data; + } + + private final int statusCode; + + public TestABCase7_GoodStatusCodes(String testId, int statusCode) + { + LOG.debug("Test ID: {}",testId); + this.statusCode = statusCode; + } + + /** + * just the close code, no reason + */ + @Test + public void testStatusCode() throws Exception + { + ByteBuffer payload = ByteBuffer.allocate(256); + BufferUtil.clearToFill(payload); + payload.putChar((char)statusCode); + BufferUtil.flipToFlush(payload,0); + + List<WebSocketFrame> send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.CLOSE).setPayload(payload.slice())); + + List<WebSocketFrame> expect = new ArrayList<>(); + expect.add(new WebSocketFrame(OpCode.CLOSE).setPayload(payload.slice())); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); + fuzzer.expectNoMoreFrames(); + } + finally + { + fuzzer.close(); + } + } + + /** + * the good close code, with reason + */ + @Test + public void testStatusCodeWithReason() throws Exception + { + ByteBuffer payload = ByteBuffer.allocate(256); + BufferUtil.clearToFill(payload); + payload.putChar((char)statusCode); + payload.put(StringUtil.getBytes("Reason")); + BufferUtil.flipToFlush(payload,0); + + List<WebSocketFrame> send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.CLOSE).setPayload(payload.slice())); + + List<WebSocketFrame> expect = new ArrayList<>(); + expect.add(new WebSocketFrame(OpCode.CLOSE).setPayload(payload.slice())); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); + fuzzer.expectNoMoreFrames(); + } + finally + { + fuzzer.close(); + } + } +} |