From 47f882e6dc356a843d66230d99bb7fcb0458504c Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 27 Jul 2012 09:41:26 -0700 Subject: [PATCH] Fixing various things around generate/parse of windowed fragments. + @WebSocket annotation's policy configuraiton is now optional + WebSocketPolicy.clonePolicy() fixed for other fields + WebSocketEventDriver now has internal WebSocket POJO specific logger to allow for logging control of the WebSocket POJO itself in case of runtime exceptions. + WebSocketEventDriver now honors bufferSize correctly. + DataFrameBytes always requests windowSize from generator, allowing generator to determine ultimate byteBuffer utilization itself. + MessageInputStream / MessageReader now clears the starting buffer + Generator now honors windowSize correctly (even if buffer obtained from ByteBufferPool.acquire() is much larger + Parser now demasks the payload after a successful parse of the framing + Various testing cleanup to produce less noisy output during testing. --- .../AllTests (websocket-core).launch | 3 + .../websocket/annotations/WebSocket.java | 8 +-- .../jetty/websocket/api/WebSocketPolicy.java | 26 +++---- .../driver/WebSocketEventDriver.java | 40 ++++++++--- .../jetty/websocket/io/DataFrameBytes.java | 10 +-- .../websocket/io/MessageInputStream.java | 3 + .../jetty/websocket/io/MessageReader.java | 3 + .../jetty/websocket/protocol/Generator.java | 21 +++--- .../jetty/websocket/protocol/Parser.java | 49 ++++--------- .../extensions/DeflateFrameExtensionTest.java | 4 -- .../websocket/io/LocalWebSocketSession.java | 9 +++ .../websocket/protocol/GeneratorTest.java | 29 +++++--- .../protocol/RFC6455ExamplesParserTest.java | 11 ++- .../test/resources/jetty-logging.properties | 2 +- .../AllTests (websocket-server).launch | 3 + .../server/WebSocketServletRFCTest.java | 71 +------------------ .../jetty/websocket/server/ab/ABServlet.java | 24 +++++++ .../jetty/websocket/server/ab/ABSocket.java | 60 ++++++++++++++++ .../websocket/server/ab/AbstractABCase.java | 3 +- .../websocket/server/helper/RFCServlet.java | 14 ++++ .../websocket/server/helper/RFCSocket.java | 63 ++++++++++++++++ .../test/resources/jetty-logging.properties | 4 +- 22 files changed, 290 insertions(+), 170 deletions(-) create mode 100644 jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/ABServlet.java create mode 100644 jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/ABSocket.java create mode 100644 jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/helper/RFCServlet.java create mode 100644 jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/helper/RFCSocket.java diff --git a/jetty-websocket/websocket-core/AllTests (websocket-core).launch b/jetty-websocket/websocket-core/AllTests (websocket-core).launch index e8c853d79ab..126e38af855 100644 --- a/jetty-websocket/websocket-core/AllTests (websocket-core).launch +++ b/jetty-websocket/websocket-core/AllTests (websocket-core).launch @@ -6,6 +6,9 @@ + + + diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/annotations/WebSocket.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/annotations/WebSocket.java index c4dd0b5f612..aced62f7518 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/annotations/WebSocket.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/annotations/WebSocket.java @@ -30,11 +30,11 @@ import java.lang.annotation.Target; { ElementType.TYPE }) public @interface WebSocket { - int maxBinarySize() default 8192; + int maxBinarySize() default -2; - int maxBufferSize() default 8192; + int maxBufferSize() default -2; - int maxIdleTime() default 300000; + int maxIdleTime() default -2; - int maxTextSize() default 8192; + int maxTextSize() default -2; } diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/api/WebSocketPolicy.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/api/WebSocketPolicy.java index 208aafd4c0e..48712dbcab2 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/api/WebSocketPolicy.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/api/WebSocketPolicy.java @@ -128,10 +128,12 @@ public class WebSocketPolicy public WebSocketPolicy clonePolicy() { WebSocketPolicy clone = new WebSocketPolicy(this.behavior); - clone.bufferSize = this.bufferSize; + clone.autoFragment = this.autoFragment; clone.masker = this.masker; - clone.maxBinaryMessageSize = this.maxBinaryMessageSize; clone.idleTimeout = this.idleTimeout; + clone.bufferSize = this.bufferSize; + clone.maxPayloadSize = this.maxPayloadSize; + clone.maxBinaryMessageSize = this.maxBinaryMessageSize; clone.maxTextMessageSize = this.maxTextMessageSize; return clone; } @@ -146,6 +148,11 @@ public class WebSocketPolicy return bufferSize; } + public int getIdleTimeout() + { + return idleTimeout; + } + public Masker getMasker() { return masker; @@ -156,11 +163,6 @@ public class WebSocketPolicy return maxBinaryMessageSize; } - public int getIdleTimeout() - { - return idleTimeout; - } - public int getMaxPayloadSize() { return maxPayloadSize; @@ -186,6 +188,11 @@ public class WebSocketPolicy this.bufferSize = bufferSize; } + public void setIdleTimeout(int idleTimeout) + { + this.idleTimeout = idleTimeout; + } + public void setMasker(Masker masker) { this.masker = masker; @@ -196,11 +203,6 @@ public class WebSocketPolicy this.maxBinaryMessageSize = maxBinaryMessageSize; } - public void setIdleTimeout(int idleTimeout) - { - this.idleTimeout = idleTimeout; - } - public void setMaxPayloadSize(int maxPayloadSize) { if (maxPayloadSize < bufferSize) 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 dc22e130aee..89cd01d03d3 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 @@ -54,6 +54,7 @@ import org.eclipse.jetty.websocket.protocol.WebSocketFrame; public class WebSocketEventDriver implements IncomingFrames { private static final Logger LOG = Log.getLogger(WebSocketEventDriver.class); + private final Logger socketLog; private final Object websocket; private final WebSocketPolicy policy; private final EventMethods events; @@ -74,29 +75,48 @@ public class WebSocketEventDriver implements IncomingFrames this.events = methodsCache.getMethods(websocket.getClass()); this.bufferPool = bufferPool; + this.socketLog = Log.getLogger(websocket.getClass()); + if (events.isAnnotated()) { WebSocket anno = websocket.getClass().getAnnotation(WebSocket.class); // Setup the policy - policy.setBufferSize(anno.maxBufferSize()); - policy.setMaxBinaryMessageSize(anno.maxBinarySize()); - policy.setMaxTextMessageSize(anno.maxTextSize()); - policy.setIdleTimeout(anno.maxIdleTime()); + if (anno.maxBufferSize() > 0) + { + this.policy.setBufferSize(anno.maxBufferSize()); + } + if (anno.maxBinarySize() > 0) + { + this.policy.setMaxBinaryMessageSize(anno.maxBinarySize()); + } + if (anno.maxTextSize() > 0) + { + this.policy.setMaxTextMessageSize(anno.maxTextSize()); + } + if (anno.maxIdleTime() > 0) + { + this.policy.setIdleTimeout(anno.maxIdleTime()); + } } } - private void appendBuffer(ByteBuffer msgBuf, ByteBuffer byteBuffer) + private void appendBuffer(ByteBuffer msgBuf, ByteBuffer payloadBuf) { - if (byteBuffer == null) + if (payloadBuf == null) { // nothing to do (empty payload is possible) return; } - if (msgBuf.remaining() < byteBuffer.remaining()) + if (msgBuf.remaining() < payloadBuf.remaining()) { - throw new MessageTooLargeException("Message exceeded maximum buffer"); + if (LOG.isDebugEnabled()) + { + LOG.debug(" msgBuf = {}",BufferUtil.toDetailString(msgBuf)); + LOG.debug("payloadBuf = {}",BufferUtil.toDetailString(msgBuf)); + } + throw new MessageTooLargeException("Message exceeded maximum buffer size of [" + payloadBuf.capacity() + "]"); } - msgBuf.put(byteBuffer); + msgBuf.put(payloadBuf); } public WebSocketPolicy getPolicy() @@ -389,7 +409,7 @@ public class WebSocketEventDriver implements IncomingFrames private void unhandled(Throwable t) { - LOG.warn("Unhandled Error (closing connection)",t); + socketLog.warn("Unhandled Error (closing connection)",t); // Unhandled Error, close the connection. switch (policy.getBehavior()) diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/io/DataFrameBytes.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/io/DataFrameBytes.java index 57f7e49d31d..eaabfb4aaa9 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/io/DataFrameBytes.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/io/DataFrameBytes.java @@ -62,15 +62,9 @@ public class DataFrameBytes extends FrameBytes try { int windowSize = connection.getPolicy().getBufferSize(); - // TODO: create a window size? - size = frame.getPayloadLength(); - if (size > windowSize) - { - size = windowSize; - } - - buffer = connection.getGenerator().generate(size,frame); + // TODO: windowSize should adjust according to some sort of flow control rules. + buffer = connection.getGenerator().generate(windowSize,frame); return buffer; } catch (Throwable x) diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/io/MessageInputStream.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/io/MessageInputStream.java index f344e74d619..0bec1613dc6 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/io/MessageInputStream.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/io/MessageInputStream.java @@ -19,6 +19,8 @@ import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; +import org.eclipse.jetty.util.BufferUtil; + /** * Support class for reading binary message data as an InputStream. */ @@ -28,6 +30,7 @@ public class MessageInputStream extends InputStream implements StreamAppender public MessageInputStream(ByteBuffer buf) { + BufferUtil.clearToFill(buf); this.buffer = buf; } diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/io/MessageReader.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/io/MessageReader.java index 6bbdccbd1aa..7587e2d540d 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/io/MessageReader.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/io/MessageReader.java @@ -19,6 +19,8 @@ import java.io.IOException; import java.io.Reader; import java.nio.ByteBuffer; +import org.eclipse.jetty.util.BufferUtil; + /** * Support class for reading text message data as an Reader. *

