Release the nagoya socket work as 3.1.0 - #50
Conversation
`framed_json` is bound on `tokio::io::AsyncRead + AsyncWrite`, so a consumer that does not run tokio cannot reach the framed transport at all, even though `Transport` itself is a blanket impl over `Stream + Sink` and names no runtime. The seam was already neutral; only the one implementation behind it was not. So this adds `framed_json_neutral`, the same transport over `futures::io::AsyncRead + AsyncWrite`. That trait pair is the neutral one: tokio adapts to it through `tokio-util`'s `Compat`, and Nagoya through `nagoya::io::Compat`, so a caller can name a byte stream without naming a runtime. The tokio path is untouched and remains the default. `tokio_util`'s `Framed` is what the tokio path gets for free and there is no `futures-util` equivalent, so the buffering is written out here: the same two buffers, one accumulating what has been read but not yet framed, one holding what has been encoded but not yet written. An oversized length prefix is refused before the body is buffered, which is what keeps a peer from naming four gigabytes and being believed. `encode` and `decode` are shared with the tokio path rather than reimplemented, so the bytes are identical by construction. A test asserts that anyway, because the format is normative for non-Rust peers and there are now two implementations that could drift.
`framed_json_neutral` asks for futures-io, which was the right seam and not yet a reachable one: Nagoya's socket implements Nagoya's own `Stream`, whose `read` and `write_all` are `async fn`, and `nagoya::io::Compat` runs the other way, presenting a futures-io stream to a Nagoya consumer. The doc comment claiming that Compat closed the gap was wrong and is corrected here. `NagoyaStream` closes it instead, and is thin because `TcpStream` already exposes `poll_read`, `poll_write`, and `poll_flush` publicly with exactly the futures-io signature. So this delegates rather than bridges: no boxed future, no second buffer, no duplicate readiness logic. Had those methods been private, a wrapper would have had to poll an `async fn` borrowing `&mut self` from inside `poll_read`, which is not soundly possible. `poll_close` flushes and stops there, because Nagoya exposes no half-close. That is said outright rather than implied, since a peer waiting to see end-of-stream before the drop needs a half-close on the Nagoya side first. The test sends a `WireMessage` both ways over a Unix socket on Nagoya's reactor and executor, with no tokio runtime started. A compile would not have shown this: the adapter's whole job is readiness, so the message has to travel. The feature is off by default and additive; the tokio path is untouched, and the dependency resolves from the registry: 0.1.9 is the published release that already speaks AF_UNIX and carries `TaskSet`.
Connections are polled in place with FuturesUnordered so a nagoya reactor can run the accept loop. spawn_local required a tokio LocalSet even when the listener was not tokio.
serve_with already drives connections with FuturesUnordered so a nagoya reactor can run the accept loop. Request handlers in the session still spawn_local'd, which is why Karen wrapped that loop in a LocalSet. Poll those bodies on the session task the same way: a slow hook cannot stall the read loop, one thread, no tokio task spawn. TCP listen is unchanged.
Connections are polled in place with FuturesUnordered, matching serve_with. A LocalSet remains only because the hyper upgrader still spawn_locals onto TokioExecutor. nago-wss is the tokio-free replacement for that backend.
The five commits on this branch add a feature and change runtime behaviour, and the manifest still said 3.0.3, which crates.io already holds with different content. Anything depending on this work therefore had no version to name it and had to reach the crate by path. A new minor rather than a re-cut 3.0.3: two crates sharing one version number would give anyone who already resolved the old one a silent behaviour change.
|
Local gate, run before asking for review rather than leaning on CI. The feature is genuinely proven end to end, not just compiled: A wire message across a real nagoya unix socket in both directions, plus the One thing a reviewer should knowA plain That reads as a pass. The 40-test difference between the two runs above is the Not introduced by this branch and not fixed here, because doing so would widen |
The workflow fired on every push to master and every pull request. The gate is the one an author runs locally before asking for review; a tick that arrives without being asked for is a tick people stop reading. workflow_dispatch keeps it runnable from the Actions tab when somebody wants it. The runner is unchanged.
|
Added
Folded in here rather than split out because this repository keeps one open pull |
Five commits that were already written, plus the version bump that lets anything
depend on them.
Why this is blocked today
The manifest said
3.0.3, and crates.io already holds a3.0.3with differentcontent. So this work had no version number that identified it, and the one
consumer that needs it, karen, had to reach the crate by path. That path
dependency is the last one in karen that cannot be argued away.
A new minor rather than a re-cut
3.0.3: two crates sharing one version numberwould hand anyone who already resolved the old one a silent behaviour change.
What is here
nagoya-transport(src/libs/ws/transport.rs:93-100), gatingpub mod nagoyaand re-exportingNagoyaStream, which is what givesframed_json_neutralover a nagoya socket. Declared atCargo.toml:70.serve_withwithoutspawn_local(src/libs/ws/server.rs:262,:176-181,src/libs/ws/session.rs:366), driving connections onFuturesUnorderedso a nagoya reactor can drive it. The publishedserve_withspawn_locals and panics outside a tokioLocalSet.What this does not do
It does not remove tokio from this crate. Hyper's upgrader still
spawn_locals ontoTokioExecutor(src/libs/ws/server.rs:179,src/libs/ws/transport.rs:25), so thewspath still needs it.The honest claim is: the
nagoya-transportpath is tokio-free, thewspath isnot. karen is clean only because it takes
default-features = falseand dropsws, which drops tungstenite, rustls, hyper, reqwest and webpki-roots with it.Please do not let this land described as "endpoint-libs no longer needs tokio".
After merge
Publishing 3.1.0 lets karen replace
path = "../endpoint-libs"withversion = "=3.1.0", and unblocks agentcode and moe-pgo when their own tokioremoval starts. All three currently either carry a path dependency or sit on the
2.1 line.
Linear history, no merge commits.