Improve PerMessageDeflateClientExtensionHandler
(#11413)
Motivation: The `PerMessageDeflateClientExtensionHandler` has the following strange behaviors currently: * The `requestedServerNoContext` parameter doesn't actually add the `server_no_context_takeover` parameter to the client offer; instead it depends on the requested server window size. * The handshake will fail if the server responds with a `server_no_context_takeover` parameter and `requestedServerNoContext` is false. According to RFC 7692 (7.1.1.1) the server may do this, and this means that to cover both cases one needs to use two handshakers in the channel pipeline: one with `requestedServerNoContext = true` and one with `requestedServerNoContext = false`. * The value of the `server_max_window_bits` parameter in the server response is never checked (should be between 8 and 15). And the value of `client_max_window_bits` is checked only in the branch handling the server window parameter. Modification: * Add the `server_no_context_takeover` parameter if `requestedServerNoContext` is true. * Accept a server handshake response which includes the server no context takeover parameter even if we did not request it. * Check the values of the client and server window size in their respective branches and fail the handshake if they are out of bounds. Result: There will be no need to use two handshakers in the pipeline to be lenient in what handshakes are accepted.
This commit is contained in:
parent
842e73f8d3
commit
c97981403d
@ -117,7 +117,7 @@ public final class PerMessageDeflateClientExtensionHandshaker implements WebSock
|
|||||||
@Override
|
@Override
|
||||||
public WebSocketExtensionData newRequestData() {
|
public WebSocketExtensionData newRequestData() {
|
||||||
HashMap<String, String> parameters = new HashMap<String, String>(4);
|
HashMap<String, String> parameters = new HashMap<String, String>(4);
|
||||||
if (requestedServerWindowSize != MAX_WINDOW_SIZE) {
|
if (requestedServerNoContext) {
|
||||||
parameters.put(SERVER_NO_CONTEXT, null);
|
parameters.put(SERVER_NO_CONTEXT, null);
|
||||||
}
|
}
|
||||||
if (allowClientNoContext) {
|
if (allowClientNoContext) {
|
||||||
@ -153,13 +153,16 @@ public final class PerMessageDeflateClientExtensionHandshaker implements WebSock
|
|||||||
// allowed client_window_size_bits
|
// allowed client_window_size_bits
|
||||||
if (allowClientWindowSize) {
|
if (allowClientWindowSize) {
|
||||||
clientWindowSize = Integer.parseInt(parameter.getValue());
|
clientWindowSize = Integer.parseInt(parameter.getValue());
|
||||||
|
if (clientWindowSize > MAX_WINDOW_SIZE || clientWindowSize < MIN_WINDOW_SIZE) {
|
||||||
|
succeed = false;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
succeed = false;
|
succeed = false;
|
||||||
}
|
}
|
||||||
} else if (SERVER_MAX_WINDOW.equalsIgnoreCase(parameter.getKey())) {
|
} else if (SERVER_MAX_WINDOW.equalsIgnoreCase(parameter.getKey())) {
|
||||||
// acknowledged server_window_size_bits
|
// acknowledged server_window_size_bits
|
||||||
serverWindowSize = Integer.parseInt(parameter.getValue());
|
serverWindowSize = Integer.parseInt(parameter.getValue());
|
||||||
if (clientWindowSize > MAX_WINDOW_SIZE || clientWindowSize < MIN_WINDOW_SIZE) {
|
if (serverWindowSize > MAX_WINDOW_SIZE || serverWindowSize < MIN_WINDOW_SIZE) {
|
||||||
succeed = false;
|
succeed = false;
|
||||||
}
|
}
|
||||||
} else if (CLIENT_NO_CONTEXT.equalsIgnoreCase(parameter.getKey())) {
|
} else if (CLIENT_NO_CONTEXT.equalsIgnoreCase(parameter.getKey())) {
|
||||||
@ -171,11 +174,7 @@ public final class PerMessageDeflateClientExtensionHandshaker implements WebSock
|
|||||||
}
|
}
|
||||||
} else if (SERVER_NO_CONTEXT.equalsIgnoreCase(parameter.getKey())) {
|
} else if (SERVER_NO_CONTEXT.equalsIgnoreCase(parameter.getKey())) {
|
||||||
// acknowledged server_no_context_takeover
|
// acknowledged server_no_context_takeover
|
||||||
if (requestedServerNoContext) {
|
serverNoContext = true;
|
||||||
serverNoContext = true;
|
|
||||||
} else {
|
|
||||||
succeed = false;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// unknown parameter
|
// unknown parameter
|
||||||
succeed = false;
|
succeed = false;
|
||||||
|
@ -130,6 +130,63 @@ public class PerMessageDeflateClientExtensionHandshakerTest {
|
|||||||
assertNull(extension);
|
assertNull(extension);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParameterValidation() {
|
||||||
|
WebSocketClientExtension extension;
|
||||||
|
Map<String, String> parameters;
|
||||||
|
|
||||||
|
PerMessageDeflateClientExtensionHandshaker handshaker =
|
||||||
|
new PerMessageDeflateClientExtensionHandshaker(6, true, 15, true, false);
|
||||||
|
|
||||||
|
parameters = new HashMap<String, String>();
|
||||||
|
parameters.put(CLIENT_MAX_WINDOW, "15");
|
||||||
|
parameters.put(SERVER_MAX_WINDOW, "8");
|
||||||
|
extension = handshaker.handshakeExtension(new WebSocketExtensionData(PERMESSAGE_DEFLATE_EXTENSION, parameters));
|
||||||
|
|
||||||
|
// Test that handshake succeeds when parameters are valid
|
||||||
|
assertNotNull(extension);
|
||||||
|
assertEquals(RSV1, extension.rsv());
|
||||||
|
assertTrue(extension.newExtensionDecoder() instanceof PerMessageDeflateDecoder);
|
||||||
|
assertTrue(extension.newExtensionEncoder() instanceof PerMessageDeflateEncoder);
|
||||||
|
|
||||||
|
parameters = new HashMap<String, String>();
|
||||||
|
parameters.put(CLIENT_MAX_WINDOW, "15");
|
||||||
|
parameters.put(SERVER_MAX_WINDOW, "7");
|
||||||
|
|
||||||
|
extension = handshaker.handshakeExtension(new WebSocketExtensionData(PERMESSAGE_DEFLATE_EXTENSION, parameters));
|
||||||
|
|
||||||
|
// Test that handshake fails when parameters are invalid
|
||||||
|
assertNull(extension);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testServerNoContextTakeover() {
|
||||||
|
WebSocketClientExtension extension;
|
||||||
|
Map<String, String> parameters;
|
||||||
|
|
||||||
|
PerMessageDeflateClientExtensionHandshaker handshaker =
|
||||||
|
new PerMessageDeflateClientExtensionHandshaker(6, true, 15, true, false);
|
||||||
|
|
||||||
|
parameters = new HashMap<String, String>();
|
||||||
|
parameters.put(SERVER_NO_CONTEXT, null);
|
||||||
|
extension = handshaker.handshakeExtension(new WebSocketExtensionData(PERMESSAGE_DEFLATE_EXTENSION, parameters));
|
||||||
|
|
||||||
|
// Test that handshake succeeds when server responds with `server_no_context_takeover` that we didn't offer
|
||||||
|
assertNotNull(extension);
|
||||||
|
assertEquals(RSV1, extension.rsv());
|
||||||
|
assertTrue(extension.newExtensionDecoder() instanceof PerMessageDeflateDecoder);
|
||||||
|
assertTrue(extension.newExtensionEncoder() instanceof PerMessageDeflateEncoder);
|
||||||
|
|
||||||
|
// initialize
|
||||||
|
handshaker = new PerMessageDeflateClientExtensionHandshaker(6, true, 15, true, true);
|
||||||
|
|
||||||
|
parameters = new HashMap<String, String>();
|
||||||
|
extension = handshaker.handshakeExtension(new WebSocketExtensionData(PERMESSAGE_DEFLATE_EXTENSION, parameters));
|
||||||
|
|
||||||
|
// Test that handshake fails when client offers `server_no_context_takeover` but server doesn't support it
|
||||||
|
assertNull(extension);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDecoderNoClientContext() {
|
public void testDecoderNoClientContext() {
|
||||||
PerMessageDeflateClientExtensionHandshaker handshaker =
|
PerMessageDeflateClientExtensionHandshaker handshaker =
|
||||||
|
Loading…
Reference in New Issue
Block a user