Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions .changes/unreleased/Added-20260916-120000.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ body: |-
HTTP/2 settings, keepalive, header-read timeout, max age / idle / request
retirement, graceful GOAWAY on `shutdown`, panic isolation — and resolves
to a `ConnectionClosed` whose `reason()` says how it ended
(`CloseReason::{Closed, Shutdown, MaxAge, Idle, MaxRequests, Error}`).
It runs on whichever runtime polls it; a tokio socket's I/O stays on the
runtime that created it until moved with `into_std` /
`TcpStream::from_std`. The new public `ConnectionInfo` (also at the crate
root) describes the connection: `peer_addr()`, `peer_certs()`, and
connection-scoped `extensions_mut()` that the loop fills once — a parsed
client-certificate identity, say — and that are cloned into every request
on the connection. `PeerAddr` / `PeerCerts` on requests always come from
(`CloseReason::{Closed, Shutdown, MaxAge, Idle, MaxRequests, Error}`) and
whose `error()` / `error_arc()` return the error that ended it, including
one hit while draining. It runs on whichever runtime polls it; a tokio
socket's I/O stays on the runtime that created it until moved with
`into_std` / `TcpStream::from_std`. The new public `ConnectionInfo` (also
at the crate root) describes the connection: `peer_addr()`,
`peer_certs()`, and connection-scoped `extensions_mut()` that the loop
fills once — a parsed client-certificate identity, say — and that are
cloned into every request on the connection. `PeerAddr` / `PeerCerts` on requests always come from
`ConnectionInfo`'s typed fields, never from its extensions. A loop that
admits connections by client identity, caps them per tenant, or places
them on different runtimes keeps every guarantee of `Server::serve`
Expand Down
7 changes: 7 additions & 0 deletions .changes/unreleased/Added-20260918-170000.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
kind: Added
body: |-
**`connectrpc::http`: re-export of the `http` crate.** Its types appear
throughout the public API — `http::Extensions` in the
`with_connection_extensions` function, header maps on requests and
responses — so code can name them without its own `http` dependency.
time: 2026-09-18T17:00:00.000000000+00:00
11 changes: 4 additions & 7 deletions .changes/unreleased/Fixed-20260916-140500.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
kind: Fixed
body: |-
**Idle reaping and file-descriptor exhaustion.** With
**Idle reaping no longer closes a streaming response.** With
`with_max_connection_idle` set, a request now counts as in flight until
its response body has been sent, so a long streaming response is no
longer closed as idle. As with gRPC's `MaxConnectionIdle`, a client that
stops reading now keeps its connection until `with_max_connection_age`
retires it. After `EMFILE` / `ENFILE` the accept loop of
`Server` / `BoundServer` and `connectrpc::axum::serve_tls` pauses accepts
for up to a second, or until shutdown, instead of spinning.
its response body has been sent, not only until its response head. As
with gRPC's `MaxConnectionIdle`, a client that stops reading now keeps
its connection until `with_max_connection_age` retires it.
time: 2026-09-16T14:05:00.000000000+00:00
8 changes: 8 additions & 0 deletions .changes/unreleased/Fixed-20260916-140600.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
kind: Fixed
body: |-
**The accept loop pauses when out of file descriptors.** After `EMFILE` /
`ENFILE` (`WSAEMFILE` on Windows), the accept loop of `Server` /
`BoundServer` and `connectrpc::axum::serve_tls` pauses accepts for up to a
second, or until shutdown. Before, it retried at once on Unix, spinning a
core, and did not recognise the Windows code.
time: 2026-09-16T14:06:00.000000000+00:00
8 changes: 6 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,13 @@ tracing = "0.1"
tracing-subscriber = { version = "0.3.20", features = ["env-filter"] }

# HTTP server/client
h2 = "0.4"
# 0.4.15: the header-list-size tests expect its GOAWAY on oversized header lists.
h2 = "0.4.15"
hyper = "1"
hyper-util = "0"
# `ConnectionClosed::error` relies on the auto connection's `Interrupted`
# "Cancelled" error for a shutdown before protocol detection (checked in
# 0.1.19 and 0.1.20).
hyper-util = "0.1.20"

# TLS
rustls = "0.23"
Expand Down
17 changes: 10 additions & 7 deletions connectrpc/src/axum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,16 @@ use crate::server::serve_with_listener;
/// [`ConnectionConfig`] setting, graceful shutdown, panic isolation.
///
/// See the [module docs](self) for what this adds over `axum::serve`. After
/// running out of file descriptors (`EMFILE` / `ENFILE`) the loop pauses
/// accepts for up to a second, or until the shutdown signal fires.
/// running out of file descriptors (`EMFILE` / `ENFILE`, or `WSAEMFILE` on
/// Windows) the loop pauses accepts for up to a second, or until the
/// shutdown signal fires.
///
/// # Errors
///
/// The future resolves to `Err` only for a non-transient `accept(2)` error.
/// Per-connection failures are logged and never end the loop.
/// The future resolves to `Err` only for an accept error that is neither
/// transient nor file-descriptor exhaustion, as for
/// [`BoundServer::serve`](crate::BoundServer::serve). Per-connection
/// failures are logged and never end the loop.
pub fn serve(listener: TcpListener, router: axum::Router) -> Serve {
Serve {
listener,
Expand Down Expand Up @@ -128,9 +131,9 @@ pub fn serve(listener: TcpListener, router: axum::Router) -> Serve {
///
/// # Errors
///
/// As [`serve`]: only a non-transient `accept(2)` error. TLS handshake
/// failures and timeouts are logged at `debug` / `warn` and never end the
/// loop.
/// As [`serve`]: only an accept error that is neither transient nor
/// file-descriptor exhaustion. TLS handshake failures and timeouts are
/// logged at `debug` / `warn` and never end the loop.
#[cfg(feature = "server-tls")]
#[cfg_attr(docsrs, doc(cfg(feature = "server-tls")))]
pub fn serve_tls(
Expand Down
6 changes: 6 additions & 0 deletions connectrpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,12 @@ pub use error::SharedSource;
/// `http-body` dependency to use generated code.
pub use http_body;

/// Re-export of the `http` crate, whose types, such as
/// [`HeaderMap`](http::HeaderMap) and [`Extensions`](http::Extensions),
/// appear throughout the public API — so consumers can name them without
/// their own `http` dependency.
pub use http;

// Protocol detection
pub use protocol::Protocol;
pub use protocol::RequestProtocol;
Expand Down
Loading
Loading