[Rust Frontend] Refactor TLS serve path with unified `MaybeTlsListener` (#47101)
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Bugen Zhao committed
Jun 30, 2026 at 21:31 UTC
3675bcff67ad5688efef74a0948e879962c4bc38
6 files changed
+138
-180
rust/src/server/Cargo.toml
+1
@@ -61,6 +61,7 @@ expect-test.workspace = true
61
rmp-serde.workspace = true
62
serial_test.workspace = true
63
tempfile.workspace = true
64
+tokio = { workspace = true, features = ["test-util"] }
65
tower.workspace = true
66
vllm-engine-core-client = { workspace = true, features = ["test-util"] }
67
zeromq.workspace = true
rust/src/server/src/grpc/mod.rs
+1
-78
@@ -4,21 +4,16 @@ mod convert;
4
5
use std::pin::Pin;
6
use std::sync::Arc;
7
-use std::task::{Context, Poll};
7
9
-use futures::{Stream, StreamExt as _, stream};
8
+use futures::{Stream, StreamExt as _};
9
use thiserror_ext::AsReport as _;
11
-use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
10
use tokio::sync::mpsc;
13
-use tokio_openssl::SslStream;
11
use tokio_stream::wrappers::ReceiverStream;
15
-use tonic::transport::server::{Connected, TcpConnectInfo};
12
use tonic::{Request, Response, Status};
13
use tracing::info;
14
use vllm_text::{DecodedTextEvent, TextOutputStreamExt as _};
15
16
use self::convert::ResponseOpts;
21
-use crate::listener::{Listener, ListenerIo};
17
use crate::state::AppState;
18
19
/// Generated protobuf/gRPC types for the `vllm` package.
@@ -31,78 +26,6 @@ pub use pb::generate_server::GenerateServer;
26
#[cfg(test)]
27
mod tests;
28
34
-/// Newtype over `tokio-openssl`'s `SslStream` so we can implement tonic's
35
-/// [`Connected`] on it (the orphan rule blocks doing so on the foreign type).
36
-pub(crate) struct GrpcTlsStream {
37
- inner: SslStream<ListenerIo>,
38
-}
39
-
40
-impl GrpcTlsStream {
41
- pub(crate) fn new(inner: SslStream<ListenerIo>) -> Self {
42
- Self { inner }
43
- }
44
-}
45
-
46
-impl AsyncRead for GrpcTlsStream {
47
- fn poll_read(
48
- self: Pin<&mut Self>,
49
- cx: &mut Context<'_>,
50
- buf: &mut ReadBuf<'_>,
51
- ) -> Poll<std::io::Result<()>> {
52
- Pin::new(&mut self.get_mut().inner).poll_read(cx, buf)
53
- }
54
-}
55
-
56
-impl AsyncWrite for GrpcTlsStream {
57
- fn poll_write(
58
- self: Pin<&mut Self>,
59
- cx: &mut Context<'_>,
60
- buf: &[u8],
61
- ) -> Poll<std::io::Result<usize>> {
62
- Pin::new(&mut self.get_mut().inner).poll_write(cx, buf)
63
- }
64
-
65
- fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
66
- Pin::new(&mut self.get_mut().inner).poll_flush(cx)
67
- }
68
-
69
- fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
70
- Pin::new(&mut self.get_mut().inner).poll_shutdown(cx)
71
- }
72
-}
73
-
74
-impl Connected for GrpcTlsStream {
75
- type ConnectInfo = TcpConnectInfo;
76
-
77
- fn connect_info(&self) -> TcpConnectInfo {
78
- self.inner.get_ref().connect_info()
79
- }
80
-}
81
-
82
-/// Adapt the shared server listener into tonic's incoming stream shape.
83
-pub(crate) fn incoming(listener: Listener) -> impl Stream<Item = std::io::Result<ListenerIo>> {
84
- stream::unfold(listener, |mut listener| async move {
85
- let (io, _) = axum::serve::Listener::accept(&mut listener).await;
86
- Some((Ok(io), listener))
87
- })
88
-}
89
-
90
-/// Wrap the gRPC listener so each accepted connection completes a TLS handshake
91
-/// before tonic serves it.
92
-pub(crate) fn tls_incoming(
93
- listener: Listener,
94
- context: openssl::ssl::SslContext,
95
- handshake_timeout: std::time::Duration,
96
-) -> impl Stream<Item = std::io::Result<GrpcTlsStream>> {
97
- tls_listener::builder(context)
98
- .handshake_timeout(handshake_timeout)
99
- .listen(listener)
100
- .map(|res| {
101
- res.map(|(inner, _addr)| GrpcTlsStream::new(inner))
102
- .map_err(std::io::Error::other)
103
- })
104
-}
105
-
29
/// gRPC Generate service implementation backed by the shared application state.
30
pub struct GenerateServiceImpl {
31
state: Arc<AppState>,
rust/src/server/src/grpc/tests.rs
+5
-5
@@ -30,8 +30,8 @@ use zeromq::prelude::{SocketRecv, SocketSend};
30
use zeromq::{DealerSocket, PushSocket, ZmqMessage};
31
32
use super::pb::generate_client::GenerateClient;
33
-use super::{GenerateServer, GenerateServiceImpl, incoming, pb, tls_incoming};
34
-use crate::listener::Listener;
33
+use super::{GenerateServer, GenerateServiceImpl, pb};
34
+use crate::listener::{Listener, MaybeTlsListener};
35
use crate::state::AppState;
36
use crate::tls;
37
use crate::tls_tests::{TestCerts, server_tls};
@@ -287,7 +287,7 @@ async fn grpc_test_server(
287
let addr = listener.local_addr().expect("local addr");
288
289
let server_task = tokio::spawn(async move {
290
- let incoming = incoming(Listener::Tcp(listener));
290
+ let incoming = MaybeTlsListener::plain(Listener::Tcp(listener));
291
TonicServer::builder()
292
.add_service(svc)
293
.serve_with_incoming(incoming)
@@ -318,7 +318,7 @@ async fn grpc_tls_test_server(
318
let addr = listener.local_addr().expect("local addr").to_string();
319
320
let server_task = tokio::spawn(async move {
321
- let incoming = tls_incoming(Listener::Tcp(listener), context, tls::TLS_HANDSHAKE_TIMEOUT);
321
+ let incoming = MaybeTlsListener::tls(Listener::Tcp(listener), context);
322
TonicServer::builder()
323
.add_service(svc)
324
.serve_with_incoming(incoming)
@@ -413,7 +413,7 @@ async fn grpc_server_with_keepalive(
413
}
414
415
let server_task = tokio::spawn(async move {
416
- let incoming = incoming(Listener::Tcp(listener));
416
+ let incoming = MaybeTlsListener::plain(Listener::Tcp(listener));
417
builder
418
.add_service(svc)
419
.serve_with_incoming(incoming)
rust/src/server/src/lib.rs
+17
-62
@@ -27,7 +27,6 @@ pub use config::{
27
ApiServerOptions, Config, CoordinatorMode, CorsConfig, DEFAULT_KEEP_ALIVE_TIMEOUT,
28
HttpListenerMode, TlsConfig,
29
};
30
-use futures::FutureExt as _;
30
use hyper::body::Incoming;
31
use hyper::server::conn::http1;
32
use hyper_util::rt::{TokioIo, TokioTimer};
@@ -45,7 +44,7 @@ use vllm_engine_core_client::{EngineCoreClient, EngineCoreClientConfig};
44
use vllm_llm::Llm;
45
use vllm_text::TextLlm;
46
48
-use crate::listener::Listener;
47
+use crate::listener::{Listener, MaybeTlsListener};
48
use crate::routes::build_router;
49
use crate::server_info::ServerInfoSnapshot;
50
use crate::state::AppState;
@@ -179,7 +178,7 @@ where
178
let listener = Listener::bind(&config.listener_mode)
179
.await
180
.context("failed to bind listener for OpenAI server")?;
182
- let bind_address = listener.local_addr()?;
181
+ let bind_address = listener.local_addr_display()?;
182
let model = state.primary_model_name().to_owned();
183
let app = extend_router(build_router(state.clone()));
184
@@ -252,7 +251,6 @@ where
251
// silent client cannot hold the connection open.
252
let keep_alive_timeout = config.keep_alive_timeout;
253
let timeouts = ConnectionTimeouts {
255
- handshake: tls::TLS_HANDSHAKE_TIMEOUT,
254
header_read: if keep_alive_timeout.is_zero() {
255
DEFAULT_KEEP_ALIVE_TIMEOUT
256
} else {
@@ -266,9 +264,15 @@ where
264
let server_shutdown = server_shutdown.clone();
265
let force_shutdown = force_shutdown.clone();
266
async move {
267
+ let listener = match tls_config {
268
+ Some(context) => MaybeTlsListener::tls(listener, context),
269
+ None => MaybeTlsListener::plain(listener),
270
+ };
271
+ let server = serve_connections(listener, app, shutdown.cancelled_owned(), timeouts);
272
+
273
let result = tokio::select! {
270
- result = serve_listener(listener, tls_config, app, shutdown.cancelled_owned(), timeouts) => {
271
- result
274
+ result = server => {
275
+ result.context("HTTP server failed")
276
}
277
_ = force_shutdown.cancelled() => {
278
warn!("HTTP graceful shutdown deadline elapsed; aborting server");
@@ -292,18 +296,11 @@ where
296
shutdown.cancelled().await;
297
return Ok(());
298
};
295
- // Box to unify the TLS and plaintext arms' different stream types.
296
- let server = match grpc_tls {
297
- Some(context) => {
298
- let incoming =
299
- grpc::tls_incoming(grpc_listener, context, tls::TLS_HANDSHAKE_TIMEOUT);
300
- svc.serve_with_incoming_shutdown(incoming, shutdown.cancelled_owned()).boxed()
301
- }
302
- None => {
303
- let incoming = grpc::incoming(grpc_listener);
304
- svc.serve_with_incoming_shutdown(incoming, shutdown.cancelled_owned()).boxed()
305
- }
299
+ let incoming = match grpc_tls {
300
+ Some(context) => MaybeTlsListener::tls(grpc_listener, context),
301
+ None => MaybeTlsListener::plain(grpc_listener),
302
};
303
+ let server = svc.serve_with_incoming_shutdown(incoming, shutdown.cancelled_owned());
304
305
let result = tokio::select! {
306
result = server => {
@@ -333,61 +330,19 @@ where
330
/// Per-connection timeouts applied while serving HTTP/HTTPS.
331
#[derive(Clone, Copy)]
332
pub(crate) struct ConnectionTimeouts {
336
- /// Max time for a client to complete the TLS handshake (TLS path only).
337
- pub(crate) handshake: Duration,
333
/// HTTP/1 header-read timeout (bounds idle keep-alive and the head read).
334
pub(crate) header_read: Duration,
335
/// Whether HTTP/1 keep-alive is enabled; `false` closes after each response.
336
pub(crate) keep_alive_enabled: bool,
337
}
338
344
-/// Apply optional TLS termination and per-connection HTTP timeouts, then serve
345
-/// `app`. Shared by [`serve_with_router_extension`] and the TLS tests.
346
-async fn serve_listener(
347
- listener: Listener,
348
- tls: Option<openssl::ssl::SslContext>,
349
- app: Router,
350
- shutdown: impl Future<Output = ()> + Send + 'static,
351
- timeouts: ConnectionTimeouts,
352
-) -> Result<()> {
353
- match tls {
354
- Some(context) => {
355
- // tls-listener terminates TLS (handshake + timeout); serve_connections
356
- // owns the HTTP keep-alive/idle bound that axum::serve cannot express.
357
- // Failed handshakes (incl. timeouts) log at ERROR via tls-listener.
358
- let listener = tls_listener::builder(context)
359
- .handshake_timeout(timeouts.handshake)
360
- .listen(listener);
361
- serve_connections(
362
- listener,
363
- app,
364
- shutdown,
365
- timeouts.header_read,
366
- timeouts.keep_alive_enabled,
367
- )
368
- .await
369
- .context("HTTPS server failed")
370
- }
371
- None => serve_connections(
372
- listener,
373
- app,
374
- shutdown,
375
- timeouts.header_read,
376
- timeouts.keep_alive_enabled,
377
- )
378
- .await
379
- .context("HTTP server failed"),
380
- }
381
-}
382
-
339
/// Serve `app` per connection (HTTP/1) with a keep-alive idle timeout and
340
/// graceful drain. Hand-rolled on hyper because [`axum::serve()`] takes no config.
341
async fn serve_connections<L>(
342
mut listener: L,
343
app: Router,
344
shutdown: impl Future<Output = ()> + Send,
389
- header_read: Duration,
390
- keep_alive_enabled: bool,
345
+ timeouts: ConnectionTimeouts,
346
) -> Result<()>
347
where
348
L: axum::serve::Listener,
@@ -404,8 +359,8 @@ where
359
app.clone().map_request(|req: Request<Incoming>| req.map(Body::new)),
360
);
361
let mut builder = http1::Builder::new();
407
- builder.timer(TokioTimer::new()).header_read_timeout(header_read);
408
- if !keep_alive_enabled {
362
+ builder.timer(TokioTimer::new()).header_read_timeout(timeouts.header_read);
363
+ if !timeouts.keep_alive_enabled {
364
builder.keep_alive(false);
365
}
366
let connection = builder.serve_connection(TokioIo::new(io), service);
rust/src/server/src/listener.rs
+96
-6
@@ -12,13 +12,14 @@ use std::pin::Pin;
12
use std::task::{Context, Poll, ready};
13
14
use auto_enums::enum_derive;
15
+use openssl::ssl::SslContext;
16
use socket2::Socket;
17
use tls_listener::{AsyncAccept, AsyncListener};
18
use tokio::net::{TcpListener, TcpStream, UnixListener, UnixStream};
19
use tonic::transport::server::{Connected, TcpConnectInfo};
20
use tracing::trace;
21
21
-use crate::HttpListenerMode;
22
+use crate::{HttpListenerMode, tls};
23
24
/// Runtime listener type used by the OpenAI-compatible HTTP or gRPC server,
25
/// which is either a TCP listener or a Unix-domain listener.
@@ -61,7 +62,7 @@ impl Listener {
62
63
/// Return a log-friendly local address string for either TCP or Unix
64
/// sockets.
64
- pub fn local_addr(&self) -> Result<String> {
65
+ pub fn local_addr_display(&self) -> Result<String> {
66
match self {
67
Self::Tcp(listener) => Ok(listener.local_addr()?.to_string()),
68
Self::Unix(listener) => Ok(match listener.local_addr()?.as_pathname() {
@@ -92,7 +93,7 @@ impl Listener {
93
}
94
}
95
95
- fn listener_addr(&self) -> Result<ListenerAddr> {
96
+ fn local_addr(&self) -> Result<ListenerAddr> {
97
match self {
98
Self::Tcp(listener) => listener.local_addr().map(ListenerAddr::Tcp),
99
Self::Unix(listener) => listener.local_addr().map(ListenerAddr::Unix),
@@ -100,6 +101,7 @@ impl Listener {
101
}
102
}
103
104
+/// Allow the unified listener to plug directly into tonic's gRPC server.
105
impl Connected for ListenerIo {
106
type ConnectInfo = TcpConnectInfo;
107
@@ -144,7 +146,7 @@ impl axum::serve::Listener for Listener {
146
}
147
148
fn local_addr(&self) -> Result<Self::Addr> {
147
- self.listener_addr()
149
+ self.local_addr()
150
}
151
}
152
@@ -173,10 +175,98 @@ impl AsyncAccept for Listener {
175
}
176
}
177
}
176
-
178
impl AsyncListener for Listener {
179
fn local_addr(&self) -> Result<Self::Address> {
179
- self.listener_addr()
180
+ self.local_addr()
181
+ }
182
+}
183
+
184
+/// A listener that may be either a plain TCP/UDS listener or a TLS listener over it.
185
+pub enum MaybeTlsListener {
186
+ Plain(Listener),
187
+ Tls(tls_listener::TlsListener<Listener, SslContext>),
188
+}
189
+
190
+impl MaybeTlsListener {
191
+ /// Create a plain listener without TLS.
192
+ pub fn plain(listener: Listener) -> Self {
193
+ Self::Plain(listener)
194
+ }
195
+
196
+ /// Create a TLS listener over the given plain listener.
197
+ pub fn tls(listener: Listener, context: SslContext) -> Self {
198
+ Self::Tls(
199
+ tls_listener::builder(context)
200
+ .handshake_timeout(tls::TLS_HANDSHAKE_TIMEOUT)
201
+ .listen(listener),
202
+ )
203
+ }
204
+}
205
+
206
+/// Listener I/O type that may be either a plain TCP/UDS stream or a TLS stream over it.
207
+#[derive(Debug)]
208
+#[enum_derive(tokio1::AsyncRead, tokio1::AsyncWrite)]
209
+pub enum MaybeTlsStream {
210
+ Plain(ListenerIo),
211
+ Tls(tokio_openssl::SslStream<ListenerIo>),
212
+}
213
+
214
+/// Allow the maybe-TLS listener to plug directly into `axum::serve(...)`.
215
+impl axum::serve::Listener for MaybeTlsListener {
216
+ type Addr = ListenerAddr;
217
+ type Io = MaybeTlsStream;
218
+
219
+ async fn accept(&mut self) -> (Self::Io, Self::Addr) {
220
+ match self {
221
+ Self::Plain(listener) => {
222
+ let (io, addr) = axum::serve::Listener::accept(listener).await;
223
+ (MaybeTlsStream::Plain(io), addr)
224
+ }
225
+ Self::Tls(tls_listener) => {
226
+ let (io, addr) = axum::serve::Listener::accept(tls_listener).await;
227
+ (MaybeTlsStream::Tls(io), addr)
228
+ }
229
+ }
230
+ }
231
+
232
+ fn local_addr(&self) -> tokio::io::Result<Self::Addr> {
233
+ match self {
234
+ Self::Plain(listener) => listener.local_addr(),
235
+ Self::Tls(tls_listener) => tls_listener.local_addr(),
236
+ }
237
+ }
238
+}
239
+
240
+/// Allow the maybe-TLS listener to plug directly into tonic's gRPC server.
241
+impl Connected for MaybeTlsStream {
242
+ type ConnectInfo = TcpConnectInfo;
243
+
244
+ fn connect_info(&self) -> TcpConnectInfo {
245
+ match self {
246
+ Self::Plain(stream) => stream.connect_info(),
247
+ Self::Tls(stream) => stream.get_ref().connect_info(),
248
+ }
249
+ }
250
+}
251
+
252
+/// Allow the maybe-TLS listener to be adaptable to tonic's incoming stream shape.
253
+impl futures::Stream for MaybeTlsListener {
254
+ type Item = std::io::Result<MaybeTlsStream>;
255
+
256
+ fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
257
+ match self.get_mut() {
258
+ Self::Plain(listener) => {
259
+ let listener = Pin::new(listener);
260
+ let (io, _) = ready!(listener.poll_accept(cx))?;
261
+ Poll::Ready(Some(Ok(MaybeTlsStream::Plain(io))))
262
+ }
263
+ Self::Tls(tls_listener) => {
264
+ let tls_listener = Pin::new(tls_listener);
265
+ let (io, _) =
266
+ ready!(tls_listener.poll_accept(cx)).map_err(std::io::Error::other)?;
267
+ Poll::Ready(Some(Ok(MaybeTlsStream::Tls(io))))
268
+ }
269
+ }
270
}
271
}
272
rust/src/server/src/tls_tests.rs
+18
-29
@@ -1,5 +1,5 @@
1
//! TLS tests: `build_server_config` unit checks plus end-to-end OpenSSL handshakes
2
-//! through the production `serve_listener` path, with a trivial router since TLS
2
+//! through the production listener/connection path, with a trivial router since TLS
3
//! terminates below the app.
4
5
use std::pin::Pin;
@@ -23,8 +23,8 @@ use tokio_openssl::SslStream;
23
use tokio_util::sync::CancellationToken;
24
25
use crate::config::{HttpListenerMode, TlsConfig};
26
-use crate::listener::Listener;
27
-use crate::{ConnectionTimeouts, serve_listener, tls};
26
+use crate::listener::{Listener, MaybeTlsListener};
27
+use crate::{ConnectionTimeouts, serve_connections, tls};
28
29
// ============================================================================
30
// Test infrastructure
@@ -251,7 +251,6 @@ fn build_tls(certs: &TestCerts, cert: &str, key: Option<&str>) -> TlsConfig {
251
252
/// Generous per-connection timeouts that never fire during the fast tests.
253
const TEST_TIMEOUTS: ConnectionTimeouts = ConnectionTimeouts {
254
- handshake: Duration::from_secs(60),
254
header_read: Duration::from_secs(5),
255
keep_alive_enabled: true,
256
};
@@ -261,9 +260,8 @@ async fn spawn_server(tls_config: Option<TlsConfig>) -> (String, CancellationTok
260
}
261
262
/// Bind an ephemeral listener and serve a trivial router via the production
264
-/// `serve_listener`, optionally with TLS. The listener is bound (and thus
265
-/// accepting into the backlog) before returning, so a client may connect
266
-/// immediately without a sleep.
263
+/// listener/connection path. The listener is bound (and thus accepting into the
264
+/// backlog) before returning, so a client may connect immediately without a sleep.
265
async fn spawn_server_with_timeouts(
266
tls_config: Option<TlsConfig>,
267
timeouts: ConnectionTimeouts,
@@ -274,7 +272,7 @@ async fn spawn_server_with_timeouts(
272
})
273
.await
274
.expect("bind listener");
277
- let addr = listener.local_addr().expect("local addr");
275
+ let addr = listener.local_addr_display().expect("local addr");
276
277
let server_config =
278
tls_config.map(|cfg| tls::build_server_config(&cfg).expect("build server config"));
@@ -282,14 +280,11 @@ async fn spawn_server_with_timeouts(
280
let shutdown = CancellationToken::new();
281
let server_shutdown = shutdown.clone();
282
tokio::spawn(async move {
285
- let _ = serve_listener(
286
- listener,
287
- server_config,
288
- app,
289
- server_shutdown.cancelled_owned(),
290
- timeouts,
291
- )
292
- .await;
283
+ let listener = match server_config {
284
+ Some(context) => MaybeTlsListener::tls(listener, context),
285
+ None => MaybeTlsListener::plain(listener),
286
+ };
287
+ let _ = serve_connections(listener, app, server_shutdown.cancelled_owned(), timeouts).await;
288
});
289
(addr, shutdown)
290
}
@@ -521,20 +516,19 @@ async fn plain_http_serves_when_tls_is_disabled() {
516
shutdown.cancel();
517
}
518
524
-#[tokio::test]
519
+#[tokio::test(start_paused = true)]
520
async fn tls_handshake_timeout_drops_silent_client() {
521
// Silent client (no ClientHello) must be dropped at the handshake deadline.
522
let certs = TestCerts::generate();
528
- let timeouts = ConnectionTimeouts {
529
- handshake: Duration::from_millis(150),
530
- header_read: Duration::from_secs(5),
531
- keep_alive_enabled: true,
532
- };
533
- let (addr, shutdown) = spawn_server_with_timeouts(Some(server_tls(&certs, 0)), timeouts).await;
523
+ let (addr, shutdown) = spawn_server(Some(server_tls(&certs, 0))).await;
524
525
let mut tcp = TcpStream::connect(&addr).await.expect("connect");
526
+ tokio::task::yield_now().await;
527
+ tokio::time::advance(tls::TLS_HANDSHAKE_TIMEOUT + Duration::from_millis(1)).await;
528
+ tokio::task::yield_now().await;
529
+
530
let mut buf = [0u8; 1];
537
- let read = tokio::time::timeout(Duration::from_secs(5), tcp.read(&mut buf)).await;
531
+ let read = tokio::time::timeout(Duration::from_secs(1), tcp.read(&mut buf)).await;
532
assert!(
533
matches!(read, Ok(Ok(0)) | Ok(Err(_))),
534
"server must drop a stalled TLS handshake (expected close, got {read:?})"
@@ -546,7 +540,6 @@ async fn tls_handshake_timeout_drops_silent_client() {
540
async fn keep_alive_timeout_closes_idle_connection() {
541
// Idle keep-alive connection must be closed at the deadline.
542
let timeouts = ConnectionTimeouts {
549
- handshake: Duration::from_secs(60),
543
header_read: Duration::from_millis(150),
544
keep_alive_enabled: true,
545
};
@@ -582,7 +575,6 @@ async fn keep_alive_timeout_closes_idle_tls_connection() {
575
// still fires through tls-listener's post-handshake SslStream, not just plaintext.
576
let certs = TestCerts::generate();
577
let timeouts = ConnectionTimeouts {
585
- handshake: Duration::from_secs(60),
578
header_read: Duration::from_millis(150),
579
keep_alive_enabled: true,
580
};
@@ -618,7 +610,6 @@ async fn keep_alive_timeout_closes_idle_tls_connection() {
610
async fn idle_timeout_closes_silent_client() {
611
// Silent client closed by the header-read timeout (http1-only arms it from byte 0).
612
let timeouts = ConnectionTimeouts {
621
- handshake: Duration::from_secs(60),
613
header_read: Duration::from_millis(150),
614
keep_alive_enabled: true,
615
};
@@ -638,7 +629,6 @@ async fn idle_timeout_closes_silent_client() {
629
async fn keep_alive_zero_disables_keep_alive() {
630
// 0 disables keep-alive (serve, then close), like uvicorn's timeout_keep_alive=0.
631
let timeouts = ConnectionTimeouts {
641
- handshake: Duration::from_secs(60),
632
header_read: Duration::from_secs(5),
633
keep_alive_enabled: false,
634
};
@@ -671,7 +661,6 @@ async fn disabled_keep_alive_still_closes_silent_client() {
661
// Even with keep-alive off, the head read stays bounded, so a silent client
662
// is dropped rather than held open.
663
let timeouts = ConnectionTimeouts {
674
- handshake: Duration::from_secs(60),
664
header_read: Duration::from_millis(150),
665
keep_alive_enabled: false,
666
};