diff --git a/src/main/java/org/java_websocket/extensions/permessage_deflate/PerMessageDeflateExtension.java b/src/main/java/org/java_websocket/extensions/permessage_deflate/PerMessageDeflateExtension.java index 9eb16ca1..a04054cb 100644 --- a/src/main/java/org/java_websocket/extensions/permessage_deflate/PerMessageDeflateExtension.java +++ b/src/main/java/org/java_websocket/extensions/permessage_deflate/PerMessageDeflateExtension.java @@ -2,7 +2,6 @@ import java.io.ByteArrayOutputStream; import java.nio.ByteBuffer; -import java.util.LinkedHashMap; import java.util.Map; import java.util.zip.DataFormatException; import java.util.zip.Deflater; @@ -47,15 +46,14 @@ public class PerMessageDeflateExtension extends CompressionExtension { private boolean serverNoContextTakeover = true; private boolean clientNoContextTakeover = false; - // For WebSocketServers, this variable holds the extension parameters that the peer client has requested. - // For WebSocketClients, this variable holds the extension parameters that client himself has requested. - private Map requestedParameters = new LinkedHashMap<>(); - private final int compressionLevel; private final Inflater inflater; private final Deflater deflater; + private boolean resetDeflater = false; + private boolean resetInflater = false; + /** * Constructor for the PerMessage Deflate Extension (7. Thepermessage-deflate" Extension) * @@ -184,8 +182,8 @@ We can check the getRemaining() method to see whether the data we supplied has b if (inputFrame.isFin()) { decompress(TAIL_BYTES, output); - // If context takeover is disabled, inflater can be reset. - if (clientNoContextTakeover) { + // If context takeover is disabled on the other end, inflater can be reset. + if (resetInflater) { inflater.reset(); } } @@ -254,8 +252,8 @@ public void encodeFrame(Framedata inputFrame) { if (endsWithTail(outputBytes)) { outputLength -= TAIL_BYTES.length; } - - if (serverNoContextTakeover) { + // If context takeover is disabled on this end, deflater can be reset. + if (resetDeflater) { deflater.reset(); } } @@ -294,11 +292,19 @@ public boolean acceptProvidedExtensionAsServer(String inputExtension) { // Holds parameters that peer client has sent. Map headers = extensionData.getExtensionParameters(); - requestedParameters.putAll(headers); - if (requestedParameters.containsKey(CLIENT_NO_CONTEXT_TAKEOVER)) { + if (headers.containsKey(SERVER_NO_CONTEXT_TAKEOVER)) { + serverNoContextTakeover = true; + } + if (headers.containsKey(CLIENT_NO_CONTEXT_TAKEOVER)) { clientNoContextTakeover = true; } + // RFC 7692: + // server_ prefix parameters configure the server compressor (deflater) + // client_ prefix parameters configure the server decompressor (inflater) + resetDeflater = serverNoContextTakeover; + resetInflater = clientNoContextTakeover; + return true; } @@ -316,7 +322,25 @@ public boolean acceptProvidedExtensionAsClient(String inputExtension) { // Holds parameters that are sent by the server, as a response to our initial extension request. Map headers = extensionData.getExtensionParameters(); - // After this point, parameters that the server sent back can be configured, but we don't use them for now. + if (headers.containsKey(SERVER_NO_CONTEXT_TAKEOVER)) { + serverNoContextTakeover = true; + } else { + // If the server does not return server_no_context_takeover, the client must not reset the + // decompressor (inflater) because that would break communication. Note that in contrast, + // the client can reset the compressor (deflater) even if the server does not reset the + // decompressor (inflater), so this is not required for client_no_context_takeover below. + serverNoContextTakeover = false; + } + if (headers.containsKey(CLIENT_NO_CONTEXT_TAKEOVER)) { + clientNoContextTakeover = true; + } + + // RFC 7692: + // client_ prefix parameters configure the client compressor (deflater) + // server_ prefix parameters configure the client decompressor (inflater) + resetDeflater = clientNoContextTakeover; + resetInflater = serverNoContextTakeover; + return true; } @@ -325,17 +349,15 @@ public boolean acceptProvidedExtensionAsClient(String inputExtension) { @Override public String getProvidedExtensionAsClient() { - requestedParameters.put(CLIENT_NO_CONTEXT_TAKEOVER, ExtensionRequestData.EMPTY_VALUE); - requestedParameters.put(SERVER_NO_CONTEXT_TAKEOVER, ExtensionRequestData.EMPTY_VALUE); - - return EXTENSION_REGISTERED_NAME + "; " + SERVER_NO_CONTEXT_TAKEOVER + "; " - + CLIENT_NO_CONTEXT_TAKEOVER; + return EXTENSION_REGISTERED_NAME + + (serverNoContextTakeover ? "; " + SERVER_NO_CONTEXT_TAKEOVER : "") + + (clientNoContextTakeover ? "; " + CLIENT_NO_CONTEXT_TAKEOVER : ""); } @Override public String getProvidedExtensionAsServer() { return EXTENSION_REGISTERED_NAME - + "; " + SERVER_NO_CONTEXT_TAKEOVER + + (serverNoContextTakeover ? "; " + SERVER_NO_CONTEXT_TAKEOVER : "") + (clientNoContextTakeover ? "; " + CLIENT_NO_CONTEXT_TAKEOVER : ""); } diff --git a/src/test/java/org/java_websocket/extensions/PerMessageDeflateExtensionTest.java b/src/test/java/org/java_websocket/extensions/PerMessageDeflateExtensionTest.java index 5a86648f..f7e7da56 100644 --- a/src/test/java/org/java_websocket/extensions/PerMessageDeflateExtensionTest.java +++ b/src/test/java/org/java_websocket/extensions/PerMessageDeflateExtensionTest.java @@ -214,7 +214,7 @@ public void testIsFrameValid() { @Test public void testGetProvidedExtensionAsClient() { PerMessageDeflateExtension deflateExtension = new PerMessageDeflateExtension(); - assertEquals("permessage-deflate; server_no_context_takeover; client_no_context_takeover", + assertEquals("permessage-deflate; server_no_context_takeover", deflateExtension.getProvidedExtensionAsClient()); } @@ -242,6 +242,8 @@ public void testSetServerNoContextTakeover() { PerMessageDeflateExtension deflateExtension = new PerMessageDeflateExtension(); deflateExtension.setServerNoContextTakeover(false); assertFalse(deflateExtension.isServerNoContextTakeover()); + assertEquals("permessage-deflate", + deflateExtension.getProvidedExtensionAsServer()); } @Test @@ -255,6 +257,8 @@ public void testSetClientNoContextTakeover() { PerMessageDeflateExtension deflateExtension = new PerMessageDeflateExtension(); deflateExtension.setClientNoContextTakeover(true); assertTrue(deflateExtension.isClientNoContextTakeover()); + assertEquals("permessage-deflate; server_no_context_takeover; client_no_context_takeover", + deflateExtension.getProvidedExtensionAsClient()); } @Test