Question

I have this problem with compression, and I am not sure if it is a bug. My WebSocket server does not support context takeover, and I am having problems sending messages, but not receiving.

The browser issues a request like this:

GET /socket HTTP/1.1
Host: thirdparty.com
Origin: http://example.com
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits, x-webkit-deflate-frame

If the server does not specify any option about context takeover:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Access-Control-Allow-Origin: http://example.com
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Extensions: permessage-deflate

I can read and write the first message, but cannot do subsequent reads or writes, because Chrome expects the server is keeping the context.

So my server provides this answer:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Access-Control-Allow-Origin: http://example.com
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Extensions: permessage-deflate; client_no_context_takeover; server_no_context_takeover

And now I can receive messages without problems, but again, I can only send the first message, the second message fails, and I see an error in Chrome saying that it failed at inflating the frame. I tried to send two identical strings, and I can see how the server is sending twice the same data, but the client fails to decompress it the second time.

So it seems that Chrome accepts the client_no_context_takeover parameter that specify that the client won't use the same compression context for all messages when compressing, but ignores server_no_context_takeover indicates the server won't use the same context.

Is this a bug in Chrome? I am not clear about if I can send options back that have not been offered/requested by the client.

Is there any other option I can use to disable the client context takeover?

UPDATE:

In WebSocketPerMessageDeflate.cpp in the Chromium source code, I can see:

if (clientNoContextTakeover != parameters.end()) {
    if (!clientNoContextTakeover->value.isNull()) {
        m_failureReason = "Received invalid client_no_context_takeover parameter";
        return false;
    }
    mode = WebSocketDeflater::DoNotTakeOverContext;
    ++numProcessedParameters;
}

But also:

if (serverNoContextTakeover != parameters.end()) {
    if (!serverNoContextTakeover->value.isNull()) {
        m_failureReason = "Received invalid server_no_context_takeover parameter";
        return false;
    }
    ++numProcessedParameters;
}

In the first snippet, it is setting the "mode" variable, but in the second one is not doing nothing, so it seems it is basically ignoring the parameter.

Cheers.

Was it helpful?

Solution 2

I finally found the problem.

https://datatracker.ietf.org/doc/html/draft-ietf-hybi-permessage-compression-17#section-8.2.3

8.2.3.4. Using a DEFLATE Block with BFINAL Set to 1

Going through the examples in the draft I found my server was sending slightly different payloads. Turned out that the problem was the BFINAL, I need to set it to 0 by adding a 0 byte at the end.

Now it works.

OTHER TIPS

A server must send a server_no_context_takeover parameter in a response only if the client requested the "no context takeover". In essence, the server acknowledges the client's request.

If a server decides to do "no context takeover" for sending on it's own (without the client having requested it), that's fine. In this case, no parameter is sent by the server.

A deflate sender can always on it's own drop compression context and/or reduce compression window size. There is no need to tell the receiver. The deflate wire format has enough information for the receiver to cope with that.

Here is how configuration and handshake looks with Crossbar.io.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top