diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8Appendable.java b/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8Appendable.java index de02cc4e3f4..0869f3dae85 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8Appendable.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8Appendable.java @@ -179,6 +179,7 @@ public abstract class Utf8Appendable return _state == UTF8_ACCEPT; } + @SuppressWarnings("serial") public static class NotUtf8Exception extends IllegalArgumentException { public NotUtf8Exception(String reason) diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8StringBuilder.java b/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8StringBuilder.java index 09866884eae..b42f1d5f7ed 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8StringBuilder.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8StringBuilder.java @@ -72,6 +72,6 @@ public class Utf8StringBuilder extends Utf8Appendable private void checkState() { if (!isUtf8SequenceComplete()) - throw new IllegalArgumentException("Tried to read incomplete UTF8 decoded String"); + throw new NotUtf8Exception("Tried to read incomplete UTF8 decoded String"); } } diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/log/StdErrLogTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/log/StdErrLogTest.java index 5134a72bc34..441abed918c 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/log/StdErrLogTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/log/StdErrLogTest.java @@ -45,14 +45,14 @@ public class StdErrLogTest log.info("testing:{}",null,null); log.info("testing",null,null); - output.assertContains("INFO:oejul.LogTest:testing:test,format1"); - output.assertContains("INFO:oejul.LogTest:testing:test,format1"); - output.assertContains("INFO:oejul.LogTest:testing:test format2"); - output.assertContains("INFO:oejul.LogTest:testing test format3"); - output.assertContains("INFO:oejul.LogTest:testing:test,null"); - output.assertContains("INFO:oejul.LogTest:testing null null"); - output.assertContains("INFO:oejul.LogTest:testing:null"); - output.assertContains("INFO:oejul.LogTest:testing"); + output.assertContains("INFO:oejul.LogTest:1: testing:test,format1"); + output.assertContains("INFO:oejul.LogTest:1: testing:test,format1"); + output.assertContains("INFO:oejul.LogTest:1: testing:test format2"); + output.assertContains("INFO:oejul.LogTest:1: testing test format3"); + output.assertContains("INFO:oejul.LogTest:1: testing:test,null"); + output.assertContains("INFO:oejul.LogTest:1: testing null null"); + output.assertContains("INFO:oejul.LogTest:1: testing:null"); + output.assertContains("INFO:oejul.LogTest:1: testing"); } @Test @@ -75,12 +75,12 @@ public class StdErrLogTest log.setDebugEnabled(false); log.debug("testing {} {}","test","debug-deprecated-false"); - output.assertContains("DBUG:xxx:testing test debug"); - output.assertContains("INFO:xxx:testing test info"); - output.assertContains("WARN:xxx:testing test warn"); + output.assertContains("DBUG:xxx:1: testing test debug"); + output.assertContains("INFO:xxx:1: testing test info"); + output.assertContains("WARN:xxx:1: testing test warn"); output.assertNotContains("YOU SHOULD NOT SEE THIS!"); - output.assertContains("DBUG:xxx:testing test debug-deprecated"); - output.assertNotContains("DBUG:xxx:testing test debug-depdeprecated-false"); + output.assertContains("DBUG:xxx:1: testing test debug-deprecated"); + output.assertNotContains("DBUG:xxx:1: testing test debug-depdeprecated-false"); } @Test @@ -95,7 +95,7 @@ public class StdErrLogTest Assert.assertThat("Log.name(child)", next.getName(), is("test.next")); next.info("testing {} {}","next","info"); - output.assertContains(":test.next:testing next info"); + output.assertContains(":test.next:1: testing next info"); } @Test @@ -307,7 +307,7 @@ public class StdErrLogTest output.assertContains("Cheer Me"); // Validate Stack Traces - output.assertContains(".StdErrLogTest:"); + output.assertContains(".StdErrLogTest:1: "); output.assertContains("java.lang.Throwable: out of focus"); output.assertContains("java.lang.Throwable: scene lost"); } @@ -354,7 +354,7 @@ public class StdErrLogTest output.assertNotContains(""); output.assertNotContains("on editing room floor"); - output.assertContains(".StdErrLogTest:"); + output.assertContains(".StdErrLogTest:1: "); output.assertContains("java.lang.Throwable: out of focus"); output.assertContains("java.lang.Throwable: scene lost"); } @@ -427,7 +427,7 @@ public class StdErrLogTest output.assertNotContains(""); output.assertNotContains("on editing room floor"); - output.assertContains(".StdErrLogTest:"); + output.assertContains(".StdErrLogTest:1: "); output.assertContains("java.lang.Throwable: out of focus"); output.assertContains("java.lang.Throwable: scene lost"); } diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/AbstractABCase.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/AbstractABCase.java index 1ce3ca4613e..23938089960 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/AbstractABCase.java +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/AbstractABCase.java @@ -4,6 +4,7 @@ import java.nio.ByteBuffer; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.StandardByteBufferPool; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.websocket.api.WebSocketPolicy; import org.eclipse.jetty.websocket.protocol.Generator; import org.eclipse.jetty.websocket.server.SimpleServletServer; @@ -44,6 +45,26 @@ public abstract class AbstractABCase server.stop(); } + public static String toUtf8String(byte[] buf) + { + String raw = StringUtil.toUTF8String(buf,0,buf.length); + StringBuilder ret = new StringBuilder(); + int len = raw.length(); + for (int i = 0; i < len; i++) + { + int codepoint = raw.codePointAt(i); + if (Character.isUnicodeIdentifierPart(codepoint)) + { + ret.append(String.format("\\u%04X",codepoint)); + } + else + { + ret.append(Character.toChars(codepoint)); + } + } + return ret.toString(); + } + public Generator getLaxGenerator() { return laxGenerator; 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 35f02c7b947..eb6267240a6 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 @@ -5,7 +5,7 @@ import org.junit.runners.Suite; @RunWith(Suite.class) @Suite.SuiteClasses( -{ TestABCase1.class, TestABCase2.class, TestABCase3.class, TestABCase4.class, TestABCase5.class, TestABCase7_9.class }) +{ TestABCase1.class, TestABCase2.class, TestABCase3.class, TestABCase4.class, TestABCase5.class, TestABCase6.class, TestABCase7_9.class }) public class AllTests { /* let junit do the rest */ 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 46ccb7e8d7a..5cde38c09b2 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 @@ -50,6 +50,26 @@ public class Fuzzer this.generator = testcase.getLaxGenerator(); } + public ByteBuffer asNetworkBuffer(List send) + { + int buflen = 0; + for (WebSocketFrame f : send) + { + buflen += f.getPayloadLength() + Generator.OVERHEAD; + } + ByteBuffer buf = ByteBuffer.allocate(buflen); + BufferUtil.clearToFill(buf); + + // Generate frames + for (WebSocketFrame f : send) + { + f.setMask(MASK); // make sure we have mask set + BufferUtil.put(generator.generate(f),buf); + } + BufferUtil.flipToFlush(buf,0); + return buf; + } + public void close() { this.client.disconnect(); @@ -80,7 +100,7 @@ public class Fuzzer prefix = "Frame[" + i + "]"; - Assert.assertThat(prefix + ".opcode",actual.getOpCode(),is(expected.getOpCode())); + Assert.assertThat(prefix + ".opcode",OpCode.name(actual.getOpCode()),is(OpCode.name(expected.getOpCode()))); prefix += "/" + actual.getOpCode(); if (expected.getOpCode() == OpCode.CLOSE) { @@ -125,6 +145,12 @@ public class Fuzzer } } + public void send(ByteBuffer buf, int numBytes) throws IOException + { + client.writeRaw(buf,numBytes); + client.flush(); + } + public void send(List send) throws IOException { Assert.assertThat("Client connected",client.isConnected(),is(true)); diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase3.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase3.java index 6e2e0754504..b3335b40e61 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase3.java +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase3.java @@ -1,3 +1,18 @@ +// ======================================================================== +// 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 java.util.ArrayList; diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase4.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase4.java index 5c4e6c23f78..a270ea3094c 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase4.java +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase4.java @@ -151,4 +151,143 @@ public class TestABCase4 extends AbstractABCase fuzzer.close(); } } + + /** + * Send opcode 11 (reserved) + */ + @Test + public void testCase4_2_1() throws Exception + { + List send = new ArrayList<>(); + send.add(new WebSocketFrame((byte)11)); // intentionally bad + + List 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); + } + finally + { + fuzzer.close(); + } + } + + /** + * Send opcode 12 (reserved) + */ + @Test + public void testCase4_2_2() throws Exception + { + List send = new ArrayList<>(); + send.add(new WebSocketFrame((byte)12).setPayload("bad")); // intentionally bad + + List 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); + } + finally + { + fuzzer.close(); + } + } + + /** + * Send small text, then frame with opcode 13 (reserved), then ping + */ + @Test + public void testCase4_2_3() throws Exception + { + List send = new ArrayList<>(); + send.add(WebSocketFrame.text("hello")); + send.add(new WebSocketFrame((byte)13)); // intentionally bad + send.add(WebSocketFrame.ping()); + + List expect = new ArrayList<>(); + expect.add(WebSocketFrame.text("hello")); // echo + 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); + } + finally + { + fuzzer.close(); + } + } + + /** + * Send small text, then frame with opcode 14 (reserved), then ping + */ + @Test + public void testCase4_2_4() throws Exception + { + List send = new ArrayList<>(); + send.add(WebSocketFrame.text("hello")); + send.add(new WebSocketFrame((byte)14).setPayload("bad")); // intentionally bad + send.add(WebSocketFrame.ping()); + + List expect = new ArrayList<>(); + expect.add(WebSocketFrame.text("hello")); // echo + 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); + } + finally + { + fuzzer.close(); + } + } + + /** + * Send small text, then frame with opcode 15 (reserved), then ping + */ + @Test + public void testCase4_2_5() throws Exception + { + List send = new ArrayList<>(); + send.add(WebSocketFrame.text("hello")); + send.add(new WebSocketFrame((byte)15).setPayload("bad")); // intentionally bad + send.add(WebSocketFrame.ping()); + + List expect = new ArrayList<>(); + expect.add(WebSocketFrame.text("hello")); // echo + 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); + } + finally + { + fuzzer.close(); + } + } } diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase5.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase5.java index 18a719d66a6..e7aa498830f 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase5.java +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase5.java @@ -15,460 +15,731 @@ //======================================================================== package org.eclipse.jetty.websocket.server.ab; -import static org.hamcrest.Matchers.*; - -import java.nio.ByteBuffer; +import java.net.SocketException; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.TimeUnit; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.StandardByteBufferPool; -import org.eclipse.jetty.util.BufferUtil; -import org.eclipse.jetty.util.StringUtil; -import org.eclipse.jetty.websocket.api.WebSocketPolicy; +import org.eclipse.jetty.toolchain.test.AdvancedRunner; +import org.eclipse.jetty.toolchain.test.annotation.Slow; +import org.eclipse.jetty.websocket.api.StatusCode; 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.ByteBufferAssert; -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.Ignore; import org.junit.Test; +import org.junit.runner.RunWith; -public class TestABCase5 +/** + * Fragmentation Tests + */ +@RunWith(AdvancedRunner.class) +public class TestABCase5 extends AbstractABCase { - private static final byte FIN = (byte)0x80; - private static final byte NOFIN = 0x00; - - private static SimpleServletServer server; - private static Generator laxGenerator; - - @BeforeClass - public static void initGenerators() - { - WebSocketPolicy policy = WebSocketPolicy.newServerPolicy(); - ByteBufferPool bufferPool = new StandardByteBufferPool(); - laxGenerator = new Generator(policy,bufferPool,false); - } - - @BeforeClass - public static void startServer() throws Exception - { - server = new SimpleServletServer(new MyEchoServlet()); - server.start(); - } - - @AfterClass - public static void stopServer() - { - server.stop(); - } - + /** + * Send ping fragmented in 2 packets + */ @Test - public void testCase5_1PingIn2Packets() throws Exception + public void testCase5_1() throws Exception { - BlockheadClient client = new BlockheadClient(server.getServerUri()); + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.PING).setPayload("hello, ").setFin(false)); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload("world")); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); try { - client.connect(); - client.sendStandardRequest(); - client.expectUpgradeResponse(); - - ByteBuffer buf = ByteBuffer.allocate(Generator.OVERHEAD + 2); - BufferUtil.clearToFill(buf); - - String fragment1 = "fragment1"; - - // Intentionally bad PING (spec says control frames must be FIN==true) - buf.put((byte)(NOFIN | OpCode.PING)); - - byte b = 0x00; // no masking - b |= fragment1.length() & 0x7F; - buf.put(b); - buf.put(fragment1.getBytes()); - BufferUtil.flipToFlush(buf,0); - - client.writeRaw(buf); - - ByteBuffer buf2 = ByteBuffer.allocate(Generator.OVERHEAD + 2); - BufferUtil.clearToFill(buf2); - - String fragment2 = "fragment2"; - - buf2.put((byte)(FIN | OpCode.PING)); - b = 0x00; // no masking - b |= fragment2.length() & 0x7F; - buf2.put(b); - buf2.put(fragment2.getBytes()); - BufferUtil.flipToFlush(buf2,0); - - client.writeRaw(buf2); - - // Read frame - IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500); - WebSocketFrame frame = capture.getFrames().get(0); - - Assert.assertThat("frame should be close frame",frame.getOpCode(),is(OpCode.CLOSE)); - - Assert.assertThat("CloseFrame.status code",new CloseInfo(frame).getStatusCode(),is(1002)); + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); } finally { - client.close(); + fuzzer.close(); } } + /** + * Send continuation+fin, then text+fin (framewise) + */ @Test - public void testCase5_1PingIn2PacketsWithBuilder() throws Exception + public void testCase5_10() throws Exception { - BlockheadClient client = new BlockheadClient(server.getServerUri()); + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload("sorry").setFin(true)); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload("hello, world")); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); try { - client.connect(); - client.sendStandardRequest(); - client.expectUpgradeResponse(); - - String fragment1 = "fragment1"; - WebSocketFrame frame1 = WebSocketFrame.ping().setFin(false).setPayload(fragment1); - ByteBuffer buf1 = laxGenerator.generate(frame1); - client.writeRaw(buf1); - - String fragment2 = "fragment2"; - WebSocketFrame frame2 = WebSocketFrame.ping().setPayload(fragment2); - ByteBuffer buf2 = laxGenerator.generate(frame2); - client.writeRaw(buf2); - - // Read frame - IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500); - WebSocketFrame frame = capture.getFrames().pop(); - - Assert.assertThat("frame should be close frame",frame.getOpCode(),is(OpCode.CLOSE)); - - Assert.assertThat("CloseFrame.status code",new CloseInfo(frame).getStatusCode(),is(1002)); + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.PER_FRAME); + fuzzer.send(send); + fuzzer.expect(expect); } finally { - client.close(); + fuzzer.close(); } } + /** + * Send continuation+fin, then text+fin (slowly) + */ @Test - public void testCase5_2PongIn2Packets() throws Exception + public void testCase5_11() throws Exception { - BlockheadClient client = new BlockheadClient(server.getServerUri()); + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload("sorry").setFin(true)); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload("hello, world")); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); try { - client.connect(); - client.sendStandardRequest(); - client.expectUpgradeResponse(); - - ByteBuffer buf = ByteBuffer.allocate(Generator.OVERHEAD + 2); - BufferUtil.clearToFill(buf); - - String fragment1 = "fragment1"; - - buf.put((byte)(NOFIN | OpCode.PONG)); - - byte b = 0x00; // no masking - b |= fragment1.length() & 0x7F; - buf.put(b); - buf.put(fragment1.getBytes()); - BufferUtil.flipToFlush(buf,0); - - client.writeRaw(buf); - - ByteBuffer buf2 = ByteBuffer.allocate(Generator.OVERHEAD + 2); - BufferUtil.clearToFill(buf2); - - String fragment2 = "fragment2"; - - buf2.put((byte)(FIN | OpCode.CONTINUATION)); - b = 0x00; // no masking - b |= fragment2.length() & 0x7F; - buf2.put(b); - buf2.put(fragment2.getBytes()); - BufferUtil.flipToFlush(buf2,0); - - client.writeRaw(buf2); - - // Read frame - IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500); - WebSocketFrame frame = capture.getFrames().pop(); - - Assert.assertThat("frame should be close frame",frame.getOpCode(),is(OpCode.CLOSE)); - - Assert.assertThat("CloseFrame.status code",new CloseInfo(frame).getStatusCode(),is(1002)); + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.SLOW); + fuzzer.setSlowSendSegmentSize(1); + try + { + fuzzer.send(send); + } + catch (SocketException ignore) + { + // Potential for SocketException (Broken Pipe) here. + // But not in 100% of testing scenarios. It is a safe + // exception to ignore in this testing scenario, as the + // slow writing of the frames can result in the server + // throwing a PROTOCOL ERROR termination/close when it + // encounters the bad continuation frame above (this + // termination is the expected behavior), and this + // early socket close can propagate back to the client + // before it has a chance to finish writing out the + // remaining frame octets + } + fuzzer.expect(expect); } finally { - client.close(); + fuzzer.close(); } } + /** + * Send continuation+!fin, then text+fin + */ @Test - public void testCase5_2PongIn2PacketsWithBuilder() throws Exception + public void testCase5_12() throws Exception { - BlockheadClient client = new BlockheadClient(server.getServerUri()); + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload("sorry").setFin(false)); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload("hello, world")); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); try { - client.connect(); - client.sendStandardRequest(); - client.expectUpgradeResponse(); - - String fragment1 = "fragment1"; - WebSocketFrame frame1 = WebSocketFrame.pong().setFin(false).setPayload(fragment1); - ByteBuffer buf1 = laxGenerator.generate(frame1); - client.writeRaw(buf1); - - String fragment2 = "fragment2"; - WebSocketFrame frame2 = new WebSocketFrame(OpCode.CONTINUATION).setFin(false).setPayload(fragment2); - ByteBuffer buf2 = laxGenerator.generate(frame2); - client.writeRaw(buf2); - - // Read frame - IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500); - WebSocketFrame frame = capture.getFrames().pop(); - - Assert.assertThat("frame should be close frame",frame.getOpCode(),is(OpCode.CLOSE)); - Assert.assertThat("CloseFrame.status code",new CloseInfo(frame).getStatusCode(),is(1002)); + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); } finally { - client.disconnect(); + fuzzer.close(); } } + /** + * Send continuation+!fin, then text+fin (framewise) + */ @Test - public void testCase5_3TextIn2Packets() throws Exception + public void testCase5_13() throws Exception { - BlockheadClient client = new BlockheadClient(server.getServerUri()); + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload("sorry").setFin(false)); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload("hello, world")); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); try { - client.connect(); - client.sendStandardRequest(); - client.expectUpgradeResponse(); - - ByteBuffer buf = ByteBuffer.allocate(Generator.OVERHEAD + 2); - BufferUtil.clearToFill(buf); - - String fragment1 = "fragment1"; - - buf.put((byte)(NOFIN | OpCode.TEXT)); - - byte b = 0x00; // no masking - b |= fragment1.length() & 0x7F; - buf.put(b); - buf.put(fragment1.getBytes()); - BufferUtil.flipToFlush(buf,0); - - client.writeRaw(buf); - - ByteBuffer buf2 = ByteBuffer.allocate(Generator.OVERHEAD + 2); - BufferUtil.clearToFill(buf2); - - String fragment2 = "fragment2"; - - buf2.put((byte)(FIN | OpCode.CONTINUATION)); - b = 0x00; // no masking - b |= fragment2.length() & 0x7F; - buf2.put(b); - buf2.put(fragment2.getBytes()); - BufferUtil.flipToFlush(buf2,0); - - client.writeRaw(buf2); - - // Read frame - IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500); - WebSocketFrame frame = capture.getFrames().pop(); - - Assert.assertThat("frame should be text frame",frame.getOpCode(),is(OpCode.TEXT)); - - Assert.assertThat("TextFrame.payload",frame.getPayloadAsUTF8(),is(fragment1 + fragment2)); + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.PER_FRAME); + fuzzer.send(send); + fuzzer.expect(expect); } finally { - client.close(); + fuzzer.close(); } } + /** + * Send continuation+!fin, then text+fin (slowly) + */ @Test - public void testCase5_6TextPingRemainingText() throws Exception + public void testCase5_14() throws Exception { - BlockheadClient client = new BlockheadClient(server.getServerUri()); + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload("sorry").setFin(false)); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload("hello, world")); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); try { - client.connect(); - client.sendStandardRequest(); - client.expectUpgradeResponse(); - - // Send a text packet - - ByteBuffer buf = ByteBuffer.allocate(Generator.OVERHEAD + 2); - BufferUtil.clearToFill(buf); - - String fragment1 = "fragment1"; - - buf.put((byte)(NOFIN | OpCode.TEXT)); - - byte b = 0x00; // no masking - b |= fragment1.length() & 0x7F; - buf.put(b); - buf.put(fragment1.getBytes()); - BufferUtil.flipToFlush(buf,0); - - client.writeRaw(buf); - - // Send a ping with payload - - ByteBuffer pingBuf = ByteBuffer.allocate(Generator.OVERHEAD + 2); - BufferUtil.clearToFill(pingBuf); - - String pingPayload = "ping payload"; - - pingBuf.put((byte)(FIN | OpCode.PING)); - - b = 0x00; // no masking - b |= pingPayload.length() & 0x7F; - pingBuf.put(b); - pingBuf.put(pingPayload.getBytes()); - BufferUtil.flipToFlush(pingBuf,0); - - client.writeRaw(pingBuf); - - // Send remaining text as continuation - - ByteBuffer buf2 = ByteBuffer.allocate(Generator.OVERHEAD + 2); - BufferUtil.clearToFill(buf2); - - String fragment2 = "fragment2"; - - buf2.put((byte)(FIN | OpCode.CONTINUATION)); - b = 0x00; // no masking - b |= fragment2.length() & 0x7F; - buf2.put(b); - buf2.put(fragment2.getBytes()); - BufferUtil.flipToFlush(buf2,0); - - client.writeRaw(buf2); - - // Should be 2 frames, pong frame followed by combined echo'd text frame - IncomingFramesCapture capture = client.readFrames(2,TimeUnit.SECONDS,1); - WebSocketFrame frame = capture.getFrames().pop(); - - Assert.assertThat("first frame should be pong frame",frame.getOpCode(),is(OpCode.PONG)); - - ByteBuffer payload1 = BufferUtil.toBuffer(pingPayload,StringUtil.__UTF8_CHARSET); - - ByteBufferAssert.assertEquals("payloads should be equal",payload1,frame.getPayload()); - frame = capture.getFrames().pop(); - - Assert.assertThat("second frame should be text frame",frame.getOpCode(),is(OpCode.TEXT)); - Assert.assertThat("TextFrame.payload",frame.getPayloadAsUTF8(),is(fragment1 + fragment2)); + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.SLOW); + fuzzer.setSlowSendSegmentSize(1); + try + { + fuzzer.send(send); + } + catch (SocketException ignore) + { + // Potential for SocketException (Broken Pipe) here. + // But not in 100% of testing scenarios. It is a safe + // exception to ignore in this testing scenario, as the + // slow writing of the frames can result in the server + // throwing a PROTOCOL ERROR termination/close when it + // encounters the bad continuation frame above (this + // termination is the expected behavior), and this + // early socket close can propagate back to the client + // before it has a chance to finish writing out the + // remaining frame octets + } + fuzzer.expect(expect); } finally { - client.close(); + fuzzer.close(); } } + /** + * Send text fragmented properly in 2 frames, then continuation!fin, then text unfragmented. + */ @Test - public void testCase5_6TextPingRemainingTextWithBuilder() throws Exception + public void testCase5_15() throws Exception { - BlockheadClient client = new BlockheadClient(server.getServerUri()); + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload("fragment1").setFin(false)); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload("fragment2").setFin(true)); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload("fragment3").setFin(false)); // bad frame + send.add(new WebSocketFrame(OpCode.TEXT).setPayload("fragment4").setFin(true)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + send.add(WebSocketFrame.text("fragment1fragment2")); + expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); try { - client.connect(); - client.sendStandardRequest(); - client.expectUpgradeResponse(); - - // Send a text packet - String textPayload1 = "fragment1"; - WebSocketFrame frame1 = WebSocketFrame.text().setFin(false).setPayload(textPayload1); - ByteBuffer buf1 = laxGenerator.generate(frame1); - client.writeRaw(buf1); - - // Send a ping with payload - String pingPayload = "ping payload"; - WebSocketFrame frame2 = WebSocketFrame.ping().setPayload(pingPayload); - ByteBuffer buf2 = laxGenerator.generate(frame2); - client.writeRaw(buf2); - - // Send remaining text as continuation - String textPayload2 = "fragment2"; - WebSocketFrame frame3 = new WebSocketFrame(OpCode.CONTINUATION).setPayload(textPayload2); - ByteBuffer buf3 = laxGenerator.generate(frame3); - client.writeRaw(buf3); - - // Should be 2 frames, pong frame followed by combined echo'd text frame - IncomingFramesCapture capture = client.readFrames(2,TimeUnit.MILLISECONDS,500); - WebSocketFrame frame = capture.getFrames().pop(); - - Assert.assertThat("first frame should be pong frame",frame.getOpCode(),is(OpCode.PONG)); - - ByteBuffer payload1 = BufferUtil.toBuffer(pingPayload,StringUtil.__UTF8_CHARSET); - ByteBufferAssert.assertEquals("Payload",payload1,frame.getPayload()); - - frame = capture.getFrames().pop(); - - Assert.assertThat("second frame should be text frame",frame.getOpCode(),is(OpCode.TEXT)); - - Assert.assertThat("TextFrame.payload",frame.getPayloadAsUTF8(),is(textPayload1 + textPayload2)); + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); } finally { - client.close(); + fuzzer.close(); } } + /** + * (continuation!fin, text!fin, continuation+fin) * 2 + */ @Test - @Ignore("AB tests have chop concepts currently unsupported by test...I think, also the string being returns is not Bad Continuation") - public void testCase5_9BadContinuation() throws Exception + public void testCase5_16() throws Exception { - BlockheadClient client = new BlockheadClient(server.getServerUri()); + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload("fragment1").setFin(false)); // bad frame + send.add(new WebSocketFrame(OpCode.TEXT).setPayload("fragment2").setFin(false)); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload("fragment3").setFin(true)); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload("fragment4").setFin(false)); // bad frame + send.add(new WebSocketFrame(OpCode.TEXT).setPayload("fragment5").setFin(false)); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload("fragment6").setFin(true)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); try { - client.connect(); - client.sendStandardRequest(); - client.expectUpgradeResponse(); - - // Send a text packet - - ByteBuffer buf = ByteBuffer.allocate(Generator.OVERHEAD + 2); - BufferUtil.clearToFill(buf); - - String fragment1 = "fragment"; - - // continuation w / FIN - - buf.put((byte)(FIN | OpCode.CONTINUATION)); - - byte b = 0x00; // no masking - b |= fragment1.length() & 0x7F; - buf.put(b); - buf.put(fragment1.getBytes()); - BufferUtil.flipToFlush(buf,0); - - client.writeRaw(buf); - - // Read frame - IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500); - WebSocketFrame frame = capture.getFrames().pop(); - - Assert.assertThat("frame should be close frame",frame.getOpCode(),is(OpCode.CLOSE)); - - Assert.assertThat("CloseFrame.status code",new CloseInfo(frame).getStatusCode(),is(1002)); - - Assert.assertThat("CloseFrame.reason",new CloseInfo(frame).getReason(),is("Bad Continuation")); - // TODO put close reasons into public strings in impl someplace? + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); } finally { - client.close(); + fuzzer.close(); + } + } + + /** + * (continuation+fin, text!fin, continuation+fin) * 2 + */ + @Test + public void testCase5_17() throws Exception + { + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload("fragment1").setFin(true)); // nothing to continue + send.add(new WebSocketFrame(OpCode.TEXT).setPayload("fragment2").setFin(false)); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload("fragment3").setFin(true)); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload("fragment4").setFin(true)); // nothing to continue + send.add(new WebSocketFrame(OpCode.TEXT).setPayload("fragment5").setFin(false)); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload("fragment6").setFin(true)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List 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); + } + finally + { + fuzzer.close(); + } + } + + /** + * text message fragmented in 2 frames, both frames as opcode=TEXT + */ + @Test + public void testCase5_18() throws Exception + { + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload("fragment1").setFin(false)); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload("fragment2").setFin(true)); // bad frame, must be continuation + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List 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); + } + finally + { + fuzzer.close(); + } + } + + /** + * send text message fragmented in 5 frames, with 2 pings and wait between. + */ + @Test + @Slow + public void testCase5_19() throws Exception + { + // phase 1 + List send1 = new ArrayList<>(); + send1.add(new WebSocketFrame(OpCode.TEXT).setPayload("f1").setFin(false)); + send1.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload(",f2").setFin(false)); + send1.add(new WebSocketFrame(OpCode.PING).setPayload("pong-1")); + + List expect1 = new ArrayList<>(); + expect1.add(WebSocketFrame.pong().setPayload("pong-1")); + + // phase 2 + List send2 = new ArrayList<>(); + send2.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload(",f3").setFin(false)); + send2.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload(",f4").setFin(false)); + send2.add(new WebSocketFrame(OpCode.PING).setPayload("pong-2")); + send2.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload(",f5").setFin(true)); + send2.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect2 = new ArrayList<>(); + expect2.add(WebSocketFrame.pong().setPayload("pong-2")); + expect2.add(WebSocketFrame.text("f1,f2,f3,f4,f5")); + expect2.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + + // phase 1 + fuzzer.send(send1); + fuzzer.expect(expect1); + + // delay + TimeUnit.SECONDS.sleep(1); + + // phase 2 + fuzzer.send(send2); + fuzzer.expect(expect2); + } + finally + { + fuzzer.close(); + } + } + + /** + * Send pong fragmented in 2 packets + */ + @Test + public void testCase5_2() throws Exception + { + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.PONG).setPayload("hello, ").setFin(false)); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload("world")); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List 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); + } + finally + { + fuzzer.close(); + } + } + + /** + * send text message fragmented in 5 frames, with 2 pings and wait between. (framewise) + */ + @Test + public void testCase5_20() throws Exception + { + List send1 = new ArrayList<>(); + send1.add(new WebSocketFrame(OpCode.TEXT).setPayload("f1").setFin(false)); + send1.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload(",f2").setFin(false)); + send1.add(new WebSocketFrame(OpCode.PING).setPayload("pong-1")); + + List send2 = new ArrayList<>(); + send2.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload(",f3").setFin(false)); + send2.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload(",f4").setFin(false)); + send2.add(new WebSocketFrame(OpCode.PING).setPayload("pong-2")); + send2.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload(",f5").setFin(true)); + send2.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect1 = new ArrayList<>(); + expect1.add(WebSocketFrame.pong().setPayload("pong-1")); + + List expect2 = new ArrayList<>(); + expect2.add(WebSocketFrame.pong().setPayload("pong-2")); + expect2.add(WebSocketFrame.text("f1,f2,f3,f4,f5")); + expect2.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.PER_FRAME); + + fuzzer.send(send1); + fuzzer.expect(expect1); + + TimeUnit.SECONDS.sleep(1); + + fuzzer.send(send2); + fuzzer.expect(expect2); + } + finally + { + fuzzer.close(); + } + } + + /** + * send text message fragmented in 5 frames, with 2 pings and wait between. (framewise) + */ + @Test + public void testCase5_20_slow() throws Exception + { + List send1 = new ArrayList<>(); + send1.add(new WebSocketFrame(OpCode.TEXT).setPayload("f1").setFin(false)); + send1.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload(",f2").setFin(false)); + send1.add(new WebSocketFrame(OpCode.PING).setPayload("pong-1")); + + List send2 = new ArrayList<>(); + send2.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload(",f3").setFin(false)); + send2.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload(",f4").setFin(false)); + send2.add(new WebSocketFrame(OpCode.PING).setPayload("pong-2")); + send2.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload(",f5").setFin(true)); + send2.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect1 = new ArrayList<>(); + expect1.add(WebSocketFrame.pong().setPayload("pong-1")); + + List expect2 = new ArrayList<>(); + expect2.add(WebSocketFrame.pong().setPayload("pong-2")); + expect2.add(WebSocketFrame.text("f1,f2,f3,f4,f5")); + expect2.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.SLOW); + fuzzer.setSlowSendSegmentSize(1); + + fuzzer.send(send1); + fuzzer.expect(expect1); + + TimeUnit.SECONDS.sleep(1); + + fuzzer.send(send2); + fuzzer.expect(expect2); + } + finally + { + fuzzer.close(); + } + } + + /** + * Send text fragmented in 2 packets + */ + @Test + public void testCase5_3() throws Exception + { + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload("hello, ").setFin(false)); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload("world").setFin(true)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List 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(); + } + } + + /** + * Send text fragmented in 2 packets (framewise) + */ + @Test + public void testCase5_4() throws Exception + { + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload("hello, ").setFin(false)); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload("world").setFin(true)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List 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.PER_FRAME); + fuzzer.send(send); + fuzzer.expect(expect); + } + finally + { + fuzzer.close(); + } + } + + /** + * Send text fragmented in 2 packets (slowly) + */ + @Test + public void testCase5_5() throws Exception + { + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload("hello, ").setFin(false)); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload("world").setFin(true)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List 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.SLOW); + fuzzer.setSlowSendSegmentSize(1); + fuzzer.send(send); + fuzzer.expect(expect); + } + finally + { + fuzzer.close(); + } + } + + /** + * Send text fragmented in 2 packets, with ping between them + */ + @Test + public void testCase5_6() throws Exception + { + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload("hello, ").setFin(false)); + send.add(new WebSocketFrame(OpCode.PING).setPayload("ping")); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload("world").setFin(true)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.add(WebSocketFrame.pong().setPayload("ping")); + 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(); + } + } + + /** + * Send text fragmented in 2 packets, with ping between them (framewise) + */ + @Test + public void testCase5_7() throws Exception + { + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload("hello, ").setFin(false)); + send.add(new WebSocketFrame(OpCode.PING).setPayload("ping")); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload("world").setFin(true)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.add(WebSocketFrame.pong().setPayload("ping")); + 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.PER_FRAME); + fuzzer.send(send); + fuzzer.expect(expect); + } + finally + { + fuzzer.close(); + } + } + + /** + * Send text fragmented in 2 packets, with ping between them (slowly) + */ + @Test + public void testCase5_8() throws Exception + { + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload("hello, ").setFin(false)); + send.add(new WebSocketFrame(OpCode.PING).setPayload("ping")); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload("world").setFin(true)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.add(WebSocketFrame.pong().setPayload("ping")); + 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.SLOW); + fuzzer.setSlowSendSegmentSize(1); + fuzzer.send(send); + fuzzer.expect(expect); + } + finally + { + fuzzer.close(); + } + } + + /** + * Send continuation+fin, then text+fin + */ + @Test + public void testCase5_9() throws Exception + { + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload("sorry").setFin(true)); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload("hello, world")); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List 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); + } + finally + { + fuzzer.close(); } } } diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6.java new file mode 100644 index 00000000000..08d60fc3734 --- /dev/null +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase6.java @@ -0,0 +1,846 @@ +// ======================================================================== +// 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 java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.toolchain.test.AdvancedRunner; +import org.eclipse.jetty.toolchain.test.annotation.Slow; +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.Test; +import org.junit.runner.RunWith; + +/** + * UTF-8 Tests + */ +@RunWith(AdvancedRunner.class) +public class TestABCase6 extends AbstractABCase +{ + /** + * text message, 1 frame, 0 length + */ + @Test + public void testCase6_1_1() throws Exception + { + List send = new ArrayList<>(); + send.add(WebSocketFrame.text()); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.add(WebSocketFrame.text()); + 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(); + } + } + + /** + * text message, 0 length, 3 fragments + */ + @Test + public void testCase6_1_2() throws Exception + { + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setFin(false)); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setFin(false)); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setFin(true)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.add(WebSocketFrame.text()); + 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(); + } + } + + /** + * text message, small length, 3 fragments (only middle frame has payload) + */ + @Test + public void testCase6_1_3() throws Exception + { + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setFin(false)); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setFin(false).setPayload("middle")); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setFin(true)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.add(WebSocketFrame.text("middle")); + 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(); + } + } + + /** + * valid utf8 text message, 1 frame/fragment. + */ + @Test + public void testCase6_2_1() throws Exception + { + String utf8 = "Hello-\uC2B5@\uC39F\uC3A4\uC3BC\uC3A0\uC3A1-UTF-8!!"; + byte msg[] = StringUtil.getUtf8Bytes(utf8); + + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload(msg)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.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); + } + finally + { + fuzzer.close(); + } + } + + /** + * valid utf8 text message, 2 fragments (on UTF8 code point boundary) + */ + @Test + public void testCase6_2_2() throws Exception + { + String utf8[] = + { "Hello-\uC2B5@\uC39F\uC3A4", "\uC3BC\uC3A0\uC3A1-UTF-8!!" }; + + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload(utf8[0]).setFin(false)); + send.add(new WebSocketFrame(OpCode.CONTINUATION).setPayload(utf8[1]).setFin(true)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.add(new WebSocketFrame(OpCode.TEXT).setPayload(utf8[0] + utf8[1])); + 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(); + } + } + + /** + * valid utf8 text message, many fragments (1 byte each) + */ + @Test + public void testCase6_2_3() throws Exception + { + String utf8 = "Hello-\uC2B5@\uC39F\uC3A4\uC3BC\uC3A0\uC3A1-UTF-8!!"; + byte msg[] = StringUtil.getUtf8Bytes(utf8); + + List send = new ArrayList<>(); + int len = msg.length; + byte opcode = OpCode.TEXT; + byte mini[]; + for (int i = 0; i < len; i++) + { + WebSocketFrame frame = new WebSocketFrame(opcode); + mini = new byte[1]; + mini[0] = msg[i]; + frame.setPayload(mini); + frame.setFin(!(i < (len - 1))); + send.add(frame); + } + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.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); + } + finally + { + fuzzer.close(); + } + } + + /** + * valid utf8 text message, many fragments (1 byte each) + */ + @Test + public void testCase6_2_4() throws Exception + { + byte msg[] = Hex.asByteArray("CEBAE1BDB9CF83CEBCCEB5"); + + List send = new ArrayList<>(); + int len = msg.length; + byte opcode = OpCode.TEXT; + byte mini[]; + for (int i = 0; i < len; i++) + { + WebSocketFrame frame = new WebSocketFrame(opcode); + mini = new byte[1]; + mini[0] = msg[i]; + frame.setPayload(mini); + frame.setFin(!(i < (len - 1))); + send.add(frame); + } + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.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); + } + finally + { + fuzzer.close(); + } + } + + /** + * invalid utf8 text message, 1 frame/fragments + */ + @Test + public void testCase6_3_1() throws Exception + { + byte invalid[] = Hex.asByteArray("CEBAE1BDB9CF83CEBCCEB5EDA080656469746564"); + + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload(invalid)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); + } + finally + { + fuzzer.close(); + } + } + + /** + * invalid utf8 text message, many fragments (1 byte each) + */ + @Test + public void testCase6_3_2() throws Exception + { + byte invalid[] = Hex.asByteArray("CEBAE1BDB9CF83CEBCCEB5EDA080656469746564"); + + List send = new ArrayList<>(); + int len = invalid.length; + byte opcode = OpCode.TEXT; + byte mini[]; + for (int i = 0; i < len; i++) + { + WebSocketFrame frame = new WebSocketFrame(opcode); + mini = new byte[1]; + mini[0] = invalid[i]; + frame.setPayload(mini); + frame.setFin(!(i < (len - 1))); + send.add(frame); + } + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); + } + finally + { + fuzzer.close(); + } + } + + /** + * invalid text message, 3 fragments. + *

