Skip to content

Commit 90bde82

Browse files
committed
Fix Per-Message Compression Extension support
The per-message compression extension was badly broken, leading to exceptions and loss of communication between client and server. Fix the extension negotiation to correctly send the current state of the client_no_context_takeover and server_no_context_takeover configuration variables, and ORing the received extensions with the received ones, to ensure client and server end up with the same configuration. Fix the application of these configuration variables, which must be swapped on the client vs. the server: The configuration of the deflater on the client side must affect the inflater on the server side, and vice versa. Remove the requestedParameters map as there is no need for it. This fixes issue #1496
1 parent 8c5766a commit 90bde82

File tree

1 file changed

+34
-18
lines changed

1 file changed

+34
-18
lines changed

src/main/java/org/java_websocket/extensions/permessage_deflate/PerMessageDeflateExtension.java

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import java.io.ByteArrayOutputStream;
44
import java.nio.ByteBuffer;
5-
import java.util.LinkedHashMap;
65
import java.util.Map;
76
import java.util.zip.DataFormatException;
87
import java.util.zip.Deflater;
@@ -47,15 +46,14 @@ public class PerMessageDeflateExtension extends CompressionExtension {
4746
private boolean serverNoContextTakeover = true;
4847
private boolean clientNoContextTakeover = false;
4948

50-
// For WebSocketServers, this variable holds the extension parameters that the peer client has requested.
51-
// For WebSocketClients, this variable holds the extension parameters that client himself has requested.
52-
private Map<String, String> requestedParameters = new LinkedHashMap<>();
53-
5449
private final int compressionLevel;
5550

5651
private final Inflater inflater;
5752
private final Deflater deflater;
5853

54+
private boolean resetDeflater = false;
55+
private boolean resetInflater = false;
56+
5957
/**
6058
* Constructor for the PerMessage Deflate Extension (<a href="https://tools.ietf.org/html/rfc7692#section-7">7&#46; Thepermessage-deflate" Extension</a>)
6159
*
@@ -184,8 +182,8 @@ We can check the getRemaining() method to see whether the data we supplied has b
184182

185183
if (inputFrame.isFin()) {
186184
decompress(TAIL_BYTES, output);
187-
// If context takeover is disabled, inflater can be reset.
188-
if (clientNoContextTakeover) {
185+
// If context takeover is disabled for the other side, inflater must be reset.
186+
if (resetInflater) {
189187
inflater.reset();
190188
}
191189
}
@@ -254,8 +252,8 @@ public void encodeFrame(Framedata inputFrame) {
254252
if (endsWithTail(outputBytes)) {
255253
outputLength -= TAIL_BYTES.length;
256254
}
257-
258-
if (serverNoContextTakeover) {
255+
// If context takeover is disabled for this side, deflater must be reset.
256+
if (resetDeflater) {
259257
deflater.reset();
260258
}
261259
}
@@ -294,11 +292,19 @@ public boolean acceptProvidedExtensionAsServer(String inputExtension) {
294292

295293
// Holds parameters that peer client has sent.
296294
Map<String, String> headers = extensionData.getExtensionParameters();
297-
requestedParameters.putAll(headers);
298-
if (requestedParameters.containsKey(CLIENT_NO_CONTEXT_TAKEOVER)) {
295+
if (headers.containsKey(SERVER_NO_CONTEXT_TAKEOVER)) {
296+
serverNoContextTakeover = true;
297+
}
298+
if (headers.containsKey(CLIENT_NO_CONTEXT_TAKEOVER)) {
299299
clientNoContextTakeover = true;
300300
}
301301

302+
// RFC 7692:
303+
// server_ prefix parameters configure the server compressor (deflater)
304+
// client_ prefix parameters configure the server decompressor (inflater)
305+
resetDeflater = serverNoContextTakeover;
306+
resetInflater = clientNoContextTakeover;
307+
302308
return true;
303309
}
304310

@@ -316,7 +322,19 @@ public boolean acceptProvidedExtensionAsClient(String inputExtension) {
316322

317323
// Holds parameters that are sent by the server, as a response to our initial extension request.
318324
Map<String, String> headers = extensionData.getExtensionParameters();
319-
// After this point, parameters that the server sent back can be configured, but we don't use them for now.
325+
if (headers.containsKey(SERVER_NO_CONTEXT_TAKEOVER)) {
326+
serverNoContextTakeover = true;
327+
}
328+
if (headers.containsKey(CLIENT_NO_CONTEXT_TAKEOVER)) {
329+
clientNoContextTakeover = true;
330+
}
331+
332+
// RFC 7692:
333+
// client_ prefix parameters configure the client compressor (deflater)
334+
// server_ prefix parameters configure the client decompressor (inflater)
335+
resetDeflater = clientNoContextTakeover;
336+
resetInflater = serverNoContextTakeover;
337+
320338
return true;
321339
}
322340

@@ -325,17 +343,15 @@ public boolean acceptProvidedExtensionAsClient(String inputExtension) {
325343

326344
@Override
327345
public String getProvidedExtensionAsClient() {
328-
requestedParameters.put(CLIENT_NO_CONTEXT_TAKEOVER, ExtensionRequestData.EMPTY_VALUE);
329-
requestedParameters.put(SERVER_NO_CONTEXT_TAKEOVER, ExtensionRequestData.EMPTY_VALUE);
330-
331-
return EXTENSION_REGISTERED_NAME + "; " + SERVER_NO_CONTEXT_TAKEOVER + "; "
332-
+ CLIENT_NO_CONTEXT_TAKEOVER;
346+
return EXTENSION_REGISTERED_NAME
347+
+ (serverNoContextTakeover ? "; " + SERVER_NO_CONTEXT_TAKEOVER : "")
348+
+ (clientNoContextTakeover ? "; " + CLIENT_NO_CONTEXT_TAKEOVER : "");
333349
}
334350

335351
@Override
336352
public String getProvidedExtensionAsServer() {
337353
return EXTENSION_REGISTERED_NAME
338-
+ "; " + SERVER_NO_CONTEXT_TAKEOVER
354+
+ (serverNoContextTakeover ? "; " + SERVER_NO_CONTEXT_TAKEOVER : "")
339355
+ (clientNoContextTakeover ? "; " + CLIENT_NO_CONTEXT_TAKEOVER : "");
340356
}
341357

0 commit comments

Comments
 (0)