1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
//! The transport seam: what it takes to run the session/dispatch machinery over
//! something that is not a WebSocket.
//!
//! # Design
//!
//! Two layers, deliberately:
//!
//! * [`Transport`] — a *blanket alias* over `Sink + Stream` of [`WireMessage`],
//! modelled on tarpc's `Transport`. Implementors never name it: anything that is a
//! `Sink` and a `Stream` of the right item types already is one. This composes for
//! free with `framed_json_neutral` (the `framed-transport` feature) and with
//! hand-rolled adapters (XPC dictionaries, for instance).
//! * [`MessageStream`] — the object-safe, `async fn`-based trait the session loop
//! actually consumes. It is the pre-2.0 `WsStream` under a transport-neutral name;
//! `WsStream` remains as an alias.
//!
//! [`TransportStream`] bridges the two, so implementing either one is enough.
//!
//! # Threading
//!
//! [`MessageStream`] is `#[async_trait(?Send)]` — its futures are **not** `Send`.
//! `serve_connection` / `serve_with` poll the session and its request handlers
//! in place on one thread, and TCP `listen` polls connections the same way.
//!
//! There is no `LocalSet` anywhere in this crate any more, and no executor to
//! host one. The last one existed because the hyper upgrader `spawn_local`ed
//! onto `TokioExecutor`; the upgrade is nago-wss's now, and the server runs on
//! a nagoya reactor it builds itself (`server.rs`: `Reactor::local` plus
//! `block_on_with`). A caller that polls a session from somewhere else owns the
//! thread while it does — that is the whole requirement, and any single-threaded
//! executor satisfies it.
//!
//! # Which feature sets are free of tokio
//!
//! All of them, with nothing left over. That is a claim about
//! `cargo tree -e normal -i tokio`, which is the fact; a feature flag alone is
//! not.
//!
//! There is no exception and no interop flavour held back for a consumer that
//! still runs tokio. `framed-transport-tokio` was one and is deleted: the
//! neutral path put identical bytes on the wire, so it offered an adapter type
//! and a dependency. `otel` was the other, and the edge there was genuinely
//! upstream's -- `opentelemetry-otlp` reaches `tonic` for the OTLP protobuf
//! message types and tonic reaches tokio through tokio-stream -- so rather
//! than carry a runtime for a feature nothing in the fleet enabled, the OTLP
//! exporter is deleted too. `OtelConfig` stays, inert, because backends
//! construct it in a `LoggingConfig` literal.
//!
//! Everything this crate used to do with tokio, it now does with nagoya or with
//! `futures`:
//!
//! 1. **The TCP server path** is nagoya. `listener.rs` is
//! `nagoya::reactor::TcpListener` and `ConnectionListener`'s associated types
//! are bounded on `nagoya::io::Stream`, which is an async-fn trait rather
//! than a `poll_read`/`ReadBuf` pair. `WebsocketServer::listen_impl` builds a
//! `Reactor::local` and drives the accept loop under `block_on_with`; the
//! per-shard threads and their accept `mpsc` went with it.
//! 2. **Signal delivery** is nagoya. The flag is a `nagoya::sync::Notify` plus
//! an `AtomicBool` (`Shutdown`), and delivery is `nagoya::signal::Signal`. A
//! `Signal` is registered on one reactor and fires only while that reactor is
//! polled, which is why `init_signals` takes the `Handle`.
//! 3. **The per-connection queue** is `outbound`, this crate's own. Its bound is
//! the number of queued `WsMessage`s and a sender does not reserve a slot, so
//! `drop_conn_on_buffer_full` fires at exactly the configured depth.
//! `futures::channel::mpsc` reserves `buffer + num_senders` and would not.
//! 4. **The `select!`s** are `futures::future::select` plus `Either`
//! (`session.rs::run_loop`, `server.rs::listen_impl`, `signal.rs`). That is
//! not a fairness-preserving swap: `select` takes its left arm the moment it
//! is ready and never polls the right one, where `tokio::select!` chose at
//! random. So the session loop's order is a deliberate priority
//! (`session::priority4`): a finished handler, then outbound, then inbound,
//! then the policy flag. Handlers come first because they are the one arm a
//! peer cannot keep ready, so anywhere below inbound they starve.
//! 5. **`TOOLBOX`** in `toolbox.rs` is a `thread_local` installed on poll entry
//! and restored on return, including on panic and drop — never a
//! `tokio::task_local`.
//!
//! `RawStream` is the one bound that looks like it should be `futures_io` and is
//! not: it is an object-safe mirror of `nagoya::io::Stream`, because its
//! consumers are the nago-wss upgrade path. See the comment on it in
//! `traits.rs`. Transport code that must stay runtime-neutral takes `futures_io`
//! and goes through `framed_json_neutral` — that
//! is the seam, and it is what this module is for.
use eyre;
use ;
use WireMessage;
use ;
/// A bidirectional, typed message channel.
///
/// STOLEN SHAPE (tarpc `Transport`): a blanket alias, not a trait to implement. Any
/// `Sink<SinkItem, Error = E> + Stream<Item = Result<Item, E>>` satisfies it.
/// Adapts any [`Transport`] of [`WireMessage`] into a [`MessageStream`].
///
/// This is the bridge that lets a `Framed<UnixStream, _>`, an in-memory duplex pipe,
/// or an XPC connection drive the ordinary session loop.
;
// `framed_json` was re-exported here alongside these until 3.2.0. It is gone with
// the tokio flavour, and nothing replaces it at this path: `framed_json_neutral`
// takes a `futures_io` stream and lives in `framed`, which is public. A consumer
// that was on `framed_json` bridges its tokio stream with `tokio-util`'s `compat`
// on its own side; `framed`'s module docs spell out the one line.
pub use ;
pub use NagoyaStream;