+ * fragment #1 and fragment #3 are both valid in themselves. + *

+ * fragment #2 contains the invalid utf8 code point. + */ + @Test + @Slow + public void testCase6_4_1() throws Exception + { + byte part1[] = StringUtil.getUtf8Bytes("\u03BA\u1F79\u03C3\u03BC\u03B5"); + byte part2[] = Hex.asByteArray("F4908080"); // invalid + byte part3[] = StringUtil.getUtf8Bytes("edited"); + + List expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + + fuzzer.send(new WebSocketFrame(OpCode.TEXT).setPayload(part1).setFin(false)); + TimeUnit.SECONDS.sleep(1); + fuzzer.send(new WebSocketFrame(OpCode.CONTINUATION).setPayload(part2).setFin(false)); + TimeUnit.SECONDS.sleep(1); + fuzzer.send(new WebSocketFrame(OpCode.CONTINUATION).setPayload(part3).setFin(true)); + + fuzzer.expect(expect); + } + finally + { + fuzzer.close(); + } + } + + /** + * invalid text message, 3 fragments. + *

+ * fragment #1 is valid and ends in the middle of an incomplete code point. + *

+ * fragment #2 finishes the UTF8 code point but it is invalid + *

+ * fragment #3 contains the remainder of the message. + */ + @Test + @Slow + public void testCase6_4_2() throws Exception + { + byte part1[] = Hex.asByteArray("CEBAE1BDB9CF83CEBCCEB5F4"); // split code point + byte part2[] = Hex.asByteArray("90"); // continue code point & invalid + byte part3[] = Hex.asByteArray("8080656469746564"); // continue code point & finish + + List expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(new WebSocketFrame(OpCode.TEXT).setPayload(part1).setFin(false)); + TimeUnit.SECONDS.sleep(1); + fuzzer.send(new WebSocketFrame(OpCode.CONTINUATION).setPayload(part2).setFin(false)); + TimeUnit.SECONDS.sleep(1); + fuzzer.send(new WebSocketFrame(OpCode.CONTINUATION).setPayload(part3).setFin(true)); + fuzzer.expect(expect); + } + finally + { + fuzzer.close(); + } + } + + /** + * invalid text message, 1 frame/fragment (slowly, and split within code points) + */ + @Test + @Slow + public void testCase6_4_3() throws Exception + { + byte invalid[] = Hex.asByteArray("CEBAE1BDB9CF83CEBCCEB5F49080808080656469746564"); + + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload(invalid)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + + ByteBuffer net = fuzzer.asNetworkBuffer(send); + fuzzer.send(net,6); + fuzzer.send(net,11); + TimeUnit.SECONDS.sleep(1); + fuzzer.send(net,4); + TimeUnit.SECONDS.sleep(1); + fuzzer.send(net,100); // the rest + + fuzzer.expect(expect); + } + finally + { + fuzzer.close(); + } + } + + /** + * invalid text message, 1 frame/fragment (slowly, and split within code points) + */ + @Test + @Slow + public void testCase6_4_4() throws Exception + { + byte invalid[] = Hex.asByteArray("CEBAE1BDB9CF83CEBCCEB5F49080808080656469746564"); + + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload(invalid)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + + ByteBuffer net = fuzzer.asNetworkBuffer(send); + fuzzer.send(net,6); + fuzzer.send(net,11); + TimeUnit.SECONDS.sleep(1); + fuzzer.send(net,1); + TimeUnit.SECONDS.sleep(1); + fuzzer.send(net,100); // the rest + + fuzzer.expect(expect); + } + finally + { + fuzzer.close(); + } + } + + /** + * valid utf8 text message, 1 frame/fragment. + */ + @Test + public void testCase6_5_1() throws Exception + { + byte msg[] = Hex.asByteArray("CEBAE1BDB9CF83CEBCCEB5"); + + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload(msg)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.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); + } + finally + { + fuzzer.close(); + } + } + + /** + * invalid utf8 (incomplete code point) text message, 1 frame/fragment. + */ + @Test + public void testCase6_6_1() throws Exception + { + byte incomplete[] = Hex.asByteArray("CE"); + + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload(incomplete)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); + } + finally + { + fuzzer.close(); + } + } + + /** + * invalid utf8 text message, 1 frame/fragment, 4 valid code points + 1 partial code point + */ + @Test + public void testCase6_6_10() throws Exception + { + byte invalid[] = Hex.asByteArray("CEBAE1BDB9CF83CEBCCE"); + + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload(invalid)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); + } + finally + { + fuzzer.close(); + } + } + + /** + * valid utf8 text message, 1 frame/fragment, 5 valid code points (preserved on echo). + */ + @Test + public void testCase6_6_11() throws Exception + { + byte msg[] = Hex.asByteArray("CEBAE1BDB9CF83CEBCCEB5"); + + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload(msg)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.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); + } + finally + { + fuzzer.close(); + } + } + + /** + * valid utf8 text message, 1 frame/fragment, 1 valid code point (preserved on echo). + */ + @Test + public void testCase6_6_2() throws Exception + { + byte msg[] = Hex.asByteArray("CEBA"); + + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload(msg)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.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); + } + finally + { + fuzzer.close(); + } + } + + /** + * invalid utf8 text message, 1 frame/fragment, 1 valid code point + 1 partial code point + */ + @Test + public void testCase6_6_3() throws Exception + { + byte invalid[] = Hex.asByteArray("CEBAE1"); + + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload(invalid)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); + } + finally + { + fuzzer.close(); + } + } + + /** + * invalid utf8 text message, 1 frame/fragment, 1 valid code point + 1 invalid code point + */ + @Test + public void testCase6_6_4() throws Exception + { + byte invalid[] = Hex.asByteArray("CEBAE1BD"); + + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload(invalid)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); + } + finally + { + fuzzer.close(); + } + } + + /** + * valid utf8 text message, 1 frame/fragment, 2 valid code points (preserved on echo). + */ + @Test + public void testCase6_6_5() throws Exception + { + byte msg[] = Hex.asByteArray("CEBAE1BDB9"); + + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload(msg)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.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); + } + finally + { + fuzzer.close(); + } + } + + /** + * invalid utf8 text message, 1 frame/fragment, 2 valid code points + 1 partial code point + */ + @Test + public void testCase6_6_6() throws Exception + { + byte invalid[] = Hex.asByteArray("CEBAE1BDB9CF"); + + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload(invalid)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); + } + finally + { + fuzzer.close(); + } + } + + /** + * valid utf8 text message, 1 frame/fragment, 3 valid code points (preserved on echo). + */ + @Test + public void testCase6_6_7() throws Exception + { + byte msg[] = Hex.asByteArray("CEBAE1BDB9CF83"); + + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload(msg)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.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); + } + finally + { + fuzzer.close(); + } + } + + /** + * invalid utf8 text message, 1 frame/fragment, 3 valid code points + 1 partial code point + */ + @Test + public void testCase6_6_8() throws Exception + { + byte invalid[] = Hex.asByteArray("CEBAE1BDB9CF83CE"); + + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload(invalid)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame()); + + Fuzzer fuzzer = new Fuzzer(this); + try + { + fuzzer.connect(); + fuzzer.setSendMode(Fuzzer.SendMode.BULK); + fuzzer.send(send); + fuzzer.expect(expect); + } + finally + { + fuzzer.close(); + } + } + + /** + * valid utf8 text message, 1 frame/fragment, 4 valid code points (preserved on echo). + */ + @Test + public void testCase6_6_9() throws Exception + { + byte msg[] = Hex.asByteArray("CEBAE1BDB9CF83CEBC"); + + List send = new ArrayList<>(); + send.add(new WebSocketFrame(OpCode.TEXT).setPayload(msg)); + send.add(new CloseInfo(StatusCode.NORMAL).asFrame()); + + List expect = new ArrayList<>(); + expect.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); + } + finally + { + fuzzer.close(); + } + } +} diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/blockhead/BlockheadClient.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/blockhead/BlockheadClient.java index 759a22bae29..dbfa1befbe8 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/blockhead/BlockheadClient.java +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/blockhead/BlockheadClient.java @@ -550,6 +550,14 @@ public class BlockheadClient implements IncomingFrames, OutgoingFrames BufferUtil.writeTo(buf,out); } + public void writeRaw(ByteBuffer buf, int numBytes) throws IOException + { + int len = Math.min(numBytes,buf.remaining()); + byte arr[] = new byte[len]; + buf.get(arr,0,len); + out.write(arr); + } + public void writeRaw(String str) throws IOException { LOG.debug("write((String)[{}]){}{})",str.length(),'\n',str); @@ -558,20 +566,9 @@ public class BlockheadClient implements IncomingFrames, OutgoingFrames public void writeRawSlowly(ByteBuffer buf, int segmentSize) throws IOException { - int origLimit = buf.limit(); - int limit = buf.limit(); - int len; - int pos = buf.position(); - int overallLeft = buf.remaining(); - while (overallLeft > 0) + while (buf.remaining() > 0) { - buf.position(pos); - limit = Math.min(origLimit,pos + segmentSize); - buf.limit(limit); - len = buf.remaining(); - overallLeft -= len; - pos += len; - writeRaw(buf); + writeRaw(buf,segmentSize); flush(); } } diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/helper/Hex.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/helper/Hex.java new file mode 100644 index 00000000000..0978f53835f --- /dev/null +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/helper/Hex.java @@ -0,0 +1,60 @@ +//======================================================================== +//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.helper; + +public final class Hex +{ + private static final char[] hexcodes = "0123456789ABCDEF".toCharArray(); + + public static byte[] asByteArray(String hstr) + { + if ((hstr.length() < 0) || ((hstr.length() % 2) != 0)) + { + throw new IllegalArgumentException(String.format("Invalid string length of <%d>",hstr.length())); + } + + int size = hstr.length() / 2; + byte buf[] = new byte[size]; + byte hex; + int len = hstr.length(); + + int idx = (int)Math.floor(((size * 2) - (double)len) / 2); + for (int i = 0; i < len; i++) + { + hex = 0; + if (i >= 0) + { + hex = (byte)(Character.digit(hstr.charAt(i),16) << 4); + } + i++; + hex += (byte)(Character.digit(hstr.charAt(i),16)); + + buf[idx] = hex; + idx++; + } + + return buf; + } + + public static String asHex(byte buf[]) + { + int len = buf.length; + char out[] = new char[len * 2]; + for (int i = 0; i < len; i++) + { + out[i * 2] = hexcodes[(buf[i] & 0xF0) >> 4]; + out[(i * 2) + 1] = hexcodes[(buf[i] & 0x0F)]; + } + return String.valueOf(out); + } +}