Skip to content

Conversation

@shinny-yangyang
Copy link

Root cause

  • When finishing a permessage-deflate message, Beast always appended the 4‑byte RFC7692 tail (00 00 FF FF) but did not track how many of those bytes zlib had consumed. With very small avail_out (e.g. 1 byte), zlib could consume only part of the tail and the next iteration would re-append from the beginning, corrupting the stored block length and producing "invalid stored block length".

Fix

  • Track per-message tail consumption (0..4) and only feed the remaining portion of the tail. After all 4 bytes are consumed, continue calling inflate with avail_in=0 to naturally finish. Reset the counter on new messages and at context takeover boundaries. Also treat zlib::error::need_buffers as non-fatal in the inflate wrapper.

Changes

  • impl_base.hpp (deflateSupported=true): add pmd_type::rd_tail_in, reset in rd_deflated() and do_context_takeover_read(); add rd_tail_in()/rd_tail_in_add() helpers; clear need_buffers in inflate().
  • impl_base.hpp (deflateSupported=false): add no-op stubs for rd_tail_in helpers.
  • read.hpp: in both async and sync read paths, append only the unconsumed tail bytes, accumulate consumed bytes from zs.total_in, and finish the message when fin && zs.total_out == 0.
  • example: add repro CMake target (example/repro), with server_pmd_writer and client_smallbuf to reproduce and validate the fix.

Validation

  • Repro shows stable reception with 1‑byte reads and no inflate errors; 200 messages received cleanly and stream closes gracefully.

Compatibility

  • Only affects PMD-enabled compressed reads; non-PMD and write paths unchanged.

…use: when finishing a permessage-deflate (PMD) message, Beast always appended the 4-byte RFC7692 tail (00 00 FF FF) but did not track how many of those bytes zlib had consumed. With very small avail_out (e.g. 1 byte), zlib could consume only part of the tail and the next iteration would re-append from the beginning, corrupting the stored block length and producing "invalid stored block length".\n\nFix: track per-message tail consumption (0..4) and only feed the remaining portion of the tail. After all 4 bytes are consumed, continue calling inflate with avail_in=0 to naturally finish. Reset the counter on new messages and at context takeover boundaries. Also treat zlib::error::need_buffers as non-fatal in the inflate wrapper.\n\nChanges:\n- impl_base.hpp (deflateSupported=true): add pmd_type::rd_tail_in, reset in rd_deflated() and do_context_takeover_read(); add rd_tail_in()/rd_tail_in_add() helpers; clear need_buffers in inflate().\n- impl_base.hpp (deflateSupported=false): add no-op stubs for rd_tail_in helpers.\n- read.hpp: in both async and sync read paths, append only the unconsumed tail bytes, accumulate consumed bytes from zs.total_in, and finish the message when fin && zs.total_out == 0.\n- example: add repro CMake target (repro/), with server_pmd_writer and client_smallbuf to reproduce and validate the fix.\n\nValidation: repro shows stable reception with 1-byte reads and no inflate errors; 200 messages received cleanly and stream closes gracefully.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants