diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/DeflateCompressionMethod.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/DeflateCompressionMethod.java index de8f1d96a32..8b8ef610a39 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/DeflateCompressionMethod.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/DeflateCompressionMethod.java @@ -24,6 +24,7 @@ import java.util.zip.Deflater; import java.util.zip.Inflater; import org.eclipse.jetty.util.BufferUtil; +import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.websocket.api.BadPayloadException; @@ -125,6 +126,9 @@ public class DeflateCompressionMethod implements CompressionMethod private static class InflaterProcess implements CompressionMethod.Process { + /** Tail Bytes per Spec */ + private static final byte[] TAIL = new byte[] + { 0x00, 0x00, (byte)0xFF, (byte)0xFF }; private final Inflater inflater; private int bufferSize = DEFAULT_BUFFER_SIZE; @@ -150,11 +154,16 @@ public class DeflateCompressionMethod implements CompressionMethod if (LOG.isDebugEnabled()) { LOG.debug("inflate: {}",BufferUtil.toDetailString(input)); + LOG.debug("Input Data: {}",TypeUtil.toHexString(BufferUtil.toArray(input))); } - // Set the data that is compressed to the inflater - byte compressed[] = BufferUtil.toArray(input); - inflater.setInput(compressed,0,compressed.length); + // Set the data that is compressed (+ TAIL) to the inflater + int len = input.remaining() + 4; + byte raw[] = new byte[len]; + int inlen = input.remaining(); + input.slice().get(raw,0,inlen); + System.arraycopy(TAIL,0,raw,inlen,TAIL.length); + inflater.setInput(raw,0,raw.length); } @Override diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/FrameCompressionExtension.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/FrameCompressionExtension.java index 7dcea705831..526897c4c65 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/FrameCompressionExtension.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/FrameCompressionExtension.java @@ -65,7 +65,7 @@ public class FrameCompressionExtension extends AbstractExtension } // reset on every frame. - method.decompress().end(); + // method.decompress().end(); } finally { diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/AllTests.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/AllTests.java index 6d57d828816..9a32a8d0fde 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/AllTests.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/AllTests.java @@ -19,15 +19,15 @@ package org.eclipse.jetty.websocket.common.extensions; import org.eclipse.jetty.websocket.common.extensions.compress.DeflateCompressionMethodTest; -import org.eclipse.jetty.websocket.common.extensions.compress.PerMessageCompressionExtensionTest; -import org.eclipse.jetty.websocket.common.extensions.compress.WebkitDeflateFrameExtensionTest; +import org.eclipse.jetty.websocket.common.extensions.compress.MessageCompressionExtensionTest; +import org.eclipse.jetty.websocket.common.extensions.compress.FrameCompressionExtensionTest; import org.junit.runner.RunWith; import org.junit.runners.Suite; @RunWith(Suite.class) @Suite.SuiteClasses( - { ExtensionStackTest.class, DeflateCompressionMethodTest.class, PerMessageCompressionExtensionTest.class, FragmentExtensionTest.class, - IdentityExtensionTest.class, WebkitDeflateFrameExtensionTest.class }) + { ExtensionStackTest.class, DeflateCompressionMethodTest.class, MessageCompressionExtensionTest.class, FragmentExtensionTest.class, + IdentityExtensionTest.class, FrameCompressionExtensionTest.class }) public class AllTests { /* nothing to do here, its all done in the annotations */ diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/DeflateCompressionMethodTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/DeflateCompressionMethodTest.java index 591ef352cf9..743d65806ce 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/DeflateCompressionMethodTest.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/DeflateCompressionMethodTest.java @@ -26,10 +26,9 @@ import java.util.List; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; -import org.eclipse.jetty.websocket.common.extensions.compress.CompressionMethod; -import org.eclipse.jetty.websocket.common.extensions.compress.DeflateCompressionMethod; import org.junit.Assert; import org.junit.Test; @@ -76,6 +75,50 @@ public class DeflateCompressionMethodTest Assert.assertEquals("Message Contents",expected,actual); } + /** + * Test decompression with 2 buffers. First buffer is normal, second relies on back buffers created from first. + */ + @Test + public void testFollowupBackDistance() + { + // The Sample (Compressed) Data + byte buf1[] = TypeUtil.fromHexString("2aC9Cc4dB50200"); // DEFLATE -> "time:" + byte buf2[] = TypeUtil.fromHexString("2a01110000"); // DEFLATE -> "time:" + + // Setup Compression Method + CompressionMethod method = new DeflateCompressionMethod(); + + // Decompressed Data Holder + ByteBuffer decompressed = ByteBuffer.allocate(32); + BufferUtil.flipToFill(decompressed); + + // Perform Decompress on Buf 1 + BufferUtil.clearToFill(decompressed); + // IGNORE method.decompress().begin(); + method.decompress().input(ByteBuffer.wrap(buf1)); + while (!method.decompress().isDone()) + { + ByteBuffer window = method.decompress().process(); + BufferUtil.put(window,decompressed); + } + BufferUtil.flipToFlush(decompressed,0); + LOG.debug("decompressed[1]: {}",BufferUtil.toDetailString(decompressed)); + // IGNORE method.decompress().end(); + + // Perform Decompress on Buf 2 + BufferUtil.clearToFill(decompressed); + // IGNORE method.decompress().begin(); + method.decompress().input(ByteBuffer.wrap(buf2)); + while (!method.decompress().isDone()) + { + ByteBuffer window = method.decompress().process(); + BufferUtil.put(window,decompressed); + } + BufferUtil.flipToFlush(decompressed,0); + LOG.debug("decompressed[2]: {}",BufferUtil.toDetailString(decompressed)); + // IGNORE method.decompress().end(); + } + /** * Test a large payload (a payload length over 65535 bytes). * diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/WebkitDeflateFrameExtensionTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/FrameCompressionExtensionTest.java similarity index 94% rename from jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/WebkitDeflateFrameExtensionTest.java rename to jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/FrameCompressionExtensionTest.java index 97bd530aa72..2f813c36f72 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/WebkitDeflateFrameExtensionTest.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/FrameCompressionExtensionTest.java @@ -44,7 +44,7 @@ import org.eclipse.jetty.websocket.common.WebSocketFrame; import org.junit.Assert; import org.junit.Test; -public class WebkitDeflateFrameExtensionTest +public class FrameCompressionExtensionTest { private void assertIncoming(byte[] raw, String... expectedTextDatas) { @@ -130,6 +130,16 @@ public class WebkitDeflateFrameExtensionTest assertIncoming(rawbuf,"info:"); } + @Test + public void testChrome20_TimeTime() + { + // Captured from Chrome 20.x - "time:" then "time:" once more (sent from browser/client) + String time1 = "c1 87 82 46 74 24 a8 8f b8 69 37 44 74".replaceAll("\\s*",""); + String time2 = "c1 85 3c fd a1 7f 16 fc b0 7f 3c".replaceAll("\\s*",""); + byte rawbuf[] = TypeUtil.fromHexString(time1 + time2); + assertIncoming(rawbuf,"time:","time:"); + } + @Test public void testDeflateBasics() throws Exception { diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/PerMessageCompressionExtensionTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/MessageCompressionExtensionTest.java similarity index 99% rename from jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/PerMessageCompressionExtensionTest.java rename to jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/MessageCompressionExtensionTest.java index be08b18b742..d9a35994345 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/PerMessageCompressionExtensionTest.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/MessageCompressionExtensionTest.java @@ -41,7 +41,7 @@ import org.eclipse.jetty.websocket.common.WebSocketFrame; import org.junit.Assert; import org.junit.Test; -public class PerMessageCompressionExtensionTest +public class MessageCompressionExtensionTest { private void assertDraftExample(String hexStr, String expectedStr) { diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java index cb8cfcceb81..eac604de601 100644 --- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java +++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java @@ -445,11 +445,6 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc throw new IOException("Unable to start Extension Stack",e); } - if (LOG.isDebugEnabled()) - { - LOG.debug("{}",extensionStack.dump()); - } - // Tell jetty about the new connection request.setAttribute(HttpConnection.UPGRADE_CONNECTION_ATTRIBUTE,connection); diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/AllTests.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/AllTests.java index d3c2384f015..78700b5d0fd 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/AllTests.java +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/AllTests.java @@ -23,7 +23,7 @@ import org.junit.runners.Suite; @RunWith(Suite.class) @Suite.SuiteClasses( -{ org.eclipse.jetty.websocket.server.ab.AllTests.class, ChromeTest.class, DeflateExtensionTest.class, FragmentExtensionTest.class, IdentityExtensionTest.class, +{ org.eclipse.jetty.websocket.server.ab.AllTests.class, ChromeTest.class, FrameCompressionExtensionTest.class, FragmentExtensionTest.class, IdentityExtensionTest.class, LoadTest.class, WebSocketInvalidVersionTest.class, WebSocketLoadRFC6455Test.class, WebSocketOverSSLTest.class, WebSocketServletRFCTest.class }) public class AllTests { diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/DeflateExtensionTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FrameCompressionExtensionTest.java similarity index 85% rename from jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/DeflateExtensionTest.java rename to jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FrameCompressionExtensionTest.java index ad90bb8317d..f378ee55dff 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/DeflateExtensionTest.java +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/FrameCompressionExtensionTest.java @@ -31,7 +31,7 @@ import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; -public class DeflateExtensionTest +public class FrameCompressionExtensionTest { private static SimpleServletServer server; @@ -68,12 +68,21 @@ public class DeflateExtensionTest String msg = "Hello"; - // Client sends message. + // Client sends first message client.write(WebSocketFrame.text(msg)); IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,1000); WebSocketFrame frame = capture.getFrames().get(0); Assert.assertThat("TEXT.payload",frame.getPayloadAsUTF8(),is(msg.toString())); + + // Client sends second message + client.clearCaptured(); + msg = "There"; + client.write(WebSocketFrame.text(msg)); + + capture = client.readFrames(1,TimeUnit.MILLISECONDS,1000); + frame = capture.getFrames().get(0); + Assert.assertThat("TEXT.payload",frame.getPayloadAsUTF8(),is(msg.toString())); } finally { 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 0b91d3261c3..43aa1953e03 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 @@ -139,6 +139,11 @@ public class BlockheadClient implements IncomingFrames, OutgoingFrames this.extensions.add(xtension); } + public void clearCaptured() + { + this.incomingFrames.clear(); + } + public void clearExtensions() { extensions.clear(); diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/helper/IncomingFramesCapture.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/helper/IncomingFramesCapture.java index 16219c5f12a..d9d4aa81c80 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/helper/IncomingFramesCapture.java +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/helper/IncomingFramesCapture.java @@ -73,6 +73,11 @@ public class IncomingFramesCapture implements IncomingFrames Assert.assertThat("Has no errors",errors.size(),is(0)); } + public void clear() + { + frames.clear(); + } + public void dump() { System.err.printf("Captured %d incoming frames%n",frames.size());