diff --git a/jetty-websocket/pom.xml b/jetty-websocket/pom.xml
index 0501bf303f6..4dd8817c856 100644
--- a/jetty-websocket/pom.xml
+++ b/jetty-websocket/pom.xml
@@ -14,7 +14,7 @@
websocket-core
-
+ websocket-client
websocket-server
diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/frames/FrameBuilder.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/frames/FrameBuilder.java
new file mode 100644
index 00000000000..01a7c81d7de
--- /dev/null
+++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/frames/FrameBuilder.java
@@ -0,0 +1,234 @@
+package org.eclipse.jetty.websocket.frames;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.websocket.api.PolicyViolationException;
+import org.eclipse.jetty.websocket.protocol.OpCode;
+
+
+public class FrameBuilder
+{
+ public static FrameBuilder binaryFrame()
+ {
+ return new FrameBuilder(new BaseFrame(OpCode.BINARY));
+ }
+
+ public static FrameBuilder closeFrame()
+ {
+ return new FrameBuilder(new BaseFrame(OpCode.CLOSE));
+ }
+
+ public static FrameBuilder continuationFrame()
+ {
+ return new FrameBuilder(new BaseFrame(OpCode.CONTINUATION));
+ }
+
+ public static FrameBuilder pingFrame()
+ {
+ return new FrameBuilder(new BaseFrame(OpCode.PING));
+ }
+
+ public static FrameBuilder pongFrame()
+ {
+ return new FrameBuilder(new BaseFrame(OpCode.PONG));
+ }
+
+ public static FrameBuilder textFrame()
+ {
+ return new FrameBuilder(new BaseFrame(OpCode.TEXT));
+ }
+
+ private BaseFrame frame;
+
+ public FrameBuilder(BaseFrame frame)
+ {
+ this.frame = frame;
+ this.frame.setFin(true); // default
+ }
+
+ public byte[] asByteArray()
+ {
+ return BufferUtil.toArray(asByteBuffer());
+ }
+
+ public ByteBuffer asByteBuffer()
+ {
+ ByteBuffer buffer = ByteBuffer.allocate(frame.getPayloadLength() + 32 );
+ byte b;
+
+ // Setup fin thru opcode
+ b = 0x00;
+ if (frame.isFin())
+ {
+ b |= 0x80; // 1000_0000
+ }
+ if (frame.isRsv1())
+ {
+ b |= 0x40; // 0100_0000
+ // TODO: extensions can negotiate this (somehow)
+ throw new PolicyViolationException("RSV1 not allowed to be set");
+ }
+ if (frame.isRsv2())
+ {
+ b |= 0x20; // 0010_0000
+ // TODO: extensions can negotiate this (somehow)
+ throw new PolicyViolationException("RSV2 not allowed to be set");
+ }
+ if (frame.isRsv3())
+ {
+ b |= 0x10;
+ // TODO: extensions can negotiate this (somehow)
+ throw new PolicyViolationException("RSV3 not allowed to be set");
+ }
+
+ byte opcode = frame.getOpCode().getCode();
+
+ if (frame.isContinuation())
+ {
+ // Continuations are not the same OPCODE
+ opcode = OpCode.CONTINUATION.getCode();
+ }
+
+ b |= opcode & 0x0F;
+
+ buffer.put(b);
+
+ // is masked
+ b = 0x00;
+ b |= (frame.isMasked()?0x80:0x00);
+
+ // payload lengths
+ int payloadLength = frame.getPayloadLength();
+
+ /*
+ * if length is over 65535 then its a 7 + 64 bit length
+ */
+ if (payloadLength > 0xFF_FF)
+ {
+ // we have a 64 bit length
+ b |= 0x7F;
+ buffer.put(b); // indicate 8 byte length
+ buffer.put((byte)0); //
+ buffer.put((byte)0); // anything over an
+ buffer.put((byte)0); // int is just
+ buffer.put((byte)0); // intsane!
+ buffer.put((byte)((payloadLength >> 24) & 0xFF));
+ buffer.put((byte)((payloadLength >> 16) & 0xFF));
+ buffer.put((byte)((payloadLength >> 8) & 0xFF));
+ buffer.put((byte)(payloadLength & 0xFF));
+ }
+ /*
+ * if payload is ge 126 we have a 7 + 16 bit length
+ */
+ else if (payloadLength >= 0x7E)
+ {
+ b |= 0x7E;
+ buffer.put(b); // indicate 2 byte length
+ buffer.put((byte)(payloadLength >> 8));
+ buffer.put((byte)(payloadLength & 0xFF));
+ }
+ /*
+ * we have a 7 bit length
+ */
+ else
+ {
+ b |= (payloadLength & 0x7F);
+ buffer.put(b);
+ }
+
+ // masking key
+ if (frame.isMasked())
+ {
+ // TODO: figure out maskgen
+ buffer.put(frame.getMask());
+ }
+
+ // now the payload itself
+
+ // call back into masking check/method on this class?
+
+ // remember the position
+ int positionPrePayload = buffer.position();
+
+ // generate payload
+ if (frame.getPayloadLength() > 0)
+ {
+ BufferUtil.put(frame.getPayload(),buffer);
+ }
+
+ int positionPostPayload = buffer.position();
+
+ // mask it if needed
+ if (frame.isMasked())
+ {
+ // move back to remembered position.
+ int size = positionPostPayload - positionPrePayload;
+ byte[] mask = frame.getMask();
+ int pos;
+ for (int i = 0; i < size; i++)
+ {
+ pos = positionPrePayload + i;
+ // Mask each byte by its absolute position in the bytebuffer
+ buffer.put(pos,(byte)(buffer.get(pos) ^ mask[i % 4]));
+ }
+ }
+
+ BufferUtil.flipToFlush(buffer,0);
+
+ return buffer;
+ }
+
+ public BaseFrame asFrame()
+ {
+ return frame;
+ }
+
+ public FrameBuilder isFin( boolean fin )
+ {
+ frame.setFin(fin);
+
+ return this;
+ }
+
+ public FrameBuilder isRsv1(boolean rsv1)
+ {
+ frame.setRsv1(rsv1);
+
+ return this;
+ }
+
+ public FrameBuilder isRsv2(boolean rsv2)
+ {
+ frame.setRsv2(rsv2);
+
+ return this;
+ }
+
+ public FrameBuilder isRsv3(boolean rsv3)
+ {
+ frame.setRsv3(rsv3);
+
+ return this;
+ }
+
+ public FrameBuilder withMask(byte[] mask)
+ {
+ frame.setMasked(true);
+ frame.setMask(mask);
+
+ return this;
+ }
+
+ public FrameBuilder withPayload(byte[] bytes)
+ {
+ frame.setPayload(bytes);
+ return this;
+ }
+
+ public FrameBuilder withPayload(ByteBuffer payload)
+ {
+ frame.setPayload(payload);
+ return this;
+ }
+}
diff --git a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/frames/FrameBuilderTest.java b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/frames/FrameBuilderTest.java
new file mode 100644
index 00000000000..f283ca18535
--- /dev/null
+++ b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/frames/FrameBuilderTest.java
@@ -0,0 +1,58 @@
+package org.eclipse.jetty.websocket.frames;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class FrameBuilderTest
+{
+ public void testSimpleAsFrame()
+ {
+ PingFrame frame = (PingFrame)FrameBuilder.pingFrame().asFrame();
+
+ Assert.assertTrue(frame instanceof PingFrame);
+ }
+
+ @Test
+ public void testSimpleInvalidCloseFrameBuilder()
+ {
+ byte[] actual = FrameBuilder.closeFrame().isFin(false).asByteArray();
+
+ byte[] expected = new byte[]
+ { (byte)0x08, (byte)0x00 };
+
+ Assert.assertArrayEquals(expected,actual);
+ }
+
+ @Test
+ public void testSimpleInvalidPingFrameBuilder()
+ {
+ byte[] actual = FrameBuilder.pingFrame().isFin(false).asByteArray();
+
+ byte[] expected = new byte[]
+ { (byte)0x09, (byte)0x00 };
+
+ Assert.assertArrayEquals(expected,actual);
+ }
+
+ @Test
+ public void testSimpleValidCloseFrame()
+ {
+ byte[] actual = FrameBuilder.closeFrame().asByteArray();
+
+ byte[] expected = new byte[]
+ { (byte)0x88, (byte)0x00 };
+
+ Assert.assertArrayEquals(expected,actual);
+ }
+
+ @Test
+ public void testSimpleValidPingFrame()
+ {
+ byte[] actual = FrameBuilder.pingFrame().asByteArray();
+
+ byte[] expected = new byte[]
+ { (byte)0x89, (byte)0x00 };
+
+ Assert.assertArrayEquals(expected,actual);
+ }
+}
diff --git a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/WebSocketGeneratorRFC6455Test.java b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/generator/WebSocketGeneratorRFC6455Test.java
similarity index 99%
rename from jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/WebSocketGeneratorRFC6455Test.java
rename to jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/generator/WebSocketGeneratorRFC6455Test.java
index 40d64dc9fb0..106b66264bb 100644
--- a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/WebSocketGeneratorRFC6455Test.java
+++ b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/generator/WebSocketGeneratorRFC6455Test.java
@@ -13,7 +13,7 @@
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
-package org.eclipse.jetty.websocket;
+package org.eclipse.jetty.websocket.generator;
/**
*/
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 b61175165f6..2b096f7c739 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
@@ -13,13 +13,16 @@ import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.websocket.ByteBufferAssert;
+import org.eclipse.jetty.websocket.protocol.OpCode;
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
import org.eclipse.jetty.websocket.frames.BaseFrame;
import org.eclipse.jetty.websocket.frames.CloseFrame;
+import org.eclipse.jetty.websocket.frames.FrameBuilder;
import org.eclipse.jetty.websocket.frames.PingFrame;
+import org.eclipse.jetty.websocket.frames.PongFrame;
import org.eclipse.jetty.websocket.frames.TextFrame;
import org.eclipse.jetty.websocket.generator.FrameGenerator;
-import org.eclipse.jetty.websocket.protocol.OpCode;
import org.eclipse.jetty.websocket.server.SimpleServletServer;
import org.eclipse.jetty.websocket.server.WebSocketServerFactory;
import org.eclipse.jetty.websocket.server.WebSocketServlet;
@@ -63,14 +66,14 @@ public class TestABCase5
}
// echo the message back.
- try
- {
- getConnection().write(message);
- }
- catch (IOException e)
- {
- e.printStackTrace(System.err);
- }
+ //try
+ // {
+ // getConnection().write(message);
+ // }
+ //catch (IOException e)
+ // {
+ // e.printStackTrace(System.err);
+ // }
}
}
@@ -128,10 +131,46 @@ public class TestABCase5
client.writeRaw(buf2);
- // Read frame (hopefully text frame)
+ // Read frame
Queue frames = client.readFrames(1,TimeUnit.MILLISECONDS,500);
- CloseFrame closeFrame = (CloseFrame)frames.remove();
- Assert.assertThat("CloseFrame.status code",closeFrame.getStatusCode(),is(1002));
+ BaseFrame frame = (BaseFrame)frames.remove();
+
+ Assert.assertTrue("frame should be close frame", frame instanceof CloseFrame);
+
+ Assert.assertThat("CloseFrame.status code",((CloseFrame)frame).getStatusCode(),is(1002));
+ }
+ finally
+ {
+ client.close();
+ }
+ }
+
+ @Test
+ public void testCase5_1PingIn2PacketsWithBuilder() throws Exception
+ {
+ BlockheadClient client = new BlockheadClient(server.getServerUri());
+ try
+ {
+ client.connect();
+ client.sendStandardRequest();
+ client.expectUpgradeResponse();
+
+ String fragment1 = "fragment1";
+ ByteBuffer frame1 = FrameBuilder.pingFrame().isFin(false).withPayload(fragment1.getBytes()).asByteBuffer();
+
+ client.writeRaw(frame1);
+
+ String fragment2 = "fragment2";
+ ByteBuffer frame2 = FrameBuilder.pingFrame().withPayload(fragment2.getBytes()).asByteBuffer();
+ client.writeRaw(frame2);
+
+ // Read frame
+ Queue frames = client.readFrames(1,TimeUnit.MILLISECONDS,500);
+ BaseFrame frame = (BaseFrame)frames.remove();
+
+ Assert.assertTrue("frame should be close frame", frame instanceof CloseFrame);
+
+ Assert.assertThat("CloseFrame.status code",((CloseFrame)frame).getStatusCode(),is(1002));
}
finally
{
@@ -178,10 +217,13 @@ public class TestABCase5
client.writeRaw(buf2);
- // Read frame (hopefully text frame)
+ // Read frame
Queue frames = client.readFrames(1,TimeUnit.MILLISECONDS,500);
- CloseFrame closeFrame = (CloseFrame)frames.remove();
- Assert.assertThat("CloseFrame.status code",closeFrame.getStatusCode(),is(1002));
+ BaseFrame frame = (BaseFrame)frames.remove();
+
+ Assert.assertTrue("frame should be close frame", frame instanceof CloseFrame);
+
+ Assert.assertThat("CloseFrame.status code",((CloseFrame)frame).getStatusCode(),is(1002));
}
finally
{
@@ -190,9 +232,45 @@ public class TestABCase5
}
+ @Test
+ public void testCase5_2PongIn2PacketsWithBuilder() throws Exception
+ {
+ BlockheadClient client = new BlockheadClient(server.getServerUri());
+ try
+ {
+ client.connect();
+ client.sendStandardRequest();
+ client.expectUpgradeResponse();
+
+ String fragment1 = "fragment1";
+
+ ByteBuffer frame1 = FrameBuilder.pongFrame().isFin(false).withPayload(fragment1.getBytes()).asByteBuffer();
+
+ client.writeRaw(frame1);
+
+ String fragment2 = "fragment2";
+
+ ByteBuffer frame2 = FrameBuilder.continuationFrame().isFin(false).withPayload(fragment2.getBytes()).asByteBuffer();
+
+ client.writeRaw(frame2);
+
+ // Read frame
+ Queue frames = client.readFrames(1,TimeUnit.MILLISECONDS,500);
+ BaseFrame frame = (BaseFrame)frames.remove();
+
+ Assert.assertTrue("frame should be close frame", frame instanceof CloseFrame);
+
+ Assert.assertThat("CloseFrame.status code",((CloseFrame)frame).getStatusCode(),is(1002));
+ }
+ finally
+ {
+ client.close();
+ }
+ }
+
@Test
- @Ignore ("not re-assembling the strings yet on server side echo")
+ @Ignore ("not supported in implementation yet, requires server side message aggregation")
public void testCase5_3TextIn2Packets() throws Exception
{
BlockheadClient client = new BlockheadClient(server.getServerUri());
@@ -231,10 +309,204 @@ public class TestABCase5
client.writeRaw(buf2);
- // Read frame (hopefully text frame)
+ // Read frame
Queue frames = client.readFrames(1,TimeUnit.MILLISECONDS,500);
- TextFrame textFrame = (TextFrame)frames.remove();
- Assert.assertThat("TextFrame.payload",textFrame.getPayloadUTF8(),is(fragment1 + fragment2));
+ BaseFrame frame = (BaseFrame)frames.remove();
+
+ Assert.assertTrue("frame should be text frame", frame instanceof TextFrame);
+
+ Assert.assertThat("TextFrame.payload",((TextFrame)frame).getPayloadUTF8(),is(fragment1 + fragment2));
+ }
+ finally
+ {
+ client.close();
+ }
+ }
+
+ @Test
+ @Ignore ("not supported in implementation yet, requires server side message aggregation")
+ public void testCase5_6TextPingRemainingText() throws Exception
+ {
+ BlockheadClient client = new BlockheadClient(server.getServerUri());
+ try
+ {
+ client.connect();
+ client.sendStandardRequest();
+ client.expectUpgradeResponse();
+
+ // Send a text packet
+
+ ByteBuffer buf = ByteBuffer.allocate(FrameGenerator.OVERHEAD + 2);
+ BufferUtil.clearToFill(buf);
+
+ String fragment1 = "fragment1";
+
+ buf.put((byte)(0x00 | OpCode.TEXT.getCode()));
+
+ 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(FrameGenerator.OVERHEAD + 2);
+ BufferUtil.clearToFill(pingBuf);
+
+ String pingPayload = "ping payload";
+
+ pingBuf.put((byte)(0x00 | OpCode.PING.getCode()));
+
+ b = 0x00; // no masking
+ b |= pingPayload.length() & 0x7F;
+ pingBuf.put(b);
+ pingBuf.put(pingPayload.getBytes());
+ BufferUtil.flipToFlush(pingBuf,0);
+
+ client.writeRaw(buf);
+
+ // Send remaining text as continuation
+
+ ByteBuffer buf2 = ByteBuffer.allocate(FrameGenerator.OVERHEAD + 2);
+ BufferUtil.clearToFill(buf2);
+
+ String fragment2 = "fragment2";
+
+ buf2.put((byte)(0x80 | OpCode.CONTINUATION.getCode()));
+ 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
+ Queue frames = client.readFrames(2,TimeUnit.MILLISECONDS,500);
+ BaseFrame frame = frames.remove();
+
+ Assert.assertTrue("first frame should be pong frame", frame instanceof PongFrame );
+
+ ByteBuffer payload1 = ByteBuffer.allocate(pingPayload.length());
+ payload1.flip();
+
+ ByteBufferAssert.assertEquals("payloads should be equal" , payload1, ((PongFrame)frame).getPayload() );
+
+ frame = (BaseFrame)frames.remove();
+
+ Assert.assertTrue("second frame should be text frame", frame instanceof TextFrame );
+
+
+ Assert.assertThat("TextFrame.payload",((TextFrame)frame).getPayloadUTF8(),is(fragment1 + fragment2));
+ }
+ finally
+ {
+ client.close();
+ }
+ }
+
+
+ @Test
+ @Ignore ("not supported in implementation yet, requires server side message aggregation")
+ public void testCase5_6TextPingRemainingTextWithBuilder() throws Exception
+ {
+ BlockheadClient client = new BlockheadClient(server.getServerUri());
+ try
+ {
+ client.connect();
+ client.sendStandardRequest();
+ client.expectUpgradeResponse();
+
+ // Send a text packet
+ String textPayload1 = "fragment1";
+
+ ByteBuffer frame1 = FrameBuilder.textFrame().isFin(false).withPayload(textPayload1.getBytes()).asByteBuffer();
+ BufferUtil.flipToFlush(frame1,0);
+ client.writeRaw(frame1);
+
+ // Send a ping with payload
+
+ String pingPayload = "ping payload";
+ ByteBuffer frame2 = FrameBuilder.pingFrame().withPayload(pingPayload.getBytes()).asByteBuffer();
+ BufferUtil.flipToFlush(frame2,0);
+
+ client.writeRaw(frame2);
+
+ // Send remaining text as continuation
+ String textPayload2 = "fragment2";
+
+ ByteBuffer frame3 = FrameBuilder.continuationFrame().withPayload(textPayload2.getBytes()).asByteBuffer();
+ BufferUtil.flipToFlush(frame3,0);
+
+ client.writeRaw(frame3);
+
+ // Should be 2 frames, pong frame followed by combined echo'd text frame
+ Queue frames = client.readFrames(2,TimeUnit.MILLISECONDS,500);
+ BaseFrame frame = frames.remove();
+
+ Assert.assertTrue("first frame should be pong frame", frame instanceof PongFrame );
+
+ ByteBuffer payload1 = ByteBuffer.allocate(pingPayload.length());
+ payload1.flip();
+
+ ByteBufferAssert.assertEquals("payloads should be equal" , payload1, ((PongFrame)frame).getPayload() );
+
+ frame = (BaseFrame)frames.remove();
+
+ Assert.assertTrue("second frame should be text frame", frame instanceof TextFrame );
+
+
+ Assert.assertThat("TextFrame.payload",((TextFrame)frame).getPayloadUTF8(),is(textPayload1 + textPayload2));
+ }
+ finally
+ {
+ client.close();
+ }
+ }
+
+ @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
+ {
+ BlockheadClient client = new BlockheadClient(server.getServerUri());
+ try
+ {
+ client.connect();
+ client.sendStandardRequest();
+ client.expectUpgradeResponse();
+
+ // Send a text packet
+
+ ByteBuffer buf = ByteBuffer.allocate(FrameGenerator.OVERHEAD + 2);
+ BufferUtil.clearToFill(buf);
+
+ String fragment1 = "fragment";
+
+ // continutation w / FIN
+
+ buf.put((byte)(0x80 | OpCode.CONTINUATION.getCode()));
+
+ 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
+ Queue frames = client.readFrames(1,TimeUnit.MILLISECONDS,500);
+ BaseFrame frame = (BaseFrame)frames.remove();
+
+ Assert.assertTrue("frame should be close frame", frame instanceof CloseFrame);
+
+ Assert.assertThat("CloseFrame.status code",((CloseFrame)frame).getStatusCode(),is(1002));
+
+ Assert.assertThat("CloseFrame.reason", ((CloseFrame)frame).getReason(),is("Bad Continuation") ); // TODO put close reasons into public strings in impl someplace
+
}
finally
{
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
index 8d1282c8608..4e867b827a4 100644
--- 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
@@ -16,6 +16,7 @@ import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
import org.eclipse.jetty.websocket.frames.BaseFrame;
import org.eclipse.jetty.websocket.frames.CloseFrame;
+import org.eclipse.jetty.websocket.frames.FrameBuilder;
import org.eclipse.jetty.websocket.generator.FrameGenerator;
import org.eclipse.jetty.websocket.protocol.OpCode;
import org.eclipse.jetty.websocket.server.SimpleServletServer;
@@ -168,4 +169,46 @@ public class TestABCase7_9
}
}
+ /**
+ * Test the requirement of issuing
+ */
+ @Test
+ public void testCase7_9_XInvalidCloseStatusCodesWithBuilder() throws Exception
+ {
+ BlockheadClient client = new BlockheadClient(server.getServerUri());
+ try
+ {
+ client.connect();
+ client.sendStandardRequest();
+ client.expectUpgradeResponse();
+
+ ByteBuffer frame = FrameBuilder.closeFrame().withMask(new byte[]
+ { 0x44, 0x44, 0x44, 0x44 }).asByteBuffer();
+
+ ByteBuffer buf = ByteBuffer.allocate(FrameGenerator.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.getCode()));
+ 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)
+ Queue frames = client.readFrames(1,TimeUnit.MILLISECONDS,500);
+ CloseFrame closeFrame = (CloseFrame)frames.remove();
+ Assert.assertThat("CloseFrame.status code",closeFrame.getStatusCode(),is(1002));
+ }
+ finally
+ {
+ client.close();
+ }
+ }
+
}