Is there any decent way to support safari's x-webkit-deflate-frame extension name for permessage-deflate?
AFAICT this is more or less an alias for permessage-deflate and I've just managed to get something working by rewriting incoming headers from Sec-WebSocket-Extensions: x-webkit-deflate-frame to Sec-WebSocket-Extension: permessage-deflate; client_max_window_bits and then rewriting associated outbound headers from Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover to Sec-WebSocket-Extensions: x-webkit-deflate-frame; no_context_takeover which seems to work in fairly limited testing but is very awkward to do properly.
I've done this using a horrible combination of httpServer.on('upgrade', verifyClient and socket.on('headers' to rewrite inbound and outbound headers but if there was any concurrency at all I'm pretty sure what I've done will fail and rewrite outbound headers on the wrong response.
Of course ideally safari would send the right extension name but even if they were to fix that it'll take a while before one could rely on it.
For the record, I've managed to do this with combination of httpServer.on('upgrade' and socket.on('headers' using the Sec-WebSocket-Key and Sec-WebSocket-Accept headers to associate a response with a specific request but a better api allowing this to be done would be great.
Any progress here? Supporting per-frame deflate is a huge win for mobile Safari.
I ended up improving on my previous solution to this problem by rewriting the request / response headers from safari with some lua blocks in my nginx config.
# request header filter - https://github.com/openresty/lua-nginx-module#rewrite_by_lua_block
rewrite_by_lua_block {
ngx.ctx.webkit_upgrade = false
websocket_extensions = ngx.req.get_headers()['sec-websocket-extensions']
if websocket_extensions == 'x-webkit-deflate-frame' then
ngx.ctx.webkit_upgrade = true
ngx.req.set_header('sec-websocket-extensions', 'permessage-deflate; client_max_window_bits')
ngx.log(ngx.INFO, 'rewrite webkit request headers // ', websocket_extensions, ' // ', ngx.req.get_headers()['sec-websocket-extensions'], ' //')
end
}
# response header filter - https://github.com/openresty/lua-nginx-module#header_filter_by_lua_block
header_filter_by_lua_block {
if ngx.ctx.webkit_upgrade then
websocket_extensions = ngx.header['sec-websocket-extensions']
ngx.header['sec-websocket-extensions'] = 'x-webkit-deflate-frame; no_context_takeover'
ngx.log(ngx.INFO, 'rewrite webkit response headers // ', websocket_extensions, ' // ', ngx.header['sec-websocket-extensions'], ' //')
end
}
Dear @dscherger Thanks for the response.
Do you mean we just need to rewrite the request and response headers for ws without modifying the ws source code?
Yes, that's correct. Safari on iOS seems to ask for compression with 'sec-websocket-extensions: x-webkit-deflate-frame' in the request headers and the ws library requires 'sec-websocket-extensions: permessage-deflate; client_max_window_bits' so rewriting the headers before the ws library sees the request seems to work. The associated response headers then need to be rewritten back into something safari understands, which iis 'sec-websocket-extensions: x-webkit-deflate-frame; no_context_takeover'.
No guarantees, but this does seem to work for me.
Thank you for the clarify. If that's the case, efforts to support iOS Safari should be relatively trivial than I expected. I just thought the Safari use per-frame deflate.
Most helpful comment
Yes, that's correct. Safari on iOS seems to ask for compression with 'sec-websocket-extensions: x-webkit-deflate-frame' in the request headers and the ws library requires 'sec-websocket-extensions: permessage-deflate; client_max_window_bits' so rewriting the headers before the ws library sees the request seems to work. The associated response headers then need to be rewritten back into something safari understands, which iis 'sec-websocket-extensions: x-webkit-deflate-frame; no_context_takeover'.
No guarantees, but this does seem to work for me.