diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/CompressExtension.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/CompressExtension.java index d4030532b33..cffdc06c582 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/CompressExtension.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/CompressExtension.java @@ -42,18 +42,39 @@ public abstract class CompressExtension extends AbstractExtension { protected static final byte[] TAIL_BYTES = new byte[]{0x00, 0x00, (byte)0xFF, (byte)0xFF}; private static final Logger LOG = Log.getLogger(CompressExtension.class); + + /** Never drop tail bytes 0000FFFF, from any frame type */ + protected static final int TAIL_DROP_NEVER = 0; + /** Always drop tail bytes 0000FFFF, from all frame types */ + protected static final int TAIL_DROP_ALWAYS = 1; + /** Only drop tail bytes 0000FFFF, from fin==true frames */ + protected static final int TAIL_DROP_FIN_ONLY = 2; + + /** Always set RSV flag, on all frame types */ + protected static final int RSV_USE_ALWAYS = 0; + /** + * Only set RSV flag on first frame in multi-frame messages. + *

+ * Note: this automatically means no-continuation frames have + * the RSV bit set + */ + protected static final int RSV_USE_ONLY_FIRST = 1; private final Queue entries = new ConcurrentArrayQueue<>(); private final IteratingCallback flusher = new Flusher(); private final Deflater compressor; private final Inflater decompressor; + private int tailDrop = TAIL_DROP_NEVER; + private int rsvUse = RSV_USE_ALWAYS; protected CompressExtension() { compressor = new Deflater(Deflater.BEST_COMPRESSION, true); decompressor = new Inflater(true); + tailDrop = getTailDropMode(); + rsvUse = getRsvUseMode(); } - + public Deflater getDeflater() { return compressor; @@ -72,6 +93,20 @@ public abstract class CompressExtension extends AbstractExtension { return true; } + + /** + * Return the mode of operation for dropping (or keeping) tail bytes in frames generated by compress (outgoing) + * + * @return either {@link #TAIL_DROP_ALWAYS}, {@link #TAIL_DROP_FIN_ONLY}, or {@link #TAIL_DROP_NEVER} + */ + abstract int getTailDropMode(); + + /** + * Return the mode of operation for RSV flag use in frames generate by compress (outgoing) + * + * @return either {@link #RSV_USE_ALWAYS} or {@link #RSV_USE_ONLY_FIRST} + */ + abstract int getRsvUseMode(); protected void forwardIncoming(Frame frame, ByteAccumulator accumulator) { @@ -301,15 +336,31 @@ public abstract class CompressExtension extends AbstractExtension } } - // Skip the last tail bytes bytes generated by SYNC_FLUSH. - payload = ByteBuffer.wrap(output, 0, outputLength - TAIL_BYTES.length); - LOG.debug("Compressed {}: {}->{} chunk bytes", entry, inputLength, outputLength); + boolean fin = frame.isFin() && finished; + + // Handle tail bytes generated by SYNC_FLUSH. + if(tailDrop == TAIL_DROP_ALWAYS) { + payload = ByteBuffer.wrap(output, 0, outputLength - TAIL_BYTES.length); + } else if(tailDrop == TAIL_DROP_FIN_ONLY) { + payload = ByteBuffer.wrap(output, 0, outputLength - (fin?TAIL_BYTES.length:0)); + } else { + // always include + payload = ByteBuffer.wrap(output, 0, outputLength); + } + if (LOG.isDebugEnabled()) + { + LOG.debug("Compressed {}: {}->{} chunk bytes",entry,inputLength,outputLength); + } boolean continuation = frame.getType().isContinuation() || !first; DataFrame chunk = new DataFrame(frame, continuation); - chunk.setRsv1(true); + if(rsvUse == RSV_USE_ONLY_FIRST) { + chunk.setRsv1(!continuation); + } else { + // always set + chunk.setRsv1(true); + } chunk.setPayload(payload); - boolean fin = frame.isFin() && finished; chunk.setFin(fin); nextOutgoingFrame(chunk, this, entry.batchMode); diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/DeflateFrameExtension.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/DeflateFrameExtension.java index 8e36e8fd005..3ac5fae5aef 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/DeflateFrameExtension.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/compress/DeflateFrameExtension.java @@ -35,6 +35,18 @@ public class DeflateFrameExtension extends CompressExtension { return "deflate-frame"; } + + @Override + int getRsvUseMode() + { + return RSV_USE_ALWAYS; + } + + @Override + int getTailDropMode() + { + return TAIL_DROP_ALWAYS; + } @Override public void incomingFrame(Frame frame) 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 index 0b0d9e537df..86683567acf 100644 --- 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 @@ -102,13 +102,25 @@ public class PerMessageDeflateExtension extends CompressExtension } super.nextOutgoingFrame(frame, callback, batchMode); } + + @Override + int getRsvUseMode() + { + return RSV_USE_ONLY_FIRST; + } + + @Override + int getTailDropMode() + { + return TAIL_DROP_FIN_ONLY; + } @Override public void setConfig(final ExtensionConfig config) { configRequested = new ExtensionConfig(config); configNegotiated = new ExtensionConfig(config.getName()); - + for (String key : config.getParameterKeys()) { key = key.trim(); diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserDebugTool.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserDebugTool.java index dd60c94fa8f..8ab036103d3 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserDebugTool.java +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserDebugTool.java @@ -23,6 +23,7 @@ import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.handler.ResourceHandler; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.websocket.common.extensions.compress.PerMessageDeflateExtension; import org.eclipse.jetty.websocket.server.WebSocketHandler; import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest; import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse; @@ -83,21 +84,15 @@ public class BrowserDebugTool implements WebSocketCreator String ua = req.getHeader("User-Agent"); String rexts = req.getHeader("Sec-WebSocket-Extensions"); - - LOG.debug("User-Agent: {}", ua); - LOG.debug("Sec-WebSocket-Extensions (Request) : {}", rexts); + + LOG.debug("User-Agent: {}",ua); + LOG.debug("Sec-WebSocket-Extensions (Request) : {}",rexts); return new BrowserSocket(ua,rexts); } - public void start() throws Exception + public int getPort() { - server.start(); - LOG.info("Server available on port {}", getPort()); - } - - public void stop() throws Exception - { - server.stop(); + return connector.getLocalPort(); } public void prepare(int port) @@ -116,6 +111,7 @@ public class BrowserDebugTool implements WebSocketCreator // factory.getExtensionFactory().unregister("deflate-frame"); // factory.getExtensionFactory().unregister("permessage-deflate"); + factory.getExtensionFactory().register("permessage-deflate",PerMessageDeflateExtension.class); // factory.getExtensionFactory().unregister("x-webkit-deflate-frame"); // Setup the desired Socket to use for all incoming upgrade requests @@ -123,6 +119,9 @@ public class BrowserDebugTool implements WebSocketCreator // Set the timeout factory.getPolicy().setIdleTimeout(30000); + + // Set top end message size + factory.getPolicy().setMaxTextMessageSize(15 * 1024 * 1024); } }; @@ -138,8 +137,14 @@ public class BrowserDebugTool implements WebSocketCreator LOG.info("{} setup on port {}",this.getClass().getName(),port); } - public int getPort() + public void start() throws Exception { - return connector.getLocalPort(); + server.start(); + LOG.info("Server available on port {}",getPort()); + } + + public void stop() throws Exception + { + server.stop(); } } diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserSocket.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserSocket.java index 83d3bcebc51..0f10fe808bc 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserSocket.java +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserSocket.java @@ -109,7 +109,15 @@ public class BrowserSocket @OnWebSocketMessage public void onTextMessage(String message) { - LOG.info("onTextMessage({})",message); + if (message.length() > 300) + { + int len = message.length(); + LOG.info("onTextMessage({} ... {}) size:{}",message.substring(0,15),message.substring(len - 15,len).replaceAll("[\r\n]*",""),len); + } + else + { + LOG.info("onTextMessage({})",message); + } int idx = message.indexOf(':'); if (idx > 0) diff --git a/jetty-websocket/websocket-server/src/test/resources/browser-debug-tool/index.html b/jetty-websocket/websocket-server/src/test/resources/browser-debug-tool/index.html index ee9ef00de39..aa31f573a34 100644 --- a/jetty-websocket/websocket-server/src/test/resources/browser-debug-tool/index.html +++ b/jetty-websocket/websocket-server/src/test/resources/browser-debug-tool/index.html @@ -17,6 +17,10 @@ + + + + \ No newline at end of file diff --git a/jetty-websocket/websocket-server/src/test/resources/browser-debug-tool/websocket.js b/jetty-websocket/websocket-server/src/test/resources/browser-debug-tool/websocket.js index 1bc5fe78947..35dd0dd1848 100644 --- a/jetty-websocket/websocket-server/src/test/resources/browser-debug-tool/websocket.js +++ b/jetty-websocket/websocket-server/src/test/resources/browser-debug-tool/websocket.js @@ -59,12 +59,20 @@ var wstool = { }, infoc : function(message) { - wstool._out("client", "[c] " + message); + if(message.length > 300) { + wstool._out("client", "[c] [big message: " + message.length + " characters]"); + } else { + wstool._out("client", "[c] " + message); + } }, infos : function(message) { this._scount++; - wstool._out("server", "[s" + this._scount + "] " + message); + if(message.length > 300) { + wstool._out("server", "[s" + this._scount + "] [big message: " + message.length + " characters]"); + } else { + wstool._out("server", "[s" + this._scount + "] " + message); + } }, setState : function(enabled) { @@ -77,6 +85,10 @@ var wstool = { $('hello').disabled = !enabled; $('there').disabled = !enabled; $('json').disabled = !enabled; + $('send10k').disabled = !enabled; + $('send100k').disabled = !enabled; + $('send1000k').disabled = !enabled; + $('send10m').disabled = !enabled; }, _onopen : function() {