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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
//! Waiting on a live pipe, and letting go of it.
//!
//! Split from `main.rs` when the file-size gate said it was holding two
//! things: parsing what the operator typed, and then sitting on the result
//! until they ask for it back. This is the second. `interrupt.rs` beside it
//! owns hearing the ask; this owns what happens either side of it.
//!
//! Both halves of the CLI end up here, which is why the trait exists at
//! all — the two handles deliberately share none, and inventing a public
//! one in the library to save a few lines in a CLI would put it on a
//! surface that has to live with it.
use Future;
use Write;
use Duration;
use ;
use crateInterrupt;
/// How long a fresh connect side may sit at `Idle` before this command
/// gives up on the serve side.
///
/// Not a library default, and deliberately not one. `connect` returns as
/// soon as the local port is bound and then keeps trying for as long as the
/// handle lives, because a sleeping laptop, a dead one and a serve side
/// five seconds from starting look identical from down there — so the
/// deadline belongs to whoever is willing to give up, which for an
/// interactive command is this one.
///
/// Longer than the thirty-odd seconds iroh spends giving up on a peer that
/// is not there. Under that, this would report an absent peer while the
/// first dial was still in flight, which is a worse answer than the slow
/// one it replaced.
pub const FIRST_CONTACT: Duration = from_secs;
/// Park until a shutdown signal, reporting the transport path and every
/// change to it.
///
/// `Relayed` is worth surfacing: it explains latency, and a user who does
/// not know their traffic is going through a relay has no way to guess why
/// the pipe feels slow. `Idle` on the connect side is worth more — it is
/// the only thing that says the far end has gone away and this side is
/// looking for it.
///
/// The starting value is printed rather than waited for, which is not
/// belt-and-braces. `status_changed` compares against the status at the
/// moment it is called, so a pipe that reached `Direct` before this
/// function was first polled has nothing left to report — and that race is
/// real: the connect side publishes its path before the first accept, and
/// its `status:` line appeared in two runs out of three.
///
/// The stream is deliberately not a parameter of *this* function. README's
/// output contract — "the first two lines are stdout, the rest is stderr",
/// which is what makes `modelpipe serve … | head -1` a ticket — has to
/// survive an edit by someone who has not read it, and `eprintln!` used to
/// enforce that for free. [`park_to`] is private to this module, so the two
/// `main.rs` call sites cannot pick a stream at all; the one place that
/// names one is the line below, next to the paragraph saying why.
pub async
/// [`park`], with somewhere to write to. Only the tests supply it.
///
/// These lines are the CLI's only output while a pipe is live, and a macro
/// writing straight to the process's stderr cannot be asserted on: while
/// this was `eprintln!`, deleting a print, swapping two of them or dropping
/// the low-water-mark update all left 419 tests green. Everything the tests
/// below pin — that a status is printed, that the relay's throttling is
/// printed *under* it, and that it is printed once — was unreachable
/// before.
async
/// Print one reading of the pipe: what it is doing, and — when there is
/// news of it — what the relay is doing to it.
///
/// The two go out together, in that order, because the throttling is a
/// correction to the line above it. A rate-limited pipe reads `relayed`
/// with a peer present and nothing failing, which is indistinguishable
/// from a healthy relayed pipe; printing the correction anywhere else
/// would leave the misleading line standing on its own.
///
/// A write error is dropped rather than propagated. This is progress
/// commentary on stderr, and a terminal that has gone away is no reason to
/// tear down a pipe that is still carrying requests — which is more than
/// the `eprintln!` this replaced offered, since that panicked.
/// The `relay:` line a reading earns, or `None` when it says nothing new.
///
/// **Nothing at zero**, which is the shape `main.rs` already uses for
/// output that would otherwise be noise: `token_line` and `qr` both hand
/// back an `Option<String>` and the caller prints what is there. A pipe no
/// relay has ever throttled — nearly every pipe — must read exactly as it
/// read before this line existed.
///
/// **Nothing twice.** `relay_connections_ratelimited` is a monotonic total
/// for the life of one endpoint rather than a flag saying "throttled right
/// now", so a line emitted on every reading would keep announcing one old
/// event for the rest of the session. `reported` is what has already been
/// said, and only a count above it is news — which makes the printed
/// number a running total and the decision to print it a delta. Both
/// halves are wanted: the total is the thing that relates to
/// `relay_connections`, and the delta is what stops the line repeating.
///
/// The value column is the one `ticket:`, `token:` and `status:` use, so
/// all four line up when they reach the same terminal.
/// Wait for the pipe to reach the serve side, or say that it could not.
///
/// This is the sentence `connect` used to produce. It blocked until the
/// first dial landed and reported an absent peer through its `Result`;
/// it now returns with the local port bound and the dial still running, so
/// the wait — and the deadline it needs — moved out here rather than
/// disappearing. The wording is the one `ConnectError::PeerUnreachable`
/// printed, because it is the same fact reported from one step further out.
///
/// Nothing is printed on the way to stdout until this returns `Ok`, which
/// is the other half of not regressing: a script capturing the URL gets one
/// only for a pipe that actually reached its peer, exactly as before.
///
/// `grace` is [`FIRST_CONTACT`] everywhere but the tests, which pass a
/// short one so that checking the decision does not mean waiting out the
/// number.
pub async
/// Shut down gracefully, unless the operator asks again.
///
/// The graceful path can legitimately take a while — it is waiting for
/// admitted requests to finish, which is the whole promise — so a second
/// signal has to be able to stop waiting. Dropping the handle is the cut,
/// and returning from here does exactly that.
pub async
/// What this module needs from either handle.
///
/// A trait here rather than in the library: the two handles deliberately
/// share none, and inventing a public one to save a few lines in a CLI
/// would put it on a surface that has to live with it.
///
/// `metrics` is named for the reading and not for the call behind it. The
/// handles' own method is `network_metrics`, and a trait method sharing
/// that name would be shadowed by the inherent one at every call site
/// here — including inside the impls below, where `self.network_metrics()`
/// would resolve to the inherent method today and to unbounded recursion
/// the day it stopped being inherent. The tests drive a fake, so nothing
/// in the suite would ever see it.
pub