Skip to content

Guard get_state liveness probe during pooled H2/H3 checkout (#914)#915

Open
benoitc wants to merge 1 commit into
masterfrom
fix/914-guard-get_state-checkout
Open

Guard get_state liveness probe during pooled H2/H3 checkout (#914)#915
benoitc wants to merge 1 commit into
masterfrom
fix/914-guard-get_state-checkout

Conversation

@benoitc

@benoitc benoitc commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Fixes #914.

Reusing a pooled HTTP/2 or HTTP/3 connection probes it with hackney_conn:get_state/1, a bare gen_statem:call. If the pooled connection is terminating at that instant (idle teardown, GOAWAY, keepalive close) the call exits. The surrounding case only handled return values, so the exit propagated and crashed the caller of hackney:connect/4 ({noproc,{gen_statem,call,[Pid,get_state]}}) instead of falling through to a fresh connection.

The same file already guards the sibling probes for this exact race in maybe_register_h2 and maybe_upgrade_ssl; the two get_state checkout probes were missed.

Change

Wrap both checkout probes in try ... catch exit:_ -> {error, terminated} end (H2 site in connect_pool/4, H3 site in try_h3_connection/5). A terminating connection is treated as unusable and checkout falls through to the existing unregister + new-connection path. Only exit is caught; error/throw would be a genuine bug and still propagate.

Tests

New hackney_connect_race_tests with a hackney_race_pool handler that hands out an already-terminated pid on H2/H3 checkout. H2 asserts hackney:connect/4 returns {error, _}; H3 asserts the get_state probe exit does not escape. Reverting the guard makes both fail with the {noproc,...get_state...} exit. Full suite (1054 tests) passes.

Reusing a pooled HTTP/2 or HTTP/3 connection probes it with
hackney_conn:get_state/1, a bare gen_statem:call. If the connection is
terminating at that instant (idle teardown, GOAWAY, keepalive close) the
call exits, and the surrounding case only handled return values, so the
exit crashed the caller of hackney:connect/4 instead of falling through
to a fresh connection.

Wrap both checkout probes in try/catch exit, mirroring the existing race
handling in maybe_register_h2 and maybe_upgrade_ssl. A terminating
connection is now treated as unusable and the checkout opens a fresh one.
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.

connect_pool: unguarded hackney_conn:get_state/1 crashes checkout when a pooled HTTP/2/3 connection terminates mid-call

1 participant