You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
permessage-deflate: decide whether to reset the inflater when the peer agreed to no context takeover #3118
When the peer agreed to no context takeover (server_no_context_takeover for a client, client_no_context_takeover for a server), do_context_takeover_read in websocket/detail/impl_base.hpp is meant to reset the inflater after each message. Since Boost 1.70 that call has been a no-op, so Beast keeps the peer's window and silently accepts peers that break the promise.
#3114 makes zlib::inflate_stream::clear() work, which would change this to rejecting such peers, plus one allocation per message. To keep #3114 a pure port, the call is commented out there with a pointer to this issue. Behaviour with the call commented out matches develop.
History
Boost ≤ 1.69:pmd_->zi.reset() emptied the window and kept the buffer, so violating peers were rejected.
8ea282e (Boost 1.70): deprecated flat_static_buffer::reset in favour of clear and renamed the websocket's buffer calls. pmd_->zi.reset() was renamed along with them, but zi is a zlib::inflate_stream, whose clear() had been empty since 5089cf5 (2016). From 1.70 on, Beast accepts violating peers, by accident.
Port zlib streams to zlib 1.3.2 #3114: implements clear() as documented (frees the window and resets the state), which would make Beast strict again.
RFC 7692
§7.2.1: when the parameter is agreed, the sender MUST start compressing each new message with an empty window.
§7.2.2: the receiver MAY then decompress each new message with an empty window; otherwise it MUST use the window from the previous message. Rejecting and accepting a violating peer are both conforming.
§7.1.1.1 / §7.1.1.2: the parameters exist so the receiver does not have to reserve window memory between messages.
Decisions
Reject or accept peers that agree to no context takeover but still refer back to earlier messages.
If rejecting: keep the window buffer or free it between messages.
Measured on #3114 with GCC 13 at -O2. The deflater (~145 KB at websocket defaults) is unaffected: zo.reset() keeps its buffers either way. A rejected message fails the connection, as any other inflate error does.
Summary
When the peer agreed to no context takeover (
server_no_context_takeoverfor a client,client_no_context_takeoverfor a server),do_context_takeover_readinwebsocket/detail/impl_base.hppis meant to reset the inflater after each message. Since Boost 1.70 that call has been a no-op, so Beast keeps the peer's window and silently accepts peers that break the promise.#3114 makes
zlib::inflate_stream::clear()work, which would change this to rejecting such peers, plus one allocation per message. To keep #3114 a pure port, the call is commented out there with a pointer to this issue. Behaviour with the call commented out matches develop.History
pmd_->zi.reset()emptied the window and kept the buffer, so violating peers were rejected.flat_static_buffer::resetin favour ofclearand renamed the websocket's buffer calls.pmd_->zi.reset()was renamed along with them, butziis azlib::inflate_stream, whoseclear()had been empty since 5089cf5 (2016). From 1.70 on, Beast accepts violating peers, by accident.clear()as documented (frees the window and resets the state), which would make Beast strict again.RFC 7692
Decisions
zi.reset()(≤ 1.69)zlib::error::invalid_distance)zi.clear()zlib::error::invalid_distance)Measured on #3114 with GCC 13 at -O2. The deflater (~145 KB at websocket defaults) is unaffected:
zo.reset()keeps its buffers either way. A rejected message fails the connection, as any other inflate error does.Other implementations
The tolerant implementations all still reset their own compressor when they agreed to no context takeover.