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
//! Serving the router over TLS.
//!
//! # Why this is fifty lines here rather than a dependency
//!
//! `axum-server` is the usual shape and was rejected. The accept loop is the
//! part of a TLS server where the decisions that matter are made, and all of
//! them are this crate's to make:
//!
//! - **A failed handshake must cost one connection, never the listener.**
//! With client certificates required, failed handshakes are *normal
//! traffic* — every port scanner and every health checker that does not
//! know about the certificate produces one.
//! - **A connection must not be able to hold a slot forever.** A client that
//! opens a socket and sends nothing is free for the client and not free
//! for the server, so the handshake has a deadline
//! ([`HANDSHAKE_TIMEOUT`]) — and so do the request headers after it
//! ([`HEADER_TIMEOUT`]), because a client that completes a handshake and
//! then goes quiet costs exactly as much as one that never handshook.
//! - **Shutdown must end.** One endpoint here answers with a body that
//! never ends, so a drain that waits for every response body waits for
//! every subscriber to leave; [`DRAIN_TIMEOUT`](crate::DRAIN_TIMEOUT) is what keeps a rollout
//! from hanging on its own change stream.
//! - **A refused handshake must be recorded, and recorded saying nothing.**
//! It goes through the same [`AuditSink`](crate::AuditSink) as everything
//! else, as `endpoint=tls outcome=unauthenticated`, with no caller, no
//! subject and no reason. An operator who has misconfigured a client CA
//! needs to see *that* handshakes are failing; nobody needs to see whose.
//!
//! `axum-server` would own every one of them and expose none, and its
//! `tls-rustls` feature selects the `aws-lc-rs` provider — a vendored copy
//! of AWS-LC, built with cmake — where this crate wants `ring`, which is
//! already in the workspace's graph. What is used instead is `tokio-rustls`
//! for the handshake and hyper's own HTTP/1 connection for what follows:
//! the same two pieces `axum::serve` uses, with the acceptor spliced in
//! between.
//!
//! The rustls configuration itself is *not* built here: it comes from
//! [`Tls`](crate::tls::Tls), where the key loading, the permission refusal
//! and the client verifier live.
use Future;
use io;
use Arc;
use Duration;
use Router;
use http1;
use ;
use TowerToHyperService;
use ;
use watch;
use JoinSet;
use TlsAcceptor;
use crate;
use crateServer;
/// How long a client has to complete a TLS handshake.
///
/// Generous for a handshake and short for a socket somebody is squatting on.
/// It is not a configuration key: a deployment that needs a different number
/// has an idle timeout of its own in front, and this one exists so that a
/// server with no such thing in front is still not held open by a client
/// that connects and says nothing.
pub const HANDSHAKE_TIMEOUT: Duration = from_secs;
/// How long a connection has, after the handshake, to send request headers.
///
/// The handshake deadline covers TLS and stops there. Without this one, a
/// client that completes a handshake and then sends no request bytes — or
/// drips an incomplete header a byte at a time — holds a socket and a task
/// for as long as it likes, which is the exhaustion
/// [`HANDSHAKE_TIMEOUT`] exists to prevent, moved one step further in.
///
/// It bounds *headers*, not the request as a whole: a stream's response body
/// is meant to run for days, and this deadline stops before the first byte
/// of one is written.
pub const HEADER_TIMEOUT: Duration = from_secs;
/// How long the accept loop waits after an accept error before trying again.
///
/// An `accept` that fails because the process is out of file descriptors
/// fails again immediately, and a loop that retries instantly turns a
/// resource limit into a spin. Ten milliseconds is invisible to a client and
/// is the difference between busy-waiting and waiting.
const ACCEPT_BACKOFF: Duration = from_millis;
/// Serves `router` over TLS until `shutdown` completes.
///
/// The TLS material and the audit sink both come from `server`, which is the
/// same one the router was built over; `router` is passed separately so that
/// an embedder can wrap it — a layer of its own, a different fallback — the
/// way it can with [`axum::serve()`].
///
/// Shutdown is graceful in both halves: the listener stops accepting, and
/// every connection already open is told to finish the request it is serving
/// and close. A config server is restarted by a rollout, and dropping the
/// fetch a pod is making at that moment would make a rollout look like a
/// configuration failure to whoever is starting up beside it.
///
/// Graceful, and bounded: a connection that has not finished within
/// [`DRAIN_TIMEOUT`](crate::DRAIN_TIMEOUT) is dropped. An open change stream has no end of its
/// own, so an unbounded drain would be a rollout that waits for its
/// subscribers rather than the other way round.
///
/// # Errors
///
/// Only what the listener itself reports. A connection that fails — a
/// handshake that was refused, a client that went away — ends that
/// connection and nothing else.
///
/// # Panics
///
/// If `server` is not configured for TLS. Callers reach this function
/// through [`Server::tls`], which is `None` exactly when that is so.
pub async
/// One connection: handshake, then HTTP until it ends or shutdown does.
async