Skip to content

Fix/connection frame handling - #161

Merged
mkuratczyk merged 7 commits into
masterfrom
fix/connection-frame-handling
Oct 16, 2026
Merged

mkuratczyk merged 7 commits into
masterfrom
fix/connection-frame-handling

Conversation

@mkuratczyk

Copy link
Copy Markdown
Collaborator

A collection of small improvements.

The reader allocated the entire declared body before receiving its data,
even when the peer sent only a partial body or closed the connection.
Content lengths outside the native int range could also wrap on 32-bit
platforms.

Start with a small buffer and grow it as data arrives. Reject lengths
that cannot fit in int, cover allocation behavior on both integer sizes,
and clarify that the reader buffer size does not impose a frame limit.
Removing content-length allowed bodies containing NULL octets to be
written even though the receiver treats NULL as the end of such a body.
Explicit content-length headers could also disagree with the body size.

Validate body framing before writing any bytes, and before enqueueing
Send or Transaction.Send requests so callers receive an immediate error.
Keep support for binary bodies with a matching content-length and text
bodies without one.
After a receipt wait timed out, a late reply could block processLoop on
an unbuffered channel with no receiver. Pending waits could also remain
blocked after connection closure, and Send held the connection mutex
while waiting for a receipt.

Buffer receipt completion, remove expired waiters, and notify pending
waits when the connection closes. Release the mutex before waiting and
track receipts separately from subscriptions. Prefer an already delivered
response over a simultaneous close notification so its result is retained.
DialWithContext used net.Dial, and the handshake applied a context deadline
only to reads after writing CONNECT. Cancellation without a deadline was
not observed, so dialing or a blocked handshake write could outlive ctx.

Use context-aware dialing and close the supplied stream when the context
is canceled during handshake I/O. Stop the cancellation callback when the
exchange finishes so later cancellation leaves the connection usable.
Cover blocked reads and writes for both net.Conn and wrapped streams.
UNSUBSCRIBE replaced a caller's receipt ID with the subscription ID after
registering the receipt waiter under the original value. When both IDs
matched, receipt dispatch completed only the request waiter, leaving the
subscription wait unfinished despite the broker's confirmation.

Preserve the caller's receipt ID and map it to the subscription separately.
Complete both waits when the receipt arrives. Adapt the mapping from
e4d68cc to the separate receipt and subscription maps, with coverage for
both matching and distinct IDs and disabled unsubscribe timeouts.
@mkuratczyk
mkuratczyk force-pushed the fix/connection-frame-handling branch from d981c86 to 6be3cf1 Compare October 14, 2026 13:08
Comment thread conn.go Outdated
Comment thread frame/frame.go
Comment thread frame/header.go Outdated
Comment thread conn.go
processLoop replaced any receipt header on an UNSUBSCRIBE frame with
the subscription id. sendFrame had already registered a waiter under
the caller's receipt id, so that waiter never got its RECEIPT and
Unsubscribe blocked until the connection closed.

Keep the caller's receipt and, when its RECEIPT arrives, complete
both the waiter and the subscription channel.
@mkuratczyk
mkuratczyk requested a review from Zerpet October 16, 2026 08:12
@mkuratczyk
mkuratczyk merged commit d2c22e7 into master Oct 16, 2026
4 checks passed
@mkuratczyk
mkuratczyk deleted the fix/connection-frame-handling branch October 16, 2026 08:18
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