diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/extensions/Extension.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/extensions/Extension.java index 64561a02bb5..09064ffb480 100644 --- a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/extensions/Extension.java +++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/extensions/Extension.java @@ -81,4 +81,9 @@ public interface Extension extends IncomingFrames, OutgoingFrames * the next outgoing extension */ public void setNextOutgoingFrames(OutgoingFrames nextOutgoing); + + // TODO: Extension should indicate if it requires boundary of fragments to be preserved + + // TODO: Extension should indicate if it uses the Extension data field of frame for its own reasons. + } \ No newline at end of file diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/extensions/ExtensionConfig.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/extensions/ExtensionConfig.java index 384275bf4cb..04c01217de2 100644 --- a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/extensions/ExtensionConfig.java +++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/extensions/ExtensionConfig.java @@ -96,8 +96,12 @@ public class ExtensionConfig { str.append(';'); str.append(param); - str.append('='); - QuoteUtil.quoteIfNeeded(str,parameters.get(param),";="); + String value = parameters.get(param); + if (value != null) + { + str.append('='); + QuoteUtil.quoteIfNeeded(str,value,";="); + } } return str.toString(); } diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/CompressionMethod.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/CompressionMethod.java deleted file mode 100644 index 49c415d2ffd..00000000000 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/CompressionMethod.java +++ /dev/null @@ -1,44 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2013 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.common.extensions.compress; - -import java.nio.ByteBuffer; - -/** - * Compression Method - */ -public interface CompressionMethod -{ - public interface Process - { - public void begin(); - - public void end(); - - public void input(ByteBuffer input); - - public boolean isDone(); - - public ByteBuffer process(); - } - - public Process compress(); - - public Process decompress(); -} 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 deleted file mode 100644 index a989254fb86..00000000000 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/DeflateCompressionMethod.java +++ /dev/null @@ -1,260 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2013 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.common.extensions.compress; - -import java.nio.ByteBuffer; -import java.util.zip.DataFormatException; -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; - -/** - * Deflate Compression Method - */ -public class DeflateCompressionMethod implements CompressionMethod -{ - private static class DeflaterProcess implements CompressionMethod.Process - { - private static final boolean BFINAL_HACK = Boolean.parseBoolean(System.getProperty("jetty.websocket.bfinal.hack","true")); - - private final Deflater deflater; - private int bufferSize = DEFAULT_BUFFER_SIZE; - - public DeflaterProcess(boolean nowrap) - { - deflater = new Deflater(Deflater.BEST_COMPRESSION,nowrap); - deflater.setStrategy(Deflater.DEFAULT_STRATEGY); - } - - @Override - public void begin() - { - deflater.reset(); - } - - @Override - public void end() - { - deflater.reset(); - } - - @Override - public void input(ByteBuffer input) - { - if (LOG.isDebugEnabled()) - { - LOG.debug("input: {}",BufferUtil.toDetailString(input)); - } - - // Set the data that is uncompressed to the deflater - byte raw[] = BufferUtil.toArray(input); - deflater.setInput(raw,0,raw.length); - deflater.finish(); - } - - @Override - public boolean isDone() - { - return deflater.finished(); - } - - @Override - public ByteBuffer process() - { - // prepare the output buffer - ByteBuffer buf = ByteBuffer.allocate(bufferSize); - BufferUtil.clearToFill(buf); - - while (!deflater.finished()) - { - byte out[] = new byte[bufferSize]; - int len = deflater.deflate(out,0,out.length,Deflater.SYNC_FLUSH); - - if (LOG.isDebugEnabled()) - { - LOG.debug("Deflater: finished={}, needsInput={}, len={}",deflater.finished(),deflater.needsInput(),len); - } - - buf.put(out,0,len); - } - BufferUtil.flipToFlush(buf,0); - - if (BFINAL_HACK) - { - /* - * Per the spec, it says that BFINAL 1 or 0 are allowed. - * - * However, Java always uses BFINAL 1, whereas the browsers Chromium and Safari fail to decompress when it encounters BFINAL 1. - * - * This hack will always set BFINAL 0 - */ - byte b0 = buf.get(0); - if ((b0 & 1) != 0) // if BFINAL 1 - { - buf.put(0,(b0 ^= 1)); // flip bit to BFINAL 0 - } - } - return buf; - } - - public void setBufferSize(int bufferSize) - { - this.bufferSize = bufferSize; - } - } - - 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; - - public InflaterProcess(boolean nowrap) { - inflater = new Inflater(nowrap); - } - - @Override - public void begin() - { - inflater.reset(); - } - - @Override - public void end() - { - inflater.reset(); - } - - @Override - public void input(ByteBuffer input) - { - if (LOG.isDebugEnabled()) - { - LOG.debug("inflate: {}",BufferUtil.toDetailString(input)); - LOG.debug("Input Data: {}",TypeUtil.toHexString(BufferUtil.toArray(input))); - } - - // 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 - public boolean isDone() - { - return (inflater.getRemaining() <= 0) || inflater.finished(); - } - - @Override - public ByteBuffer process() - { - // Establish place for inflated data - byte buf[] = new byte[bufferSize]; - try - { - int inflated = inflater.inflate(buf); - if (inflated == 0) - { - return null; - } - - ByteBuffer ret = BufferUtil.toBuffer(buf,0,inflated); - - if (LOG.isDebugEnabled()) - { - LOG.debug("uncompressed={}",BufferUtil.toDetailString(ret)); - } - - return ret; - } - catch (DataFormatException e) - { - LOG.warn(e); - throw new BadPayloadException(e); - } - } - - public void setBufferSize(int bufferSize) - { - this.bufferSize = bufferSize; - } - } - - private static final int DEFAULT_BUFFER_SIZE = 61*1024; - - private static final Logger LOG = Log.getLogger(DeflateCompressionMethod.class); - - private int bufferSize = 64 * 1024; - private final DeflaterProcess compress; - private final InflaterProcess decompress; - - public DeflateCompressionMethod() - { - /* - * Specs specify that head/tail of deflate are not to be present. - * - * So lets not use the wrapped format of bytes. - * - * Setting nowrap to true prevents the Deflater from writing the head/tail bytes and the Inflater from expecting the head/tail bytes. - */ - boolean nowrap = true; - - this.compress = new DeflaterProcess(nowrap); - this.decompress = new InflaterProcess(nowrap); - } - - @Override - public Process compress() - { - return compress; - } - - @Override - public Process decompress() - { - return decompress; - } - - public int getBufferSize() - { - return bufferSize; - } - - public void setBufferSize(int size) - { - if (size < 64) - { - throw new IllegalArgumentException("Buffer Size [" + size + "] cannot be less than 64 bytes"); - } - this.bufferSize = size; - this.compress.setBufferSize(bufferSize); - this.decompress.setBufferSize(bufferSize); - } -} diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/MessageDeflateCompressionExtension.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/MessageDeflateCompressionExtension.java deleted file mode 100644 index 5446be7397b..00000000000 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/MessageDeflateCompressionExtension.java +++ /dev/null @@ -1,142 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2013 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.common.extensions.compress; - -import java.nio.ByteBuffer; - -import org.eclipse.jetty.websocket.api.WriteCallback; -import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig; -import org.eclipse.jetty.websocket.api.extensions.Frame; -import org.eclipse.jetty.websocket.common.OpCode; -import org.eclipse.jetty.websocket.common.extensions.AbstractExtension; -import org.eclipse.jetty.websocket.common.frames.DataFrame; - -/** - * Per Message Deflate Compression extension for WebSocket. - *

- * Attempts to follow draft-ietf-hybi-permessage-compression-01 - */ -public class MessageDeflateCompressionExtension extends AbstractExtension -{ - private CompressionMethod method; - - @Override - public String getName() - { - return "permessage-deflate"; - } - - @Override - public void incomingFrame(Frame frame) - { - if (OpCode.isControlFrame(frame.getOpCode()) || !frame.isRsv1()) - { - // Cannot modify incoming control frames or ones with RSV1 set. - nextIncomingFrame(frame); - return; - } - - ByteBuffer data = frame.getPayload(); - method.decompress().input(data); - while (!method.decompress().isDone()) - { - ByteBuffer uncompressed = method.decompress().process(); - if (uncompressed == null) - { - continue; - } - - DataFrame out = new DataFrame(frame); - out.setPayload(uncompressed); - if (!method.decompress().isDone()) - { - out.setFin(false); - } - out.setRsv1(false); // Unset RSV1 on decompressed frame - nextIncomingFrame(out); - } - - // reset only at the end of a message. - if (frame.isFin()) - { - method.decompress().end(); - } - } - - /** - * Indicates use of RSV1 flag for indicating deflation is in use. - */ - @Override - public boolean isRsv1User() - { - return true; - } - - @Override - public void outgoingFrame(Frame frame, WriteCallback callback) - { - if (OpCode.isControlFrame(frame.getOpCode())) - { - // skip, cannot compress control frames. - nextOutgoingFrame(frame,callback); - return; - } - - ByteBuffer data = frame.getPayload(); - // deflate data - method.compress().input(data); - while (!method.compress().isDone()) - { - ByteBuffer buf = method.compress().process(); - DataFrame out = new DataFrame(frame); - out.setPayload(buf); - out.setRsv1(true); - if (!method.compress().isDone()) - { - out.setFin(false); - // no callback for start/middle frames - nextOutgoingFrame(out,null); - } - else - { - // pass through callback to last frame - nextOutgoingFrame(out,callback); - } - } - - // reset only at end of message - if (frame.isFin()) - { - method.compress().end(); - } - } - - @Override - public void setConfig(ExtensionConfig config) - { - super.setConfig(config); - method = new DeflateCompressionMethod(); - } - - @Override - public String toString() - { - return String.format("%s[method=%s]",this.getClass().getSimpleName(),method); - } -} diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/PerMessageDeflateExtension.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/PerMessageDeflateExtension.java new file mode 100644 index 00000000000..2bba442576f --- /dev/null +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/PerMessageDeflateExtension.java @@ -0,0 +1,253 @@ +// +// ======================================================================== +// Copyright (c) 1995-2013 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.common.extensions.compress; + +import java.nio.ByteBuffer; +import java.util.zip.DataFormatException; +import java.util.zip.Deflater; +import java.util.zip.Inflater; + +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.api.BadPayloadException; +import org.eclipse.jetty.websocket.api.WriteCallback; +import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig; +import org.eclipse.jetty.websocket.api.extensions.Frame; +import org.eclipse.jetty.websocket.common.OpCode; +import org.eclipse.jetty.websocket.common.extensions.AbstractExtension; +import org.eclipse.jetty.websocket.common.frames.DataFrame; + +/** + * Per Message Deflate Compression extension for WebSocket. + *

+ * Attempts to follow draft-ietf-hybi-permessage-compression-12 + */ +public class PerMessageDeflateExtension extends AbstractExtension +{ + private static final boolean BFINAL_HACK = Boolean.parseBoolean(System.getProperty("jetty.websocket.bfinal.hack","true")); + private static final Logger LOG = Log.getLogger(PerMessageDeflateExtension.class); + + private static final int OVERHEAD = 64; + /** Tail Bytes per Spec */ + private static final byte[] TAIL = new byte[] + { 0x00, 0x00, (byte)0xFF, (byte)0xFF }; + private int bufferSize = 64 * 1024; + private Deflater compressor; + private Inflater decompressor; + + @Override + public String getName() + { + return "permessage-deflate"; + } + + @Override + public synchronized void incomingFrame(Frame frame) + { + if (OpCode.isControlFrame(frame.getOpCode()) || !frame.isRsv1()) + { + // Cannot modify incoming control frames or ones with RSV1 set. + nextIncomingFrame(frame); + return; + } + + if (!frame.hasPayload()) + { + // no payload? nothing to do. + nextIncomingFrame(frame); + return; + } + + // Prime the decompressor + ByteBuffer payload = frame.getPayload(); + int inlen = payload.remaining(); + byte compressed[] = new byte[inlen + TAIL.length]; + payload.get(compressed,0,inlen); + System.arraycopy(TAIL,0,compressed,inlen,TAIL.length); + decompressor.setInput(compressed,0,compressed.length); + + // Perform decompression + while (decompressor.getRemaining() > 0 && !decompressor.finished()) + { + DataFrame out = new DataFrame(frame); + out.setRsv1(false); // Unset RSV1 + byte outbuf[] = new byte[Math.min(inlen * 2,bufferSize)]; + try + { + int len = decompressor.inflate(outbuf); + if (len == 0) + { + if (decompressor.needsInput()) + { + throw new BadPayloadException("Unable to inflate frame, not enough input on frame"); + } + if (decompressor.needsDictionary()) + { + throw new BadPayloadException("Unable to inflate frame, frame erroneously says it needs a dictionary"); + } + } + if (len > 0) + { + out.setPayload(ByteBuffer.wrap(outbuf,0,len)); + } + nextIncomingFrame(out); + } + catch (DataFormatException e) + { + LOG.warn(e); + throw new BadPayloadException(e); + } + } + } + + /** + * Indicates use of RSV1 flag for indicating deflation is in use. + */ + @Override + public boolean isRsv1User() + { + return true; + } + + @Override + public synchronized void outgoingFrame(Frame frame, WriteCallback callback) + { + if (OpCode.isControlFrame(frame.getOpCode())) + { + // skip, cannot compress control frames. + nextOutgoingFrame(frame,callback); + return; + } + + if (!frame.hasPayload()) + { + // pass through, nothing to do + nextOutgoingFrame(frame,callback); + return; + } + + if (LOG.isDebugEnabled()) + { + LOG.debug("outgoingFrame({}, {}) - {}",OpCode.name(frame.getOpCode()),callback != null?callback.getClass().getSimpleName():"", + BufferUtil.toDetailString(frame.getPayload())); + } + + // Prime the compressor + byte uncompressed[] = BufferUtil.toArray(frame.getPayload()); + + // Perform the compression + if (!compressor.finished()) + { + compressor.setInput(uncompressed,0,uncompressed.length); + byte compressed[] = new byte[uncompressed.length + OVERHEAD]; + + while (!compressor.needsInput()) + { + int len = compressor.deflate(compressed,0,compressed.length,Deflater.SYNC_FLUSH); + ByteBuffer outbuf = getBufferPool().acquire(len,true); + BufferUtil.clearToFill(outbuf); + + if (len > 0) + { + outbuf.put(compressed,0,len - 4); + } + + BufferUtil.flipToFlush(outbuf,0); + + if (len > 0 && BFINAL_HACK) + { + /* + * Per the spec, it says that BFINAL 1 or 0 are allowed. + * + * However, Java always uses BFINAL 1, whereas the browsers Chromium and Safari fail to decompress when it encounters BFINAL 1. + * + * This hack will always set BFINAL 0 + */ + byte b0 = outbuf.get(0); + if ((b0 & 1) != 0) // if BFINAL 1 + { + outbuf.put(0,(b0 ^= 1)); // flip bit to BFINAL 0 + } + } + + DataFrame out = new DataFrame(frame); + out.setRsv1(true); + out.setPooledBuffer(true); + out.setPayload(outbuf); + + if (!compressor.needsInput()) + { + // this is fragmented + out.setFin(false); + nextOutgoingFrame(out,null); // non final frames have no callback + } + else + { + // pass through the callback + nextOutgoingFrame(out,callback); + } + } + } + } + + @Override + public void setConfig(final ExtensionConfig config) + { + ExtensionConfig negotiated = new ExtensionConfig(config.getName()); + + boolean nowrap = true; + compressor = new Deflater(Deflater.BEST_COMPRESSION,nowrap); + compressor.setStrategy(Deflater.DEFAULT_STRATEGY); + + decompressor = new Inflater(nowrap); + + for (String key : config.getParameterKeys()) + { + key = key.trim(); + String value = config.getParameter(key,null); + switch(key) { + case "c2s_max_window_bits": + negotiated.setParameter("s2c_max_window_bits",value); + break; + case "c2s_no_context_takeover": + negotiated.setParameter("s2c_no_context_takeover",value); + break; + case "s2c_max_window_bits": + negotiated.setParameter("c2s_max_window_bits",value); + break; + case "s2c_no_context_takeover": + negotiated.setParameter("c2s_no_context_takeover",value); + break; + } + } + + super.setConfig(negotiated); + } + + @Override + public String toString() + { + StringBuilder str = new StringBuilder(); + str.append(this.getClass().getSimpleName()); + str.append('['); + str.append(']'); + return str.toString(); + } +} diff --git a/jetty-websocket/websocket-common/src/main/resources/META-INF/services/org.eclipse.jetty.websocket.api.extensions.Extension b/jetty-websocket/websocket-common/src/main/resources/META-INF/services/org.eclipse.jetty.websocket.api.extensions.Extension index 70af44ee0d6..11b4f359908 100644 --- a/jetty-websocket/websocket-common/src/main/resources/META-INF/services/org.eclipse.jetty.websocket.api.extensions.Extension +++ b/jetty-websocket/websocket-common/src/main/resources/META-INF/services/org.eclipse.jetty.websocket.api.extensions.Extension @@ -1,5 +1,5 @@ org.eclipse.jetty.websocket.common.extensions.identity.IdentityExtension org.eclipse.jetty.websocket.common.extensions.compress.DeflateFrameExtension org.eclipse.jetty.websocket.common.extensions.compress.XWebkitDeflateFrameExtension -org.eclipse.jetty.websocket.common.extensions.compress.MessageDeflateCompressionExtension +org.eclipse.jetty.websocket.common.extensions.compress.PerMessageDeflateExtension org.eclipse.jetty.websocket.common.extensions.fragment.FragmentExtension \ No newline at end of file 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 e9f19736aa2..4c5ed610951 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 @@ -18,15 +18,14 @@ package org.eclipse.jetty.websocket.common.extensions; -import org.eclipse.jetty.websocket.common.extensions.compress.DeflateCompressionMethodTest; -import org.eclipse.jetty.websocket.common.extensions.compress.MessageCompressionExtensionTest; import org.eclipse.jetty.websocket.common.extensions.compress.DeflateFrameExtensionTest; +import org.eclipse.jetty.websocket.common.extensions.compress.PerMessageDeflateExtensionTest; import org.junit.runner.RunWith; import org.junit.runners.Suite; @RunWith(Suite.class) @Suite.SuiteClasses( - { ExtensionStackTest.class, DeflateCompressionMethodTest.class, MessageCompressionExtensionTest.class, FragmentExtensionTest.class, + { ExtensionStackTest.class, PerMessageDeflateExtensionTest.class, FragmentExtensionTest.class, IdentityExtensionTest.class, DeflateFrameExtensionTest.class }) public class AllTests { diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/CapturedHexPayloads.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/CapturedHexPayloads.java index 4abbce4263a..c7933f0368d 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/CapturedHexPayloads.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/CapturedHexPayloads.java @@ -21,26 +21,19 @@ package org.eclipse.jetty.websocket.common.extensions.compress; import java.util.ArrayList; import java.util.List; -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.websocket.api.WriteCallback; import org.eclipse.jetty.websocket.api.extensions.Frame; import org.eclipse.jetty.websocket.api.extensions.OutgoingFrames; import org.eclipse.jetty.websocket.common.Hex; -import org.eclipse.jetty.websocket.common.OpCode; public class CapturedHexPayloads implements OutgoingFrames { - private static final Logger LOG = Log.getLogger(CapturedHexPayloads.class); private List captured = new ArrayList<>(); @Override public void outgoingFrame(Frame frame, WriteCallback callback) { String hexPayload = Hex.asHex(frame.getPayload()); - LOG.debug("outgoingFrame({}: \"{}\", {})", - OpCode.name(frame.getOpCode()), - hexPayload, callback!=null?callback.getClass().getSimpleName():""); captured.add(hexPayload); if (callback != null) { 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 deleted file mode 100644 index 32f741ee2c4..00000000000 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/DeflateCompressionMethodTest.java +++ /dev/null @@ -1,221 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2013 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.common.extensions.compress; - -import static org.hamcrest.Matchers.*; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -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.junit.Assert; -import org.junit.Test; - -/** - * Test the Deflate Compression Method in use by several extensions. - */ -public class DeflateCompressionMethodTest -{ - private static final Logger LOG = Log.getLogger(DeflateCompressionMethodTest.class); - - private void assertRoundTrip(CompressionMethod method, CharSequence msg) - { - String expected = msg.toString(); - - ByteBuffer orig = BufferUtil.toBuffer(expected,StringUtil.__UTF8_CHARSET); - - LOG.debug("orig: {}",BufferUtil.toDetailString(orig)); - - // compress - method.compress().begin(); - method.compress().input(orig); - ByteBuffer compressed = method.compress().process(); - LOG.debug("compressed: {}",BufferUtil.toDetailString(compressed)); - Assert.assertThat("Compress.isDone",method.compress().isDone(),is(true)); - method.compress().end(); - - // decompress - ByteBuffer decompressed = ByteBuffer.allocate(msg.length()); - LOG.debug("decompressed(a): {}",BufferUtil.toDetailString(decompressed)); - method.decompress().begin(); - method.decompress().input(compressed); - while (!method.decompress().isDone()) - { - ByteBuffer window = method.decompress().process(); - BufferUtil.put(window,decompressed); - } - BufferUtil.flipToFlush(decompressed,0); - LOG.debug("decompressed(f): {}",BufferUtil.toDetailString(decompressed)); - method.decompress().end(); - - // validate - String actual = BufferUtil.toUTF8String(decompressed); - Assert.assertThat("Message Size",actual.length(),is(msg.length())); - 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). - * - * Round Trip (RT) Compress then Decompress - */ - @Test - public void testRTLarge() - { - // large sized message - StringBuilder msg = new StringBuilder(); - for (int i = 0; i < 5000; i++) - { - msg.append("0123456789ABCDEF "); - } - msg.append('X'); // so we can see the end in our debugging - - // ensure that test remains sane - Assert.assertThat("Large Payload Length",msg.length(),greaterThan(0xFF_FF)); - - // Setup Compression Method - CompressionMethod method = new DeflateCompressionMethod(); - - // Test round trip - assertRoundTrip(method,msg); - } - - /** - * Test many small payloads (each payload length less than 126 bytes). - * - * Round Trip (RT) Compress then Decompress - */ - @Test - public void testRTManySmall() - { - // Quote - List quote = new ArrayList<>(); - quote.add("No amount of experimentation can ever prove me right;"); - quote.add("a single experiment can prove me wrong."); - quote.add("-- Albert Einstein"); - - // Setup Compression Method - CompressionMethod method = new DeflateCompressionMethod(); - - for (String msg : quote) - { - // Test round trip - assertRoundTrip(method,msg); - } - } - - /** - * Test a medium payload (a payload length between 126 - 65535 bytes). - * - * Round Trip (RT) Compress then Decompress - */ - @Test - public void testRTMedium() - { - // medium sized message - StringBuilder msg = new StringBuilder(); - for (int i = 0; i < 1000; i++) - { - msg.append("0123456789ABCDEF "); - } - msg.append('X'); // so we can see the end in our debugging - - // ensure that test remains sane - Assert.assertThat("Medium Payload Length",msg.length(),allOf(greaterThanOrEqualTo(0x7E),lessThanOrEqualTo(0xFF_FF))); - - // Setup Compression Method - CompressionMethod method = new DeflateCompressionMethod(); - - // Test round trip - assertRoundTrip(method, msg); - } - - /** - * Test a small payload (a payload length less than 126 bytes). - * - * Round Trip (RT) Compress then Decompress - */ - @Test - public void testRTSmall() - { - // Quote - StringBuilder quote = new StringBuilder(); - quote.append("No amount of experimentation can ever prove me right;\n"); - quote.append("a single experiment can prove me wrong.\n"); - quote.append("-- Albert Einstein"); - - // ensure that test remains sane - Assert.assertThat("Small Payload Length",quote.length(),lessThan(0x7E)); - - // Setup Compression Method - CompressionMethod method = new DeflateCompressionMethod(); - - // Test round trip - assertRoundTrip(method,quote); - } -} diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/MessageCompressionExtensionTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/PerMessageDeflateExtensionTest.java similarity index 61% rename from jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/MessageCompressionExtensionTest.java rename to jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/PerMessageDeflateExtensionTest.java index 41474772d39..1e0b33cf147 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/MessageCompressionExtensionTest.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/PerMessageDeflateExtensionTest.java @@ -23,7 +23,7 @@ import static org.hamcrest.Matchers.*; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.LinkedList; +import java.util.Collections; import java.util.List; import org.eclipse.jetty.io.MappedByteBufferPool; @@ -34,27 +34,77 @@ import org.eclipse.jetty.websocket.api.WebSocketPolicy; import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig; import org.eclipse.jetty.websocket.api.extensions.Frame; import org.eclipse.jetty.websocket.common.ByteBufferAssert; +import org.eclipse.jetty.websocket.common.Hex; import org.eclipse.jetty.websocket.common.IncomingFramesCapture; import org.eclipse.jetty.websocket.common.OpCode; import org.eclipse.jetty.websocket.common.OutgoingFramesCapture; +import org.eclipse.jetty.websocket.common.Parser; +import org.eclipse.jetty.websocket.common.UnitParser; import org.eclipse.jetty.websocket.common.WebSocketFrame; -import org.eclipse.jetty.websocket.common.extensions.compress.CompressionMethod.Process; import org.eclipse.jetty.websocket.common.frames.PingFrame; import org.eclipse.jetty.websocket.common.frames.TextFrame; import org.junit.Assert; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestName; -public class MessageCompressionExtensionTest +public class PerMessageDeflateExtensionTest { + @Rule + public TestName testname = new TestName(); + + private void assertIncoming(byte[] raw, String... expectedTextDatas) + { + WebSocketPolicy policy = WebSocketPolicy.newClientPolicy(); + + PerMessageDeflateExtension ext = new PerMessageDeflateExtension(); + ext.setBufferPool(new MappedByteBufferPool()); + ext.setPolicy(policy); + + ExtensionConfig config = ExtensionConfig.parse("permessage-deflate; c2s_max_window_bits"); + ext.setConfig(config); + + // Setup capture of incoming frames + IncomingFramesCapture capture = new IncomingFramesCapture(); + + // Wire up stack + ext.setNextIncomingFrames(capture); + + Parser parser = new UnitParser(policy); + parser.configureFromExtensions(Collections.singletonList(ext)); + parser.setIncomingFramesHandler(ext); + + parser.parse(ByteBuffer.wrap(raw)); + + int len = expectedTextDatas.length; + capture.assertFrameCount(len); + capture.assertHasFrame(OpCode.TEXT,len); + + for (int i = 0; i < len; i++) + { + WebSocketFrame actual = capture.getFrames().get(i); + String prefix = "Frame[" + i + "]"; + Assert.assertThat(prefix + ".opcode",actual.getOpCode(),is(OpCode.TEXT)); + Assert.assertThat(prefix + ".fin",actual.isFin(),is(true)); + Assert.assertThat(prefix + ".rsv1",actual.isRsv1(),is(false)); // RSV1 should be unset at this point + Assert.assertThat(prefix + ".rsv2",actual.isRsv2(),is(false)); + Assert.assertThat(prefix + ".rsv3",actual.isRsv3(),is(false)); + + ByteBuffer expected = BufferUtil.toBuffer(expectedTextDatas[i],StringUtil.__UTF8_CHARSET); + Assert.assertThat(prefix + ".payloadLength",actual.getPayloadLength(),is(expected.remaining())); + ByteBufferAssert.assertEquals(prefix + ".payload",expected,actual.getPayload().slice()); + } + } + private void assertDraftExample(String hexStr, String expectedStr) { WebSocketPolicy policy = WebSocketPolicy.newServerPolicy(); // Setup extension - MessageDeflateCompressionExtension ext = new MessageDeflateCompressionExtension(); + PerMessageDeflateExtension ext = new PerMessageDeflateExtension(); ext.setBufferPool(new MappedByteBufferPool()); ext.setPolicy(policy); - ExtensionConfig config = ExtensionConfig.parse("permessage-compress"); + ExtensionConfig config = ExtensionConfig.parse("permessage-deflate"); ext.setConfig(config); // Setup capture of incoming frames @@ -91,6 +141,54 @@ public class MessageCompressionExtensionTest ByteBufferAssert.assertEquals(prefix + ".payload",expected,actual.getPayload().slice()); } + private void assertDraft12Example(String hexStrCompleteFrame, String... expectedStrs) + { + WebSocketPolicy policy = WebSocketPolicy.newClientPolicy(); + + // Setup extension + PerMessageDeflateExtension ext = new PerMessageDeflateExtension(); + ext.setBufferPool(new MappedByteBufferPool()); + ext.setPolicy(policy); + ExtensionConfig config = ExtensionConfig.parse("permessage-deflate"); + ext.setConfig(config); + + // Setup capture of incoming frames + IncomingFramesCapture capture = new IncomingFramesCapture(); + + // Wire up stack + ext.setNextIncomingFrames(capture); + + // Receive frame + String hex = hexStrCompleteFrame.replaceAll("\\s*0x",""); + byte net[] = TypeUtil.fromHexString(hex); + + Parser parser = new UnitParser(policy); + parser.configureFromExtensions(Collections.singletonList(ext)); + parser.setIncomingFramesHandler(ext); + parser.parse(ByteBuffer.wrap(net)); + + // Verify captured frames. + int expectedCount = expectedStrs.length; + capture.assertFrameCount(expectedCount); + capture.assertHasFrame(OpCode.TEXT,expectedCount); + + for (int i = 0; i < expectedCount; i++) + { + WebSocketFrame actual = capture.getFrames().pop(); + + String prefix = String.format("frame[%d]",i); + Assert.assertThat(prefix + ".opcode",actual.getOpCode(),is(OpCode.TEXT)); + Assert.assertThat(prefix + ".fin",actual.isFin(),is(true)); + Assert.assertThat(prefix + ".rsv1",actual.isRsv1(),is(false)); // RSV1 should be unset at this point + Assert.assertThat(prefix + ".rsv2",actual.isRsv2(),is(false)); + Assert.assertThat(prefix + ".rsv3",actual.isRsv3(),is(false)); + + ByteBuffer expected = BufferUtil.toBuffer(expectedStrs[i],StringUtil.__UTF8_CHARSET); + Assert.assertThat(prefix + ".payloadLength",actual.getPayloadLength(),is(expected.remaining())); + ByteBufferAssert.assertEquals(prefix + ".payload",expected,actual.getPayload().slice()); + } + } + /** * Decode payload example as seen in draft-ietf-hybi-permessage-compression-01. */ @@ -103,6 +201,88 @@ public class MessageCompressionExtensionTest assertDraftExample(hex.toString(),"Hello"); } + /** + * Decode payload example as seen in draft-ietf-hybi-permessage-compression-12. Section 8.2.3.1 + */ + @Test + public void testDraft12_Hello_UnCompressedBlock() + { + StringBuilder hex = new StringBuilder(); + // basic, 1 block, compressed with 0 compression level (aka, uncompressed). + hex.append("0xc1 0x07 0xf2 0x48 0xcd 0xc9 0xc9 0x07 0x00"); + assertDraft12Example(hex.toString(),"Hello"); + } + + /** + * Decode payload example as seen in draft-ietf-hybi-permessage-compression-12. Section 8.2.3.2 + */ + @Test + public void testDraft12_Hello_NoSharingLZ77SlidingWindow() + { + StringBuilder hex = new StringBuilder(); + // message 1 + hex.append("0xc1 0x07"); // (HEADER added for this test) + hex.append("0xf2 0x48 0xcd 0xc9 0xc9 0x07 0x00"); + // message 2 + hex.append("0xc1 0x07"); // (HEADER added for this test) + hex.append("0xf2 0x48 0xcd 0xc9 0xc9 0x07 0x00"); + assertDraft12Example(hex.toString(),"Hello","Hello"); + } + + /** + * Decode payload example as seen in draft-ietf-hybi-permessage-compression-12. Section 8.2.3.2 + */ + @Test + public void testDraft12_Hello_SharingLZ77SlidingWindow() + { + StringBuilder hex = new StringBuilder(); + // message 1 + hex.append("0xc1 0x07"); // (HEADER added for this test) + hex.append("0xf2 0x48 0xcd 0xc9 0xc9 0x07 0x00"); + // message 2 + hex.append("0xc1 0x05"); // (HEADER added for this test) + hex.append("0xf2 0x00 0x11 0x00 0x00"); + assertDraft12Example(hex.toString(),"Hello","Hello"); + } + + /** + * Decode payload example as seen in draft-ietf-hybi-permessage-compression-12. Section 8.2.3.3 + */ + @Test + public void testDraft12_Hello_NoCompressionBlock() + { + StringBuilder hex = new StringBuilder(); + // basic, 1 block, compressed with no compression. + hex.append("0xc1 0x0b 0x00 0x05 0x00 0xfa 0xff 0x48 0x65 0x6c 0x6c 0x6f 0x00"); + assertDraft12Example(hex.toString(),"Hello"); + } + + /** + * Decode payload example as seen in draft-ietf-hybi-permessage-compression-12. Section 8.2.3.4 + */ + @Test + public void testDraft12_Hello_Bfinal1() + { + StringBuilder hex = new StringBuilder(); + // basic, 1 block, compressed with BFINAL set to 1. + hex.append("0xc1 0x08"); // (HEADER added for this test) + hex.append("0xf3 0x48 0xcd 0xc9 0xc9 0x07 0x00 0x00"); + assertDraft12Example(hex.toString(),"Hello"); + } + + /** + * Decode payload example as seen in draft-ietf-hybi-permessage-compression-12. Section 8.2.3.5 + */ + @Test + public void testDraft12_Hello_TwoDeflateBlocks() + { + StringBuilder hex = new StringBuilder(); + hex.append("0xc1 0x0d"); // (HEADER added for this test) + // 2 deflate blocks + hex.append("0xf2 0x48 0x05 0x00 0x00 0x00 0xff 0xff 0xca 0xc9 0xc9 0x07 0x00"); + assertDraft12Example(hex.toString(),"Hello"); + } + /** * Decode payload example as seen in draft-ietf-hybi-permessage-compression-01. */ @@ -159,12 +339,24 @@ public class MessageCompressionExtensionTest assertDraftExample(hex.toString(),"HelloHello"); } + @Test + public void testPyWebSocket_ToraToraTora() + { + // Captured from Pywebsocket (r781) - "tora" sent 3 times. + String tora1 = "c186b0c7fe48" + "9a0ed102b4c7"; + String tora2 = "c185ccb6cb50" + "e6b7a950cc"; + String tora3 = "c1847b9aac69" + "79fbac69"; + byte rawbuf[] = Hex.asByteArray(tora1 + tora2 + tora3); + assertIncoming(rawbuf,"tora","tora","tora"); + } + /** * Incoming PING (Control Frame) should pass through extension unmodified */ @Test - public void testIncomingPing() { - MessageDeflateCompressionExtension ext = new MessageDeflateCompressionExtension(); + public void testIncomingPing() + { + PerMessageDeflateExtension ext = new PerMessageDeflateExtension(); ext.setBufferPool(new MappedByteBufferPool()); ext.setPolicy(WebSocketPolicy.newServerPolicy()); ExtensionConfig config = ExtensionConfig.parse("permessage-compress"); @@ -201,7 +393,7 @@ public class MessageCompressionExtensionTest @Test public void testIncomingUncompressedFrames() { - MessageDeflateCompressionExtension ext = new MessageDeflateCompressionExtension(); + PerMessageDeflateExtension ext = new PerMessageDeflateExtension(); ext.setBufferPool(new MappedByteBufferPool()); ext.setPolicy(WebSocketPolicy.newServerPolicy()); ExtensionConfig config = ExtensionConfig.parse("permessage-compress"); @@ -250,83 +442,13 @@ public class MessageCompressionExtensionTest } } - /** - * Verify that outgoing text frames are compressed. - */ - @Test - public void testOutgoingFrames() throws IOException - { - MessageDeflateCompressionExtension ext = new MessageDeflateCompressionExtension(); - ext.setBufferPool(new MappedByteBufferPool()); - ext.setPolicy(WebSocketPolicy.newServerPolicy()); - ExtensionConfig config = ExtensionConfig.parse("permessage-compress"); - ext.setConfig(config); - - // Setup capture of outgoing frames - OutgoingFramesCapture capture = new OutgoingFramesCapture(); - - // Wire up stack - ext.setNextOutgoingFrames(capture); - - // Quote - List quote = new ArrayList<>(); - quote.add("No amount of experimentation can ever prove me right;"); - quote.add("a single experiment can prove me wrong."); - quote.add("-- Albert Einstein"); - - // Expected compressed parts - List expectedBuffers = new ArrayList<>(); - CompressionMethod method = new DeflateCompressionMethod(); - for(String part: quote) { - Process process = method.compress(); - process.begin(); - process.input(BufferUtil.toBuffer(part,StringUtil.__UTF8_CHARSET)); - expectedBuffers.add(process.process()); - process.end(); - } - - // Write quote as separate frames - for (String section : quote) - { - Frame frame = new TextFrame().setPayload(section); - ext.outgoingFrame(frame,null); - } - - int len = quote.size(); - capture.assertFrameCount(len); - capture.assertHasFrame(OpCode.TEXT,len); - - String prefix; - LinkedList frames = capture.getFrames(); - for (int i = 0; i < len; i++) - { - prefix = "Frame[" + i + "]"; - WebSocketFrame actual = frames.get(i); - - // Validate Frame - Assert.assertThat(prefix + ".opcode",actual.getOpCode(),is(OpCode.TEXT)); - Assert.assertThat(prefix + ".fin",actual.isFin(),is(true)); - Assert.assertThat(prefix + ".rsv1",actual.isRsv1(),is(true)); - Assert.assertThat(prefix + ".rsv2",actual.isRsv2(),is(false)); - Assert.assertThat(prefix + ".rsv3",actual.isRsv3(),is(false)); - - // Validate Payload - ByteBuffer expected = expectedBuffers.get(i); - // Decompress payload - ByteBuffer compressed = actual.getPayload().slice(); - - Assert.assertThat(prefix + ".payloadLength",compressed.remaining(),is(expected.remaining())); - ByteBufferAssert.assertEquals(prefix + ".payload",expected,compressed); - } - } - /** * Outgoing PING (Control Frame) should pass through extension unmodified */ @Test public void testOutgoingPing() throws IOException { - MessageDeflateCompressionExtension ext = new MessageDeflateCompressionExtension(); + PerMessageDeflateExtension ext = new PerMessageDeflateExtension(); ext.setBufferPool(new MappedByteBufferPool()); ext.setPolicy(WebSocketPolicy.newServerPolicy()); ExtensionConfig config = ExtensionConfig.parse("permessage-compress"); diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/helper/EchoServlet.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/helper/EchoServlet.java index cd4133f605c..66e23525831 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/helper/EchoServlet.java +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/helper/EchoServlet.java @@ -19,7 +19,7 @@ package org.eclipse.jetty.websocket.server.helper; import org.eclipse.jetty.websocket.common.extensions.compress.DeflateFrameExtension; -import org.eclipse.jetty.websocket.common.extensions.compress.MessageDeflateCompressionExtension; +import org.eclipse.jetty.websocket.common.extensions.compress.PerMessageDeflateExtension; import org.eclipse.jetty.websocket.servlet.WebSocketServlet; import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; @@ -34,7 +34,7 @@ public class EchoServlet extends WebSocketServlet { // Setup some extensions we want to test against factory.getExtensionFactory().register("x-webkit-deflate-frame",DeflateFrameExtension.class); - factory.getExtensionFactory().register("permessage-compress",MessageDeflateCompressionExtension.class); + factory.getExtensionFactory().register("permessage-compress",PerMessageDeflateExtension.class); // Setup the desired Socket to use for all incoming upgrade requests factory.register(EchoSocket.class);