async fn serve_inner( listener: TcpListener, router: Router, shutdown: CancellationToken, permits: Arc, grace: Duration, metrics: Option>, tls: Option, h2c: bool, frontend_state: Option>, http2_settings: Http2Connections, ) -> Result<()> { let mut tasks = JoinSet::new(); loop { tokio::select! { biased; _ = shutdown.cancelled() => break, _ = tasks.join_next(), if !tasks.is_empty() => {}, accepted = listener.accept() => { let (stream, peer) = accepted.context("listener accept failed")?; let local = stream.local_addr().context("accepted local address unavailable")?; let Ok(permit) = permits.clone().try_acquire_owned() else { if let Some(m) = &metrics { m.rejected_connections.fetch_add(1, Ordering::Relaxed); } let log_state = match &tls { Some(TlsMode::Frontend(state, _)) => Some(state), _ => frontend_state.as_ref(), }; log_protocol_error( log_state, "connection_limit_rejected", "connection concurrency limit reached", peer, local, if tls.is_some() { "tls" } else { "tcp" }, "", 0, None, ); drop(stream); continue; }; let _ = stream.set_nodelay(true); if let Some(m) = &metrics { m.connections.fetch_add(1, Ordering::Relaxed); m.active_connections.fetch_add(1, Ordering::Relaxed); } let lease = Arc::new(ConnectionLease { _permit: permit, metrics: metrics.clone() }); let router = router.clone(); let stop = shutdown.clone(); let tls = tls.clone(); let metrics = metrics.clone(); let frontend_state = frontend_state.clone(); tasks.spawn(async move { match tls { Some(TlsMode::Frontend(state, http2)) => { let deadline = tokio::time::Instant::now() + Duration::from_secs(10); let acceptor = tokio_rustls::LazyConfigAcceptor::new( rustls::server::Acceptor::default(), stream, ); let started = tokio::select! { _ = stop.cancelled() => return, started = tokio::time::timeout_at(deadline, acceptor) => started, }; let start = match started { Ok(Ok(start)) => start, Ok(Err(error)) => { state.metrics.tls_handshake_errors.fetch_add(1, Ordering::Relaxed); log_protocol_error( Some(&state), "tls_client_hello_error", error, peer, local, "tls", "", 0, None, ); return; } Err(_) => { state.metrics.tls_handshake_errors.fetch_add(1, Ordering::Relaxed); log_protocol_error( Some(&state), "tls_client_hello_timeout", "TLS ClientHello timeout", peer, local, "tls", "", 0, None, ); return; } }; let host = start .client_hello() .server_name() .and_then(|name| crate::config::request_host(name).ok()); let config = { let snapshot = state.config.load(); let frontend = host .as_deref() .and_then(|host| snapshot.frontend(host, local, true)); let local_host = host .as_deref() .and_then(|host| snapshot.not_found_host_on(host, local, true)); if let Some(frontend) = frontend { if http2 && frontend.http2_enabled { frontend.tls.clone() } else { frontend.tls_http1.clone() } } else if http2 && local_host.is_none_or(|item| item.http2_enabled) { snapshot.tls.clone() } else { snapshot.tls_http1.clone() } }; let Some(config) = config else { log_protocol_error( Some(&state), "tls_configuration_unavailable", "no TLS configuration is available for this connection", peer, local, "tls", "", 0, host.clone(), ); return; }; let accepted = tokio::select! { _ = stop.cancelled() => return, accepted = tokio::time::timeout_at(deadline, start.into_stream(config)) => accepted, }; match accepted { Ok(Ok(stream)) => { let h2 = stream.get_ref().1.alpn_protocol() == Some(b"h2".as_slice()); state.metrics.tls_connections.fetch_add(1, Ordering::Relaxed); if h2 { state.metrics.http2_connections.fetch_add(1, Ordering::Relaxed); } let extended_connect = h2 && state.config.load().websocket_rfc8441_on(local, true); connection( stream, router, peer, local, Transport { tls: true }, host.clone(), h2, extended_connect, false, Some(state.clone()), lease, stop, grace, http2_settings, Default::default(), ) .await; }, Ok(Err(error)) => { state.metrics.tls_handshake_errors.fetch_add(1, Ordering::Relaxed); log_protocol_error( Some(&state), "tls_handshake_error", error, peer, local, "tls", "", 0, host.clone(), ); }, Err(_) => { state.metrics.tls_handshake_errors.fetch_add(1, Ordering::Relaxed); log_protocol_error( Some(&state), "tls_handshake_timeout", "TLS handshake timeout", peer, local, "tls", "", 0, host.clone(), ); }, } } Some(TlsMode::Fixed(config, plaintext_router)) => { let deadline = tokio::time::Instant::now() + Duration::from_secs(10); if let Some(plain) = plaintext_router { let mut first = [0u8; 1]; let peeked = tokio::select! { _ = stop.cancelled() => return, peeked = tokio::time::timeout_at(deadline, stream.peek(&mut first)) => peeked, }; match peeked { Ok(Ok(n)) if n > 0 && first[0] != 0x16 => { connection( stream, plain, peer, local, Transport { tls: false }, None, false, false, false, None, lease, stop, grace, Http2Connections::default(), Default::default(), ) .await; return; } Ok(Ok(n)) if n > 0 => {} _ => return, } } let acceptor = tokio_rustls::TlsAcceptor::from(config); let accepted = tokio::select! { _ = stop.cancelled() => return, accepted = tokio::time::timeout_at(deadline, acceptor.accept(stream)) => accepted, }; if let Ok(Ok(stream)) = accepted { let h2 = stream.get_ref().1.alpn_protocol() == Some(b"h2".as_slice()); connection( stream, router, peer, local, Transport { tls: true }, None, h2, false, false, None, lease, stop, grace, Http2Connections::default(), Default::default(), ) .await; } } None => { let prior_h2 = if h2c { let detected = tokio::time::timeout(Duration::from_secs(10), async { let mut preface = [0u8; HTTP2_PRIOR_KNOWLEDGE_PREFACE.len()]; loop { let n = stream.peek(&mut preface).await?; if n == 0 { return Ok::(false); } let prefix = &preface[..n.min(HTTP2_PRIOR_KNOWLEDGE_PREFACE.len())]; if !h2_prior_knowledge_prefix(prefix) { return Ok(false); } if n >= HTTP2_PRIOR_KNOWLEDGE_PREFACE.len() { return Ok(prefix == HTTP2_PRIOR_KNOWLEDGE_PREFACE); } tokio::task::yield_now().await; } }).await; matches!(detected, Ok(Ok(true))) } else { false }; if prior_h2 { if let Some(m) = &metrics { m.http2_connections.fetch_add(1, Ordering::Relaxed); } } let extended_connect = prior_h2 && frontend_state .as_ref() .is_some_and(|state| state.config.load().websocket_rfc8441_on(local, false)); #[cfg(target_os = "linux")] if !prior_h2 && frontend_state.as_ref().is_some_and(|state| { state.config.load().web_php_services.values() .any(|service| service.sendfile == crate::web_server::SendfileMode::Auto) }) { let (io, slots) = sendfile::SendfileIo::new(stream); connection( io, router, peer, local, Transport { tls: false }, None, false, false, h2c, frontend_state, lease, stop, grace, http2_settings, Some(slots), ).await; return; } connection( stream, router, peer, local, Transport { tls: false }, None, prior_h2, extended_connect, h2c && !prior_h2, frontend_state, lease, stop, grace, http2_settings, Default::default(), ) .await; } } }); }, } } let _ = tokio::time::timeout(grace + Duration::from_secs(1), async { while tasks.join_next().await.is_some() {} }) .await; tasks.abort_all(); while tasks.join_next().await.is_some() {} Ok(()) } #[derive(Clone, Copy, Debug)] enum H2ShutdownCause { Idle, Lifetime, Process, }