@@ -30,6 +32,7 @@ public class MessageReader extends Reader implements StreamAppender public MessageReader(ByteBuffer buf) { + BufferUtil.clearToFill(buf); this.buffer = buf; } 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 e0822576e65..e57d921bb6b 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 @@ -160,15 +160,17 @@ public class Generator } - /* - * The generate method needs to perform two functions. - * - * 1 - on the initial call for a given frame it needs to generate the framing bytecode and as much of the payload as will fit in the given buffer size - * - * 2 - on subsequent calls it needs to return as much of the payload as will fit in the given buffer size + /** + * Generate, into a ByteBuffer, no more than bufferSize of contents from the frame. If the frame exceeds the bufferSize, then multiple calls to + * {@link #generate(int, WebSocketFrame)} are required to obtain each window of ByteBuffer to complete the frame. */ - public ByteBuffer generate(int bufferSize, WebSocketFrame frame) + public ByteBuffer generate(int windowSize, WebSocketFrame frame) { + if (windowSize < OVERHEAD) + { + throw new IllegalArgumentException("Cannot have windowSize less than " + OVERHEAD); + } + if (LOG.isDebugEnabled()) { StringBuilder dbg = new StringBuilder(); @@ -192,8 +194,11 @@ public class Generator /* * prepare the byte buffer to put frame into */ - ByteBuffer buffer = bufferPool.acquire(bufferSize + OVERHEAD,true); + ByteBuffer buffer = bufferPool.acquire(windowSize,true); BufferUtil.clearToFill(buffer); + // since the buffer from the pool can exceed the window size, artificially + // limit the buffer to the window size. + buffer.limit(buffer.position() + windowSize); if (frame.remaining() == frame.getPayloadLength()) { diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/protocol/Parser.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/protocol/Parser.java index d728dd7c709..4de04141cda 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/protocol/Parser.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/protocol/Parser.java @@ -95,41 +95,6 @@ public class Parser } } - /** - * Copy the bytes from one buffer to the other, demasking the content if necessary. - * - * @param src - * the source {@link ByteBuffer} - * @param dest - * the destination {@link ByteBuffer} - * @param length - * the length of bytes to worry about - * @return the number of bytes copied - */ - protected int copyBuffer(ByteBuffer src, ByteBuffer dest, int length) - { - int amt = Math.min(length,src.remaining()); - if (frame.isMasked()) - { - // Demask the content 1 byte at a time - // FIXME: on partially parsed frames this needs an offset from prior parse - byte mask[] = frame.getMask(); - for (int i = 0; i < amt; i++) - { - dest.put((byte)(src.get() ^ mask[i % 4])); - } - } - else - { - // Copy the content as-is - // TODO: Look into having a BufferUtil.put(from,to,len) method - byte b[] = new byte[amt]; - src.get(b,0,amt); - dest.put(b,0,amt); - } - return amt; - } - public IncomingFrames getIncomingFramesHandler() { return incomingFramesHandler; @@ -441,11 +406,23 @@ public class Parser payload = ByteBuffer.allocate(payloadLength); } - copyBuffer(buffer,payload,payload.remaining()); + BufferUtil.put(buffer,payload); if (payload.position() >= payloadLength) { BufferUtil.flipToFlush(payload,0); + + // demask (if needed) + if (frame.isMasked()) + { + byte mask[] = frame.getMask(); + int end = payload.limit(); + for (int i = payload.position(); i < end; i++) + { + payload.put(i,(byte)(payload.get(i) ^ mask[i % 4])); + } + } + frame.setPayload(payload); this.payload = null; return true; diff --git a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/extensions/DeflateFrameExtensionTest.java b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/extensions/DeflateFrameExtensionTest.java index 28e32f10e96..90ba98fe304 100644 --- a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/extensions/DeflateFrameExtensionTest.java +++ b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/extensions/DeflateFrameExtensionTest.java @@ -391,10 +391,6 @@ public class DeflateFrameExtensionTest ByteBuffer compressed = actual.getPayload().slice(); ByteBuffer uncompressed = ext.inflate(compressed); - System.err.printf("Expected : %s%n",BufferUtil.toDetailString(expected)); - System.err.printf("Compressed : %s%n",BufferUtil.toDetailString(compressed)); - System.err.printf("Uncompressed: %s%n",BufferUtil.toDetailString(uncompressed)); - Assert.assertThat(prefix + ".payloadLength",uncompressed.remaining(),is(expected.remaining())); ByteBufferAssert.assertEquals(prefix + ".payload",expected,uncompressed); } diff --git a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/io/LocalWebSocketSession.java b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/io/LocalWebSocketSession.java index 3282c790fa2..266d23eebe7 100644 --- a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/io/LocalWebSocketSession.java +++ b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/io/LocalWebSocketSession.java @@ -2,15 +2,19 @@ package org.eclipse.jetty.websocket.io; import org.eclipse.jetty.websocket.api.WebSocketPolicy; import org.eclipse.jetty.websocket.driver.WebSocketEventDriver; +import org.eclipse.jetty.websocket.protocol.OutgoingFramesCapture; import org.junit.rules.TestName; public class LocalWebSocketSession extends WebSocketSession { private String id; + private OutgoingFramesCapture outgoingCapture; public LocalWebSocketSession(TestName testname) { this(testname,null); + outgoingCapture = new OutgoingFramesCapture(); + setOutgoing(outgoingCapture); } public LocalWebSocketSession(TestName testname, WebSocketEventDriver driver) @@ -19,6 +23,11 @@ public class LocalWebSocketSession extends WebSocketSession this.id = testname.getMethodName(); } + public OutgoingFramesCapture getOutgoingCapture() + { + return outgoingCapture; + } + @Override public String toString() { diff --git a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/protocol/GeneratorTest.java b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/protocol/GeneratorTest.java index e854ff92d83..57f3360709d 100644 --- a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/protocol/GeneratorTest.java +++ b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/protocol/GeneratorTest.java @@ -12,29 +12,37 @@ import org.junit.Test; public class GeneratorTest { + /** + * Test the windowed generate of a frame that has no masking. + */ @Test public void testWindowedGenerate() { + // A decent sized frame, no masking byte payload[] = new byte[10240]; Arrays.fill(payload,(byte)0x44); WebSocketFrame frame = WebSocketFrame.binary(payload); + // tracking values int totalParts = 0; int totalBytes = 0; int windowSize = 1024; int expectedHeaderSize = 4; int expectedParts = (int)Math.ceil((double)(payload.length + expectedHeaderSize) / windowSize); + // the generator Generator generator = new UnitGenerator(); + // lets see how many parts the generator makes boolean done = false; while (!done) { - Assert.assertThat("Too many parts",totalParts,lessThan(20)); + // sanity check in loop, our test should fail if this is true. + Assert.assertThat("Too many parts",totalParts,lessThanOrEqualTo(expectedParts)); ByteBuffer buf = generator.generate(windowSize,frame); - // System.out.printf("Generated buf.limit() = %,d%n",buf.limit()); + Assert.assertThat("Generated should not exceed window size",buf.remaining(),lessThanOrEqualTo(windowSize)); totalBytes += buf.remaining(); totalParts++; @@ -42,6 +50,7 @@ public class GeneratorTest done = (frame.remaining() <= 0); } + // validate Assert.assertThat("Created Parts",totalParts,is(expectedParts)); Assert.assertThat("Created Bytes",totalBytes,is(payload.length + expectedHeaderSize)); } @@ -49,6 +58,7 @@ public class GeneratorTest @Test public void testWindowedGenerateWithMasking() { + // A decent sized frame, with masking byte payload[] = new byte[10240]; Arrays.fill(payload,(byte)0x55); @@ -56,15 +66,17 @@ public class GeneratorTest { 0x2A, (byte)0xF0, 0x0F, 0x00 }; WebSocketFrame frame = WebSocketFrame.binary(payload); - frame.setMask(mask); + frame.setMask(mask); // masking! + // tracking values int totalParts = 0; int totalBytes = 0; - int windowSize = 2929; // important, use an odd # window size to test masking across window barriers + int windowSize = 2929; // important for test, use an odd # window size to test masking across window barriers int expectedHeaderSize = 8; int expectedParts = (int)Math.ceil((double)(payload.length + expectedHeaderSize) / windowSize); - // Buffer to capture generated bytes + // Buffer to capture generated bytes (we do this to validate that the masking + // is working correctly ByteBuffer completeBuf = ByteBuffer.allocate(payload.length + expectedHeaderSize); BufferUtil.clearToFill(completeBuf); @@ -74,10 +86,11 @@ public class GeneratorTest boolean done = false; while (!done) { - Assert.assertThat("Too many parts",totalParts,lessThan(20)); + // sanity check in loop, our test should fail if this is true. + Assert.assertThat("Too many parts",totalParts,lessThanOrEqualTo(expectedParts)); ByteBuffer buf = generator.generate(windowSize,frame); - // System.out.printf("Generated buf.limit() = %,d%n",buf.limit()); + Assert.assertThat("Generated should not exceed window size",buf.remaining(),lessThanOrEqualTo(windowSize)); totalBytes += buf.remaining(); totalParts++; @@ -104,7 +117,7 @@ public class GeneratorTest Assert.assertThat("Frame.opcode",actual.getOpCode(),is(OpCode.BINARY)); Assert.assertThat("Frame.payloadLength",actual.getPayloadLength(),is(payload.length)); - // Validate payload content for proper masking + // Validate payload contents for proper masking ByteBuffer actualData = actual.getPayload().slice(); Assert.assertThat("Frame.payload.remaining",actualData.remaining(),is(payload.length)); while (actualData.remaining() > 0) diff --git a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/protocol/RFC6455ExamplesParserTest.java b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/protocol/RFC6455ExamplesParserTest.java index e335822a86f..4d5a6859fff 100644 --- a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/protocol/RFC6455ExamplesParserTest.java +++ b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/protocol/RFC6455ExamplesParserTest.java @@ -19,11 +19,9 @@ import static org.hamcrest.Matchers.*; import java.nio.ByteBuffer; +import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.websocket.api.WebSocketBehavior; import org.eclipse.jetty.websocket.api.WebSocketPolicy; -import org.eclipse.jetty.websocket.protocol.OpCode; -import org.eclipse.jetty.websocket.protocol.Parser; -import org.eclipse.jetty.websocket.protocol.WebSocketFrame; import org.junit.Assert; import org.junit.Test; @@ -41,23 +39,24 @@ public class RFC6455ExamplesParserTest parser.setIncomingFramesHandler(capture); ByteBuffer buf = ByteBuffer.allocate(16); + BufferUtil.clearToFill(buf); // Raw bytes as found in RFC 6455, Section 5.7 - Examples // A fragmented unmasked text message (part 1 of 2 "Hel") buf.put(new byte[] { (byte)0x01, (byte)0x03, 0x48, (byte)0x65, 0x6c }); - buf.flip(); // Parse #1 + BufferUtil.flipToFlush(buf,0); parser.parse(buf); // part 2 of 2 "lo" (A continuation frame of the prior text message) - buf.flip(); + BufferUtil.flipToFill(buf); buf.put(new byte[] { (byte)0x80, 0x02, 0x6c, 0x6f }); - buf.flip(); // Parse #2 + BufferUtil.flipToFlush(buf,0); parser.parse(buf); capture.assertNoErrors(); diff --git a/jetty-websocket/websocket-core/src/test/resources/jetty-logging.properties b/jetty-websocket/websocket-core/src/test/resources/jetty-logging.properties index 9e3703a01fc..fbf69445511 100644 --- a/jetty-websocket/websocket-core/src/test/resources/jetty-logging.properties +++ b/jetty-websocket/websocket-core/src/test/resources/jetty-logging.properties @@ -1,2 +1,2 @@ org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog -org.eclipse.jetty.websocket.LEVEL=DEBUG \ No newline at end of file +org.eclipse.jetty.websocket.LEVEL=WARN \ No newline at end of file diff --git a/jetty-websocket/websocket-server/AllTests (websocket-server).launch b/jetty-websocket/websocket-server/AllTests (websocket-server).launch index e964382257e..d74c4679248 100644 --- a/jetty-websocket/websocket-server/AllTests (websocket-server).launch +++ b/jetty-websocket/websocket-server/AllTests (websocket-server).launch @@ -6,6 +6,9 @@ + + + diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServletRFCTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServletRFCTest.java index 8829d0e8653..afcbc8ac476 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServletRFCTest.java +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketServletRFCTest.java @@ -17,28 +17,21 @@ package org.eclipse.jetty.websocket.server; import static org.hamcrest.Matchers.*; -import java.io.IOException; import java.net.SocketException; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Queue; import java.util.concurrent.TimeUnit; -import org.eclipse.jetty.util.FutureCallback; import org.eclipse.jetty.util.Utf8Appendable.NotUtf8Exception; import org.eclipse.jetty.util.Utf8StringBuilder; -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.util.log.Logger; -import org.eclipse.jetty.websocket.annotations.OnWebSocketConnect; -import org.eclipse.jetty.websocket.annotations.OnWebSocketMessage; -import org.eclipse.jetty.websocket.annotations.WebSocket; import org.eclipse.jetty.websocket.api.StatusCode; -import org.eclipse.jetty.websocket.api.WebSocketConnection; 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.blockhead.BlockheadClient; +import org.eclipse.jetty.websocket.server.helper.RFCServlet; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; @@ -52,68 +45,6 @@ import org.junit.Test; */ public class WebSocketServletRFCTest { - @SuppressWarnings("serial") - public static class RFCServlet extends WebSocketServlet - { - @Override - public void registerWebSockets(WebSocketServerFactory factory) - { - factory.register(RFCSocket.class); - } - } - - @WebSocket - public static class RFCSocket - { - private static Logger LOG = Log.getLogger(RFCSocket.class); - - private WebSocketConnection conn; - - @OnWebSocketMessage - public void onBinary(byte buf[], int offset, int len) - { - LOG.debug("onBinary(byte[{}],{},{})",buf.length,offset,len); - - // echo the message back. - try - { - this.conn.write(null,new FutureCallback(),buf,offset,len); - } - catch (IOException e) - { - e.printStackTrace(System.err); - } - } - - @OnWebSocketConnect - public void onOpen(WebSocketConnection conn) - { - this.conn = conn; - } - - @OnWebSocketMessage - public void onText(String message) - { - LOG.debug("onText({})",message); - // Test the RFC 6455 close code 1011 that should close - // trigger a WebSocket server terminated close. - if (message.equals("CRASH")) - { - throw new RuntimeException("Something bad happened"); - } - - // echo the message back. - try - { - this.conn.write(null,new FutureCallback(),message); - } - catch (IOException e) - { - e.printStackTrace(System.err); - } - } - } - private static Generator generator = new UnitGenerator(); private static SimpleServletServer server; diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/ABServlet.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/ABServlet.java new file mode 100644 index 00000000000..dec50ed7bac --- /dev/null +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/ABServlet.java @@ -0,0 +1,24 @@ +package org.eclipse.jetty.websocket.server.ab; + +import org.eclipse.jetty.websocket.server.WebSocketServerFactory; +import org.eclipse.jetty.websocket.server.WebSocketServlet; + +/** + * Servlet with bigger message policy sizes, with registered simple echo socket. + */ +@SuppressWarnings("serial") +public class ABServlet extends WebSocketServlet +{ + private static final int KBYTE = 1024; + private static final int MBYTE = KBYTE * KBYTE; + + @Override + public void registerWebSockets(WebSocketServerFactory factory) + { + factory.register(ABSocket.class); + + factory.getPolicy().setBufferSize(2 * MBYTE); + factory.getPolicy().setMaxTextMessageSize(2 * MBYTE); + factory.getPolicy().setMaxBinaryMessageSize(2 * MBYTE); + } +} diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/ABSocket.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/ABSocket.java new file mode 100644 index 00000000000..94652702287 --- /dev/null +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/ABSocket.java @@ -0,0 +1,60 @@ +package org.eclipse.jetty.websocket.server.ab; + +import java.io.IOException; + +import org.eclipse.jetty.util.FutureCallback; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.websocket.annotations.OnWebSocketConnect; +import org.eclipse.jetty.websocket.annotations.OnWebSocketMessage; +import org.eclipse.jetty.websocket.annotations.WebSocket; +import org.eclipse.jetty.websocket.api.WebSocketConnection; + +/** + * Simple Echo WebSocket, using async writes of echo + */ +@WebSocket +public class ABSocket +{ + private static Logger LOG = Log.getLogger(ABSocket.class); + + private WebSocketConnection conn; + + @OnWebSocketMessage + public void onBinary(byte buf[], int offset, int len) + { + LOG.debug("onBinary(byte[{}],{},{})",buf.length,offset,len); + + // echo the message back. + try + { + this.conn.write(null,new FutureCallback(),buf,offset,len); + } + catch (IOException e) + { + e.printStackTrace(System.err); + } + } + + @OnWebSocketConnect + public void onOpen(WebSocketConnection conn) + { + this.conn = conn; + } + + @OnWebSocketMessage + public void onText(String message) + { + LOG.debug("onText({})",message); + + // echo the message back. + try + { + this.conn.write(null,new FutureCallback(),message); + } + catch (IOException e) + { + e.printStackTrace(System.err); + } + } +} 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 0e184bf4554..ead17962a47 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 @@ -3,7 +3,6 @@ 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; @@ -20,7 +19,7 @@ public abstract class AbstractABCase @BeforeClass public static void startServer() throws Exception { - server = new SimpleServletServer(new MyEchoServlet()); + server = new SimpleServletServer(new ABServlet()); server.start(); } diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/helper/RFCServlet.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/helper/RFCServlet.java new file mode 100644 index 00000000000..268229f486b --- /dev/null +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/helper/RFCServlet.java @@ -0,0 +1,14 @@ +package org.eclipse.jetty.websocket.server.helper; + +import org.eclipse.jetty.websocket.server.WebSocketServerFactory; +import org.eclipse.jetty.websocket.server.WebSocketServlet; + +@SuppressWarnings("serial") +public class RFCServlet extends WebSocketServlet +{ + @Override + public void registerWebSockets(WebSocketServerFactory factory) + { + factory.register(RFCSocket.class); + } +} \ No newline at end of file diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/helper/RFCSocket.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/helper/RFCSocket.java new file mode 100644 index 00000000000..15f01704e72 --- /dev/null +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/helper/RFCSocket.java @@ -0,0 +1,63 @@ +package org.eclipse.jetty.websocket.server.helper; + +import java.io.IOException; + +import org.eclipse.jetty.util.FutureCallback; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.websocket.annotations.OnWebSocketConnect; +import org.eclipse.jetty.websocket.annotations.OnWebSocketMessage; +import org.eclipse.jetty.websocket.annotations.WebSocket; +import org.eclipse.jetty.websocket.api.WebSocketConnection; + +@WebSocket +public class RFCSocket +{ + private static Logger LOG = Log.getLogger(RFCSocket.class); + + private WebSocketConnection conn; + + @OnWebSocketMessage + public void onBinary(byte buf[], int offset, int len) + { + LOG.debug("onBinary(byte[{}],{},{})",buf.length,offset,len); + + // echo the message back. + try + { + this.conn.write(null,new FutureCallback(),buf,offset,len); + } + catch (IOException e) + { + e.printStackTrace(System.err); + } + } + + @OnWebSocketConnect + public void onOpen(WebSocketConnection conn) + { + this.conn = conn; + } + + @OnWebSocketMessage + public void onText(String message) + { + LOG.debug("onText({})",message); + // Test the RFC 6455 close code 1011 that should close + // trigger a WebSocket server terminated close. + if (message.equals("CRASH")) + { + throw new RuntimeException("Something bad happened"); + } + + // echo the message back. + try + { + this.conn.write(null,new FutureCallback(),message); + } + catch (IOException e) + { + e.printStackTrace(System.err); + } + } +} \ No newline at end of file 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 a0a48233a82..f64a66e3c3c 100644 --- a/jetty-websocket/websocket-server/src/test/resources/jetty-logging.properties +++ b/jetty-websocket/websocket-server/src/test/resources/jetty-logging.properties @@ -1,8 +1,10 @@ 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.websocket.server.helper.RFCSocket.LEVEL=OFF +# org.eclipse.jetty.util.thread.QueuedThreadPool.LEVEL=DEBUG # org.eclipse.jetty.io.SelectorManager.LEVEL=INFO -org.eclipse.jetty.websocket.LEVEL=DEBUG +# 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