Skip to content

server: Server::with_connection_extensions — populate ConnectionInfo once per connection - #304

Merged
iainmcgin merged 2 commits into
mainfrom
rpb/connection-extensions
Sep 18, 2026
Merged

iainmcgin merged 2 commits into
mainfrom
rpb/connection-extensions

Conversation

@rpb-ant

@rpb-ant rpb-ant commented Sep 14, 2026

Copy link
Copy Markdown
Collaborator

Per-connection facts, such as an identity parsed from the client certificate, are computed once after the handshake instead of on every request:

connectrpc::axum::serve_tls(listener, app, tls) // or Server / BoundServer
    .with_connection_extensions(|conn, ext| {
        ext.insert(PeerIdentity::from_connection(conn)); // cloned into every request
    })
  • New on Server, BoundServer and connectrpc::axum::Serve: with_connection_extensions(impl Fn(&ConnectionInfo, &mut http::Extensions) + Send + Sync + 'static). The function reads the connection and fills an empty map, whose entries join the connection's extensions. It cannot remove an entry or change the peer: PeerAddr / PeerCerts still come only from the ConnectionInfo peer fields. It is synchronous and cannot refuse a connection; a custom loop around Server::serve_connection does that.
  • It runs once per connection, on the connection's task, after the TLS handshake and before the first request, both in the shared accept loop and inside Server::serve_connection's future. The free server::serve_connection does not run it. A panic in it is logged and drops only that connection, which Server::serve_connection reports as CloseReason::Error; under panic = "abort" it aborts the process.
  • The function is stored in a newtype that asserts UnwindSafe / RefUnwindSafe, so Server, BoundServer and axum::Serve keep their auto traits.

@rpb-ant
rpb-ant force-pushed the rpb/connection-extensions branch from 2dc4d79 to a967b1f Compare September 15, 2026 17:28
@rpb-ant rpb-ant changed the title server: connection-scoped request extensions (ConnectionInfo, with_connection_extensions) server: Server::with_connection_extensions — populate ConnectionInfo once per connection Sep 15, 2026
@rpb-ant
rpb-ant changed the base branch from main to rpb/server-layers September 15, 2026 17:28
@rpb-ant
rpb-ant force-pushed the rpb/connection-extensions branch from a967b1f to 09887c0 Compare September 16, 2026 03:20
@rpb-ant
rpb-ant changed the base branch from rpb/server-layers to rpb/c-serve-connection September 16, 2026 03:20
@iainmcgin
iainmcgin force-pushed the rpb/connection-extensions branch from 09887c0 to 7e75d33 Compare September 17, 2026 04:48
@iainmcgin
iainmcgin force-pushed the rpb/c-serve-connection branch from 4da9e4a to 7648589 Compare September 17, 2026 04:48
@iainmcgin
iainmcgin force-pushed the rpb/c-serve-connection branch from 7648589 to f4cfeac Compare September 18, 2026 05:41
@iainmcgin
iainmcgin force-pushed the rpb/connection-extensions branch 2 times, most recently from 6dbe05b to 6eb8c6c Compare September 18, 2026 05:49
@iainmcgin
iainmcgin force-pushed the rpb/c-serve-connection branch from f4cfeac to 86f528e Compare September 18, 2026 05:49
Base automatically changed from rpb/c-serve-connection to main September 18, 2026 05:56
rpb-ant and others added 2 commits September 17, 2026 22:57
…fo` once per connection

The built-in accept loop gains one optional step between the handshake
and `serve_connection`: a `Fn(&mut ConnectionInfo) + Send + Sync`
registered with `with_connection_extensions` on `Server`, `BoundServer`
or `connectrpc::axum::Serve` (one code path, the shared accept loop;
`Server::serve_connection` applies it too). It runs once per accepted
connection, on that connection's task, and whatever it puts in
`extensions_mut()` rides into every request — the same thing a custom
loop does by hand, now available without writing one. `PeerAddr` /
`PeerCerts` still always come from the transport. The function is held
in a crate-private newtype that asserts unwind safety so the holders
keep their auto traits.

The mtls-identity example parses the client certificate into a
`PeerIdentity` once per connection instead of per request. Tests: runs
once per connection across unary and streaming requests on one HTTP/2
connection and again for the next; absent when unset; sees the verified
chain over mTLS and cannot forge `PeerCerts` or `PeerAddr`;
`Server::serve_connection` applies it; parity through `axum::serve_tls`.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BgcfmMV1ECyzhbqGvUV1a7
Signed-off-by: Ryan Brewster <rpb@anthropic.com>
…nInfo

The function registered with `with_connection_extensions` took
`&mut ConnectionInfo`, and `ConnectionInfo` is `Default` with public
`with_peer_addr` / `with_peer_certs`, so
`*conn = std::mem::take(conn).with_peer_addr(spoof)` replaced the peer
the transport observed, although the docs promise the function cannot
change the peer.

The signature is now `Fn(&ConnectionInfo, &mut http::Extensions)`: the
function reads the connection (including extensions a custom loop
inserted) and whatever it inserts joins the connection's extensions.
Changing this after release would be breaking. The `UnwindSafe`
rationale now names the holder the assertion preserves
(`BoundServer` without `server-tls`).

A panic in the function is now caught and logged: the connection is
dropped, and `Server::serve_connection` reports `CloseReason::Error`
instead of the panic escaping its future.

Signed-off-by: Iain McGinniss <309153+iainmcgin@users.noreply.github.com>
@iainmcgin
iainmcgin force-pushed the rpb/connection-extensions branch from 6eb8c6c to 7cceb7a Compare September 18, 2026 05:58
@iainmcgin
iainmcgin marked this pull request as ready for review September 18, 2026 05:58

@iainmcgin iainmcgin left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[claude code] Approving under the maintainers' agreement to review and merge each other's PRs. Rebased onto main after #308 merged; the added fix commits went through code, API, doc and deslop review.

@iainmcgin
iainmcgin enabled auto-merge September 18, 2026 05:58
@iainmcgin
iainmcgin added this pull request to the merge queue Sep 18, 2026
Merged via the queue into main with commit aaa71f6 Sep 18, 2026
14 checks passed
@iainmcgin
iainmcgin deleted the rpb/connection-extensions branch September 18, 2026 06:04
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