From cbcb76eaea63e0f9f9ba0ef7ee8f506f03bb5181 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 26 Jul 2012 15:45:54 -0700 Subject: [PATCH] More spec tests from server point of view --- .../driver/WebSocketEventDriver.java | 13 +- .../jetty/websocket/protocol/Generator.java | 2 +- .../websocket/protocol/WebSocketFrame.java | 5 +- .../websocket/server/ByteBufferAssert.java | 6 + .../websocket/server/ab/AbstractABCase.java | 87 +++++ .../jetty/websocket/server/ab/AllTests.java | 2 +- .../websocket/server/ab/TestABCase1.java | 311 ++++++++++++++++++ .../test/resources/jetty-logging.properties | 2 +- 8 files changed, 420 insertions(+), 8 deletions(-) create mode 100644 jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/AbstractABCase.java create mode 100644 jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase1.java diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/driver/WebSocketEventDriver.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/driver/WebSocketEventDriver.java index cb3c28e6876..dc22e130aee 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/driver/WebSocketEventDriver.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/driver/WebSocketEventDriver.java @@ -64,7 +64,7 @@ public class WebSocketEventDriver implements IncomingFrames /** * Establish the driver for the Websocket POJO - * + * * @param websocket */ public WebSocketEventDriver(Object websocket, EventMethodsCache methodsCache, WebSocketPolicy policy, ByteBufferPool bufferPool) @@ -87,6 +87,11 @@ public class WebSocketEventDriver implements IncomingFrames private void appendBuffer(ByteBuffer msgBuf, ByteBuffer byteBuffer) { + if (byteBuffer == null) + { + // nothing to do (empty payload is possible) + return; + } if (msgBuf.remaining() < byteBuffer.remaining()) { throw new MessageTooLargeException("Message exceeded maximum buffer"); @@ -101,7 +106,7 @@ public class WebSocketEventDriver implements IncomingFrames /** * Get the Websocket POJO in use - * + * * @return the Websocket POJO */ public Object getWebSocketObject() @@ -131,7 +136,7 @@ public class WebSocketEventDriver implements IncomingFrames /** * Internal entry point for incoming frames - * + * * @param frame * the frame that appeared */ @@ -351,7 +356,7 @@ public class WebSocketEventDriver implements IncomingFrames /** * Set the connection to use for this driver - * + * * @param conn * the connection */ diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/protocol/Generator.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/protocol/Generator.java index c7a09493a02..e0822576e65 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/protocol/Generator.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/protocol/Generator.java @@ -192,7 +192,7 @@ public class Generator /* * prepare the byte buffer to put frame into */ - ByteBuffer buffer = bufferPool.acquire(bufferSize,true); + ByteBuffer buffer = bufferPool.acquire(bufferSize + OVERHEAD,true); BufferUtil.clearToFill(buffer); if (frame.remaining() == frame.getPayloadLength()) diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/protocol/WebSocketFrame.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/protocol/WebSocketFrame.java index 7a1e7edbb92..9594dd1e754 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/protocol/WebSocketFrame.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/protocol/WebSocketFrame.java @@ -141,7 +141,10 @@ public class WebSocketFrame implements Frame } payloadLength = copy.payloadLength; payloadStart = copy.payloadStart; - data = copy.data.slice(); + if (copy.data != null) // deal with empty payloads + { + data = copy.data.slice(); + } continuationIndex = copy.continuationIndex; continuation = copy.continuation; } diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ByteBufferAssert.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ByteBufferAssert.java index f2aa021f1e2..b6afbcf6317 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ByteBufferAssert.java +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ByteBufferAssert.java @@ -34,6 +34,12 @@ public class ByteBufferAssert } } + public static void assertEquals(String message, byte[] expectedBytes, ByteBuffer actualBuffer) + { + byte actualBytes[] = BufferUtil.toArray(actualBuffer); + assertEquals(message,expectedBytes,actualBytes); + } + public static void assertEquals(String message, ByteBuffer expectedBuffer, ByteBuffer actualBuffer) { byte expectedBytes[] = BufferUtil.toArray(expectedBuffer); 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 new file mode 100644 index 00000000000..0e184bf4554 --- /dev/null +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/AbstractABCase.java @@ -0,0 +1,87 @@ +package org.eclipse.jetty.websocket.server.ab; + +import java.nio.ByteBuffer; + +import org.eclipse.jetty.websocket.server.SimpleServletServer; +import org.eclipse.jetty.websocket.server.examples.MyEchoServlet; +import org.junit.AfterClass; +import org.junit.BeforeClass; + +public abstract class AbstractABCase +{ + protected static final byte FIN = (byte)0x80; + protected static final byte NOFIN = 0x00; + private static final byte MASKED_BIT = (byte)0x80; + private static final byte[] MASK = + { 0x12, 0x34, 0x56, 0x78 }; + + protected static SimpleServletServer server; + + @BeforeClass + public static void startServer() throws Exception + { + server = new SimpleServletServer(new MyEchoServlet()); + server.start(); + } + + @AfterClass + public static void stopServer() + { + server.stop(); + } + + protected byte[] masked(final byte[] data) + { + int len = data.length; + byte ret[] = new byte[len]; + System.arraycopy(data,0,ret,0,len); + for (int i = 0; i < len; i++) + { + ret[i] ^= MASK[i % 4]; + } + return ret; + } + + private void putLength(ByteBuffer buf, int length, boolean masked) + { + if (length < 0) + { + throw new IllegalArgumentException("Length cannot be negative"); + } + byte b = (masked?MASKED_BIT:0x00); + + // write the uncompressed length + if (length > 0xFF_FF) + { + buf.put((byte)(b | 0x7F)); + buf.put((byte)0x00); + buf.put((byte)0x00); + buf.put((byte)0x00); + buf.put((byte)0x00); + buf.put((byte)((length >> 24) & 0xFF)); + buf.put((byte)((length >> 16) & 0xFF)); + buf.put((byte)((length >> 8) & 0xFF)); + buf.put((byte)(length & 0xFF)); + } + else if (length >= 0x7E) + { + buf.put((byte)(b | 0x7E)); + buf.put((byte)(length >> 8)); + buf.put((byte)(length & 0xFF)); + } + else + { + buf.put((byte)(b | length)); + } + } + + public void putMask(ByteBuffer buf) + { + buf.put(MASK); + } + + public void putPayloadLength(ByteBuffer buf, int length) + { + putLength(buf,length,true); + } +} 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 d0dcd671412..360af21c9eb 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( -{ TestABCase5.class, TestABCase7_9.class }) +{ TestABCase1.class, TestABCase5.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/TestABCase1.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase1.java new file mode 100644 index 00000000000..0878b265f99 --- /dev/null +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/TestABCase1.java @@ -0,0 +1,311 @@ +// ======================================================================== +// Copyright 2011-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +//======================================================================== +package org.eclipse.jetty.websocket.server.ab; + +import static org.hamcrest.Matchers.*; + +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.Queue; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.util.BufferUtil; +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.blockhead.BlockheadClient; +import org.junit.Assert; +import org.junit.Test; + +public class TestABCase1 extends AbstractABCase +{ + /** + * Echo 0 byte text message + */ + @Test + public void testCase1_1_1() throws Exception + { + BlockheadClient client = new BlockheadClient(server.getServerUri()); + try + { + client.connect(); + client.sendStandardRequest(); + client.expectUpgradeResponse(); + + ByteBuffer buf = ByteBuffer.allocate(16); + BufferUtil.clearToFill(buf); + + buf.put((byte)(0x00 | FIN | OpCode.TEXT.getCode())); + putPayloadLength(buf,0); + putMask(buf); + + BufferUtil.flipToFlush(buf,0); + client.writeRaw(buf); + + // Read frame + Queue frames = client.readFrames(1,TimeUnit.MILLISECONDS,500); + WebSocketFrame frame = frames.remove(); + Assert.assertThat("frame should be TEXT frame",frame.getOpCode(),is(OpCode.TEXT)); + Assert.assertThat("Text.payloadLength",frame.getPayloadLength(),is(0)); + } + finally + { + client.close(); + } + } + + /** + * Echo 125 byte text message (uses small 7-bit payload length) + */ + @Test + public void testCase1_1_2() throws Exception + { + BlockheadClient client = new BlockheadClient(server.getServerUri()); + try + { + client.connect(); + client.sendStandardRequest(); + client.expectUpgradeResponse(); + + byte msg[] = new byte[125]; + Arrays.fill(msg,(byte)'*'); + + ByteBuffer buf = ByteBuffer.allocate(msg.length + Generator.OVERHEAD); + BufferUtil.clearToFill(buf); + + buf.put((byte)(0x00 | FIN | OpCode.TEXT.getCode())); + putPayloadLength(buf,msg.length); + putMask(buf); + buf.put(masked(msg)); + + BufferUtil.flipToFlush(buf,0); + client.writeRaw(buf); + + // Read frame + Queue frames = client.readFrames(1,TimeUnit.MILLISECONDS,500); + WebSocketFrame frame = frames.remove(); + Assert.assertThat("frame should be TEXT frame",frame.getOpCode(),is(OpCode.TEXT)); + Assert.assertThat("Text.payloadLength",frame.getPayloadLength(),is(msg.length)); + ByteBufferAssert.assertEquals("Text.payload",msg,frame.getPayload()); + } + finally + { + client.close(); + } + } + + /** + * Echo 126 byte text message (uses medium 2 byte payload length) + */ + @Test + public void testCase1_1_3() throws Exception + { + BlockheadClient client = new BlockheadClient(server.getServerUri()); + try + { + client.connect(); + client.sendStandardRequest(); + client.expectUpgradeResponse(); + + byte msg[] = new byte[126]; + Arrays.fill(msg,(byte)'*'); + + ByteBuffer buf = ByteBuffer.allocate(msg.length + Generator.OVERHEAD); + BufferUtil.clearToFill(buf); + + buf.put((byte)(0x00 | FIN | OpCode.TEXT.getCode())); + putPayloadLength(buf,msg.length); + putMask(buf); + buf.put(masked(msg)); + + BufferUtil.flipToFlush(buf,0); + client.writeRaw(buf); + + // Read frame + Queue frames = client.readFrames(1,TimeUnit.MILLISECONDS,500); + WebSocketFrame frame = frames.remove(); + Assert.assertThat("frame should be TEXT frame",frame.getOpCode(),is(OpCode.TEXT)); + Assert.assertThat("Text.payloadLength",frame.getPayloadLength(),is(msg.length)); + ByteBufferAssert.assertEquals("Text.payload",msg,frame.getPayload()); + } + finally + { + client.close(); + } + } + + /** + * Echo 127 byte text message (uses medium 2 byte payload length) + */ + @Test + public void testCase1_1_4() throws Exception + { + BlockheadClient client = new BlockheadClient(server.getServerUri()); + try + { + client.connect(); + client.sendStandardRequest(); + client.expectUpgradeResponse(); + + byte msg[] = new byte[127]; + Arrays.fill(msg,(byte)'*'); + + ByteBuffer buf = ByteBuffer.allocate(msg.length + Generator.OVERHEAD); + BufferUtil.clearToFill(buf); + + buf.put((byte)(0x00 | FIN | OpCode.TEXT.getCode())); + putPayloadLength(buf,msg.length); + putMask(buf); + buf.put(masked(msg)); + + BufferUtil.flipToFlush(buf,0); + client.writeRaw(buf); + + // Read frame + Queue frames = client.readFrames(1,TimeUnit.MILLISECONDS,500); + WebSocketFrame frame = frames.remove(); + Assert.assertThat("frame should be TEXT frame",frame.getOpCode(),is(OpCode.TEXT)); + Assert.assertThat("Text.payloadLength",frame.getPayloadLength(),is(msg.length)); + ByteBufferAssert.assertEquals("Text.payload",msg,frame.getPayload()); + } + finally + { + client.close(); + } + } + + /** + * Echo 128 byte text message (uses medium 2 byte payload length) + */ + @Test + public void testCase1_1_5() throws Exception + { + BlockheadClient client = new BlockheadClient(server.getServerUri()); + try + { + client.connect(); + client.sendStandardRequest(); + client.expectUpgradeResponse(); + + byte msg[] = new byte[128]; + Arrays.fill(msg,(byte)'*'); + + ByteBuffer buf = ByteBuffer.allocate(msg.length + Generator.OVERHEAD); + BufferUtil.clearToFill(buf); + + buf.put((byte)(0x00 | FIN | OpCode.TEXT.getCode())); + putPayloadLength(buf,msg.length); + putMask(buf); + buf.put(masked(msg)); + + BufferUtil.flipToFlush(buf,0); + client.writeRaw(buf); + + // Read frame + Queue frames = client.readFrames(1,TimeUnit.MILLISECONDS,500); + WebSocketFrame frame = frames.remove(); + Assert.assertThat("frame should be TEXT frame",frame.getOpCode(),is(OpCode.TEXT)); + Assert.assertThat("Text.payloadLength",frame.getPayloadLength(),is(msg.length)); + ByteBufferAssert.assertEquals("Text.payload",msg,frame.getPayload()); + } + finally + { + client.close(); + } + } + + /** + * Echo 65535 byte text message (uses medium 2 byte payload length) + */ + @Test + public void testCase1_1_6() throws Exception + { + BlockheadClient client = new BlockheadClient(server.getServerUri()); + try + { + client.connect(); + client.sendStandardRequest(); + client.expectUpgradeResponse(); + + byte msg[] = new byte[65535]; + Arrays.fill(msg,(byte)'*'); + + ByteBuffer buf = ByteBuffer.allocate(msg.length + Generator.OVERHEAD); + BufferUtil.clearToFill(buf); + + buf.put((byte)(0x00 | FIN | OpCode.TEXT.getCode())); + putPayloadLength(buf,msg.length); + putMask(buf); + buf.put(masked(msg)); + + BufferUtil.flipToFlush(buf,0); + client.writeRaw(buf); + + // Read frame + Queue frames = client.readFrames(1,TimeUnit.MILLISECONDS,500); + WebSocketFrame frame = frames.remove(); + Assert.assertThat("frame should be TEXT frame",frame.getOpCode(),is(OpCode.TEXT)); + Assert.assertThat("Text.payloadLength",frame.getPayloadLength(),is(msg.length)); + ByteBufferAssert.assertEquals("Text.payload",msg,frame.getPayload()); + } + finally + { + client.close(); + } + } + + /** + * Echo 65536 byte text message (uses large 8 byte payload length) + */ + @Test + public void testCase1_1_7() throws Exception + { + BlockheadClient client = new BlockheadClient(server.getServerUri()); + try + { + client.connect(); + client.sendStandardRequest(); + client.expectUpgradeResponse(); + + byte msg[] = new byte[65536]; + Arrays.fill(msg,(byte)'*'); + + ByteBuffer buf = ByteBuffer.allocate(msg.length + Generator.OVERHEAD); + BufferUtil.clearToFill(buf); + + buf.put((byte)(0x00 | FIN | OpCode.TEXT.getCode())); + putPayloadLength(buf,msg.length); + putMask(buf); + buf.put(masked(msg)); + + BufferUtil.flipToFlush(buf,0); + client.writeRaw(buf); + + // Read frame + Queue frames = client.readFrames(1,TimeUnit.MILLISECONDS,500); + WebSocketFrame frame = frames.remove(); + Assert.assertThat("frame should be TEXT frame",frame.getOpCode(),is(OpCode.TEXT)); + Assert.assertThat("Text.payloadLength",frame.getPayloadLength(),is(msg.length)); + ByteBufferAssert.assertEquals("Text.payload",msg,frame.getPayload()); + } + finally + { + client.close(); + } + } + +} diff --git a/jetty-websocket/websocket-server/src/test/resources/jetty-logging.properties b/jetty-websocket/websocket-server/src/test/resources/jetty-logging.properties index 607bf07594d..a0a48233a82 100644 --- a/jetty-websocket/websocket-server/src/test/resources/jetty-logging.properties +++ b/jetty-websocket/websocket-server/src/test/resources/jetty-logging.properties @@ -2,7 +2,7 @@ org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog org.eclipse.jetty.io.LEVEL=WARN org.eclipse.jetty.server.LEVEL=WARN # org.eclipse.jetty.io.SelectorManager.LEVEL=INFO -org.eclipse.jetty.websocket.LEVEL=WARN +org.eclipse.jetty.websocket.LEVEL=DEBUG # org.eclipse.jetty.websocket.extensions.LEVEL=DEBUG # org.eclipse.jetty.websocket.protocol.Generator.LEVEL=INFO # org.eclipse.jetty.websocket.protocol.Parser.LEVEL=INFO