Skip to main content

postrust_proxy/vendored/
proxy.rs

1//! Vendored proxy service from rpxy-lib: proxy/proxy_main.rs
2//!
3//! This module provides the main proxy service that handles incoming connections.
4
5use crate::config::{ProxyConfig, RateLimitKey as RouteRateLimitKey, Route, ServerConfig};
6use crate::health::HealthChecker;
7use crate::ratelimit::{per_second, RateLimitKey, RateLimiter};
8use crate::vendored::backend::BackendAppManager;
9use crate::vendored::conn::{ConnActivity, IdleIo, InFlight};
10use crate::vendored::forwarder::ForwarderClient;
11use crate::vendored::handler::MessageHandler;
12use crate::vendored::hyper_ext::timer::Timeout;
13use crate::vendored::hyper_ext::{IncomingBodyExt, ProxyBody};
14use crate::vendored::types::PathName;
15use http_body_util::BodyExt;
16use hyper::body::Incoming;
17use hyper::server::conn::http2;
18use hyper::{Request, Response};
19use hyper_util::rt::{TokioExecutor, TokioIo, TokioTimer};
20use hyper_util::server::conn::auto;
21use std::convert::Infallible;
22use std::future::Future;
23use std::net::SocketAddr;
24use std::pin::Pin;
25use std::sync::atomic::{AtomicBool, Ordering};
26use std::sync::{Arc, OnceLock};
27use std::time::Duration;
28use tokio::net::{TcpListener, TcpStream};
29use tokio::sync::{RwLock, Semaphore};
30use tokio_util::sync::CancellationToken;
31use tracing::{debug, error, info, warn};
32use uuid::Uuid;
33
34/// Proxy service that handles incoming HTTP connections.
35pub struct ProxyService {
36    /// Backend manager
37    backend_manager: Arc<BackendAppManager>,
38    /// Forwarder client
39    forwarder: Arc<ForwarderClient>,
40    /// Rate limiter
41    rate_limiter: Arc<RateLimiter>,
42    /// Configuration
43    config: Arc<RwLock<ProxyConfig>>,
44    /// One slot per open client connection, shared by every listener, sized
45    /// by `server.max_connections` when the first listener starts.
46    connection_slots: OnceLock<Arc<Semaphore>>,
47    /// Whether the rate limiter's cleanup task has been started.
48    cleanup_started: AtomicBool,
49}
50
51/// The `server` settings a listener applies to each connection, read once when
52/// it starts.
53#[derive(Clone, Debug)]
54struct ListenerSettings {
55    header_read_timeout: Duration,
56    idle_timeout: Duration,
57    http2_keepalive_interval: Option<Duration>,
58    http2_keepalive_timeout: Duration,
59}
60
61impl ListenerSettings {
62    fn from_server(server: &ServerConfig) -> Self {
63        // A zero timeout is read as one second, not as "never": "never" is
64        // the behaviour these exist to prevent, and nobody setting a timeout
65        // means it.
66        let at_least_a_second = |secs: u64| Duration::from_secs(secs.max(1));
67        Self {
68            header_read_timeout: at_least_a_second(server.header_read_timeout_secs),
69            idle_timeout: at_least_a_second(server.idle_timeout_secs),
70            http2_keepalive_interval: match server.http2_keepalive_interval_secs {
71                0 => None,
72                secs => Some(Duration::from_secs(secs)),
73            },
74            http2_keepalive_timeout: at_least_a_second(server.http2_keepalive_timeout_secs),
75        }
76    }
77
78    /// The builder for a port that speaks HTTP/1.1 and HTTP/2 both.
79    ///
80    /// Both halves need a timer. hyper's timeouts are inert without one --
81    /// `header_read_timeout` was configured by default and never fired, which
82    /// is what let a client trickle a request head forever.
83    fn auto_builder(&self) -> auto::Builder<TokioExecutor> {
84        let mut builder = auto::Builder::new(TokioExecutor::new());
85        builder
86            .http1()
87            .timer(TokioTimer::new())
88            .header_read_timeout(self.header_read_timeout);
89        // enable_connect_protocol advertises SETTINGS_ENABLE_CONNECT_PROTOCOL,
90        // which is what permits WebSocket over HTTP/2 (RFC 8441).
91        builder
92            .http2()
93            .timer(TokioTimer::new())
94            .enable_connect_protocol()
95            .keep_alive_interval(self.http2_keepalive_interval)
96            .keep_alive_timeout(self.http2_keepalive_timeout);
97        builder
98    }
99
100    /// The builder for a connection known to be HTTP/2.
101    fn http2_builder(&self) -> http2::Builder<TokioExecutor> {
102        let mut builder = http2::Builder::new(TokioExecutor::new());
103        builder
104            .timer(TokioTimer::new())
105            .enable_connect_protocol()
106            .keep_alive_interval(self.http2_keepalive_interval)
107            .keep_alive_timeout(self.http2_keepalive_timeout);
108        builder
109    }
110}
111
112/// The hyper service for one client connection.
113///
114/// Marks each request as in flight on the connection from the moment hyper
115/// hands it over until its response body has been sent, which is what keeps
116/// the connection's idle timeout from firing under a request that is merely
117/// slow. See [`crate::vendored::conn`].
118#[derive(Clone)]
119struct ConnectionService {
120    service: Arc<ProxyService>,
121    activity: Arc<ConnActivity>,
122    client_addr: SocketAddr,
123    proto: &'static str,
124}
125
126impl hyper::service::Service<Request<Incoming>> for ConnectionService {
127    type Response = Response<ProxyBody>;
128    type Error = Infallible;
129    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
130
131    fn call(&self, mut request: Request<Incoming>) -> Self::Future {
132        let in_flight = InFlight::new(&self.activity);
133        // A copy rides on the request, for a tunnel to keep: see
134        // ForwarderClient::forward_upgrade.
135        request.extensions_mut().insert(in_flight.clone());
136        let service = self.service.clone();
137        let client_addr = self.client_addr;
138        let proto = self.proto;
139        Box::pin(async move {
140            let response = service.handle_request(request, client_addr, proto).await?;
141            Ok(response.map(|body| {
142                body.map_frame(move |frame| {
143                    let _held_until_the_body_is_dropped = &in_flight;
144                    frame
145                })
146                .boxed()
147            }))
148        })
149    }
150}
151
152/// A rate limit's scope: the route it is declared on.
153///
154/// The database id when there is one; otherwise derived from the name, which
155/// is unique across the configuration (see `merge_from_database`).
156fn route_scope(route: &Route) -> Uuid {
157    route.id.unwrap_or_else(|| {
158        Uuid::new_v5(
159            &Uuid::NAMESPACE_OID,
160            format!("postrust-proxy route {}", route.name).as_bytes(),
161        )
162    })
163}
164
165impl ProxyService {
166    /// Create a new proxy service.
167    ///
168    /// The `server` settings -- connection limit, timeouts -- are read from
169    /// `config` when each listener starts.
170    pub fn new(
171        config: Arc<RwLock<ProxyConfig>>,
172        health_checker: Arc<HealthChecker>,
173        rate_limiter: Arc<RateLimiter>,
174    ) -> Self {
175        let backend_manager =
176            Arc::new(BackendAppManager::new().with_health_checker(health_checker));
177        let forwarder = Arc::new(ForwarderClient::default());
178
179        Self {
180            backend_manager,
181            forwarder,
182            rate_limiter,
183            config,
184            connection_slots: OnceLock::new(),
185            cleanup_started: AtomicBool::new(false),
186        }
187    }
188
189    /// Load configuration into the backend manager.
190    pub async fn load_config(&self) {
191        let config = self.config.read().await;
192
193        // Register upstreams
194        for upstream in &config.upstreams {
195            self.backend_manager.register_upstream(upstream.clone());
196        }
197
198        // Register routes
199        for route in &config.routes {
200            // Find upstream by name
201            if let Some(upstream) = config.upstreams.iter().find(|u| u.name == route.upstream) {
202                use crate::vendored::types::ServerName;
203                let host = route.match_.host.as_deref().unwrap_or("*");
204                let path = route.match_.path.as_deref().unwrap_or("/");
205                let host = ServerName::new(host);
206                let path = PathName::new(path);
207                self.backend_manager
208                    .register_route(host, path, upstream.resolved_id());
209            }
210        }
211
212        info!(
213            "Loaded {} routes and {} upstreams",
214            config.routes.len(),
215            config.upstreams.len()
216        );
217    }
218
219    /// Start the HTTP proxy server.
220    pub async fn serve_http(
221        self: Arc<Self>,
222        addr: SocketAddr,
223        cancel_token: CancellationToken,
224    ) -> std::io::Result<()> {
225        let listener = TcpListener::bind(addr).await?;
226        info!("HTTP proxy listening on {}", addr);
227        self.serve_http_listener(listener, cancel_token).await
228    }
229
230    /// [`serve_http`](Self::serve_http) on a listener that is already bound --
231    /// to port 0, say, when the caller needs to know which port it got.
232    pub async fn serve_http_listener(
233        self: Arc<Self>,
234        listener: TcpListener,
235        cancel_token: CancellationToken,
236    ) -> std::io::Result<()> {
237        self.run_listener(
238            listener,
239            cancel_token,
240            "HTTP proxy",
241            |service, io, activity, client_addr, settings| async move {
242                let conn = ConnectionService {
243                    service,
244                    activity,
245                    client_addr,
246                    proto: "http",
247                };
248                // The auto builder negotiates HTTP/1.1 and prior-knowledge h2c
249                // on the same port, and the _with_upgrades variant is what lets
250                // a 101 hand the connection back to us for tunnelling.
251                if let Err(err) = settings
252                    .auto_builder()
253                    .serve_connection_with_upgrades(TokioIo::new(io), conn)
254                    .await
255                {
256                    debug!("Connection error: {}", err);
257                }
258            },
259        )
260        .await
261    }
262
263    /// Serve HTTPS, negotiating the protocol with ALPN.
264    ///
265    /// ALPN is the whole point: without it a TLS listener negotiates nothing and
266    /// every client falls back to HTTP/1.1, which leaves HTTP/2 reachable only
267    /// as cleartext h2c and WebSocket only as `ws://`.
268    ///
269    /// Because ALPN *decides* the protocol rather than guessing it, the h2 path
270    /// can use the HTTP/2 builder directly instead of sniffing -- so, unlike the
271    /// cleartext port, an invalid preface here is answered with a proper GOAWAY.
272    pub async fn serve_https(
273        self: Arc<Self>,
274        addr: SocketAddr,
275        tls_config: Arc<tokio_rustls::rustls::ServerConfig>,
276        cancel_token: CancellationToken,
277    ) -> std::io::Result<()> {
278        let listener = TcpListener::bind(addr).await?;
279        info!("HTTPS proxy listening on {} (ALPN: h2, http/1.1)", addr);
280        self.serve_https_listener(listener, tls_config, cancel_token)
281            .await
282    }
283
284    /// [`serve_https`](Self::serve_https) on a listener that is already bound.
285    pub async fn serve_https_listener(
286        self: Arc<Self>,
287        listener: TcpListener,
288        tls_config: Arc<tokio_rustls::rustls::ServerConfig>,
289        cancel_token: CancellationToken,
290    ) -> std::io::Result<()> {
291        let acceptor = tokio_rustls::TlsAcceptor::from(tls_config);
292        self.run_listener(
293            listener,
294            cancel_token,
295            "HTTPS proxy",
296            move |service, io, activity, client_addr, settings| {
297                let acceptor = acceptor.clone();
298                async move {
299                    // The handshake runs on the idle-timed socket, so a client
300                    // that opens a connection and never finishes the handshake
301                    // is dropped like any other idle one.
302                    let tls_stream = match acceptor.accept(io).await {
303                        Ok(s) => s,
304                        Err(e) => {
305                            debug!("TLS handshake failed: {}", e);
306                            return;
307                        }
308                    };
309
310                    let is_h2 = tls_stream
311                        .get_ref()
312                        .1
313                        .alpn_protocol()
314                        .map(|p| p == b"h2")
315                        .unwrap_or(false);
316
317                    let conn = ConnectionService {
318                        service,
319                        activity,
320                        client_addr,
321                        proto: "https",
322                    };
323
324                    let io = TokioIo::new(tls_stream);
325                    let result = if is_h2 {
326                        settings
327                            .http2_builder()
328                            .serve_connection(io, conn)
329                            .await
330                            .map_err(|e| {
331                                Box::<dyn std::error::Error + Send + Sync>::from(e.to_string())
332                            })
333                    } else {
334                        // http/1.1, or a client that offered no ALPN at all.
335                        // Upgrades stay available, which is what makes wss://
336                        // work.
337                        settings
338                            .auto_builder()
339                            .serve_connection_with_upgrades(io, conn)
340                            .await
341                            .map_err(|e| {
342                                Box::<dyn std::error::Error + Send + Sync>::from(e.to_string())
343                            })
344                    };
345
346                    if let Err(err) = result {
347                        debug!("HTTPS connection error: {}", err);
348                    }
349                }
350            },
351        )
352        .await
353    }
354
355    /// Serve HTTP/2 only, on a dedicated port.
356    ///
357    /// The main listener negotiates by sniffing the opening bytes, which is why
358    /// a corrupted HTTP/2 preface there gets an HTTP/1 400 instead of
359    /// GOAWAY(PROTOCOL_ERROR): it cannot be told apart from a malformed HTTP/1
360    /// request (h2spec 3.5). A listener that only ever speaks HTTP/2 has no
361    /// such ambiguity and answers the way the RFC asks.
362    ///
363    /// This is additive. The main port still serves HTTP/1.1 and h2c together;
364    /// nothing is given up by enabling it.
365    pub async fn serve_h2c(
366        self: Arc<Self>,
367        addr: SocketAddr,
368        cancel_token: CancellationToken,
369    ) -> std::io::Result<()> {
370        let listener = TcpListener::bind(addr).await?;
371        info!("HTTP/2-only proxy listening on {}", addr);
372        self.serve_h2c_listener(listener, cancel_token).await
373    }
374
375    /// [`serve_h2c`](Self::serve_h2c) on a listener that is already bound.
376    pub async fn serve_h2c_listener(
377        self: Arc<Self>,
378        listener: TcpListener,
379        cancel_token: CancellationToken,
380    ) -> std::io::Result<()> {
381        self.run_listener(
382            listener,
383            cancel_token,
384            "HTTP/2-only proxy",
385            |service, io, activity, client_addr, settings| async move {
386                let conn = ConnectionService {
387                    service,
388                    activity,
389                    client_addr,
390                    proto: "http",
391                };
392                if let Err(err) = settings
393                    .http2_builder()
394                    .serve_connection(TokioIo::new(io), conn)
395                    .await
396                {
397                    debug!("HTTP/2 connection error: {}", err);
398                }
399            },
400        )
401        .await
402    }
403
404    /// The accept loop every listener shares.
405    ///
406    /// A connection slot is taken *before* accepting, so at
407    /// `server.max_connections` the proxy simply stops accepting and new
408    /// connections queue in the kernel's backlog, rather than being accepted
409    /// only to be dropped. The slot travels with the socket (see
410    /// [`ConnActivity`]) and is released when the socket closes, including
411    /// after an upgrade has handed it to a tunnel.
412    async fn run_listener<F, Fut>(
413        self: Arc<Self>,
414        listener: TcpListener,
415        cancel_token: CancellationToken,
416        what: &'static str,
417        serve: F,
418    ) -> std::io::Result<()>
419    where
420        F: Fn(Arc<Self>, IdleIo<TcpStream>, Arc<ConnActivity>, SocketAddr, ListenerSettings) -> Fut,
421        Fut: Future<Output = ()> + Send + 'static,
422    {
423        let (settings, max_connections) = {
424            let config = self.config.read().await;
425            (
426                ListenerSettings::from_server(&config.server),
427                config.server.max_connections,
428            )
429        };
430        let slots = self
431            .connection_slots
432            .get_or_init(|| {
433                Arc::new(Semaphore::new(
434                    max_connections.clamp(1, Semaphore::MAX_PERMITS),
435                ))
436            })
437            .clone();
438        self.start_background_tasks(&cancel_token);
439
440        loop {
441            let slot = tokio::select! {
442                _ = cancel_token.cancelled() => break,
443                slot = slots.clone().acquire_owned() => match slot {
444                    Ok(slot) => slot,
445                    // Only a closed semaphore fails, and this one never is.
446                    Err(_) => break,
447                },
448            };
449
450            let accepted = tokio::select! {
451                _ = cancel_token.cancelled() => break,
452                accepted = listener.accept() => accepted,
453            };
454
455            match accepted {
456                Ok((stream, client_addr)) => {
457                    // Same reasoning as the upstream leg: relay the client's
458                    // own chunking rather than Nagle's.
459                    if let Err(e) = stream.set_nodelay(true) {
460                        debug!("Could not set TCP_NODELAY on the client socket: {}", e);
461                    }
462                    let activity = ConnActivity::new(Some(slot));
463                    let io = IdleIo::new(stream, activity.clone(), settings.idle_timeout);
464                    tokio::spawn(serve(
465                        self.clone(),
466                        io,
467                        activity,
468                        client_addr,
469                        settings.clone(),
470                    ));
471                }
472                Err(e) => {
473                    // Typically EMFILE. Retrying at once would spin a core on
474                    // an error that will not clear until something closes, so
475                    // back off briefly.
476                    error!("Accept error: {}", e);
477                    drop(slot);
478                    tokio::select! {
479                        _ = cancel_token.cancelled() => break,
480                        _ = tokio::time::sleep(Duration::from_millis(100)) => {}
481                    }
482                }
483            }
484        }
485
486        info!("{} stopped", what);
487        Ok(())
488    }
489
490    /// Start what serving depends on, once, whichever listener starts first.
491    ///
492    /// The rate limiter's cleanup is here rather than left to the caller
493    /// because nothing called it: every client that was ever rate limited
494    /// stayed in memory for the life of the process.
495    fn start_background_tasks(&self, cancel_token: &CancellationToken) {
496        if !self.cleanup_started.swap(true, Ordering::AcqRel) {
497            tokio::spawn(
498                self.rate_limiter
499                    .clone()
500                    .start_cleanup(cancel_token.clone()),
501            );
502        }
503    }
504
505    /// Handle a single HTTP request.
506    async fn handle_request(
507        &self,
508        request: Request<Incoming>,
509        client_addr: SocketAddr,
510        proto: &str,
511    ) -> Result<Response<ProxyBody>, Infallible> {
512        let uri = request.uri().clone();
513        let method = request.method().clone();
514
515        // Host, falling back to the URI authority. HTTP/2 has no Host header,
516        // so reading only the header routed nothing for any host-matched route
517        // over h2 or h2c. See MessageHandler::request_host.
518        let host = MessageHandler::request_host(&request);
519        let host = host.as_str();
520
521        let path = uri.path();
522
523        debug!("{} {} {} from {}", method, host, path, client_addr);
524
525        // Before routing, since routing is what this protects.
526        if let Some(reason) = MessageHandler::unsafe_path_reason(path) {
527            debug!("Refusing {} from {}: {}", path, client_addr, reason);
528            return Ok(MessageHandler::bad_request(reason));
529        }
530
531        // Find matching route and upstream
532        let (route, upstream, server) = {
533            let config = self.config.read().await;
534            let server = (
535                config.server.allowed_upgrade_protocols.clone(),
536                config.server.trust_forwarded_headers,
537                Duration::from_secs(config.server.tunnel_idle_timeout_secs.max(1)),
538            );
539            // The whole of RouteMatch, not just host and path prefix. The
540            // previous filter chain ignored path_type, methods and headers,
541            // each of which silently widened a route past what its author
542            // wrote -- `path_type = "exact"` on /health also matched
543            // /health-internal, and `methods = ["GET"]` accepted DELETE.
544            let matched = config
545                .routes
546                .iter()
547                .filter(|r| r.enabled)
548                .filter(|r| {
549                    r.match_
550                        .matches(host, path, method.as_str(), request.headers())
551                })
552                .max_by_key(|r| {
553                    let path_len = r.match_.path.as_ref().map(|p| p.len()).unwrap_or(0);
554                    (r.priority, path_len)
555                })
556                .cloned();
557
558            match matched {
559                Some(r) => {
560                    let upstream = config
561                        .upstreams
562                        .iter()
563                        .find(|u| u.name == r.upstream)
564                        .map(|u| (u.resolved_id(), u.enabled));
565                    (Some(r), upstream, server)
566                }
567                None => (None, None, server),
568            }
569        };
570        let (allowed_upgrades, trust_forwarded, tunnel_idle) = server;
571
572        let route = match route {
573            Some(r) => r,
574            None => {
575                debug!("No route found for {} {}", host, path);
576                return Ok(MessageHandler::not_found());
577            }
578        };
579
580        let upstream_id = match upstream {
581            Some((id, true)) => id,
582            Some((_, false)) => {
583                debug!(
584                    "Upstream '{}' for route {} is disabled",
585                    route.upstream, route.name
586                );
587                return Ok(MessageHandler::service_unavailable("Upstream disabled"));
588            }
589            None => {
590                warn!(
591                    "Upstream '{}' not found for route {}",
592                    route.upstream, route.name
593                );
594                return Ok(MessageHandler::service_unavailable("Upstream not found"));
595            }
596        };
597
598        // Rate limiting, in this route's own buckets, by the key the route
599        // asks for. It used to be one bucket per client IP for every route,
600        // whatever `key` said, sized by whichever route was reached first.
601        if let Some(ref rate_limit) = route.rate_limit {
602            let scope = route_scope(&route);
603            let by_ip = || RateLimitKey::client_ip(client_addr.ip());
604            let key = match &rate_limit.key {
605                RouteRateLimitKey::ClientIp => by_ip(),
606                RouteRateLimitKey::Route => RateLimitKey::Route(scope),
607                // A request without the header is counted by its address.
608                // One shared bucket for every such request would let a single
609                // anonymous client spend it for all the others.
610                RouteRateLimitKey::Header(name) => {
611                    let values: Vec<&[u8]> = request
612                        .headers()
613                        .get_all(name.as_str())
614                        .iter()
615                        .map(|v| v.as_bytes())
616                        .collect();
617                    if values.is_empty() {
618                        by_ip()
619                    } else {
620                        RateLimitKey::header_value(&values.join(&b',')[..])
621                    }
622                }
623            };
624            let rate = per_second(rate_limit.requests, rate_limit.window_secs);
625            if !self
626                .rate_limiter
627                .check_with_config(scope, key, rate, rate_limit.requests)
628            {
629                return Ok(MessageHandler::too_many_requests());
630            }
631        }
632
633        // Select backend
634        let backend = match self.backend_manager.select_backend(upstream_id, None) {
635            Some(b) => b,
636            None => {
637                warn!("No enabled backend for route {}", route.name);
638                return Ok(MessageHandler::service_unavailable("No backend available"));
639            }
640        };
641
642        // Build forwarded request
643        let (parts, body) = request.into_parts();
644        let mut forwarded_request = Request::from_parts(parts, body.boxed_body());
645
646        // An upgrade request keeps its Connection/Upgrade pair, but only after the
647        // strip has removed everything else -- see restore_upgrade_headers.
648        //
649        // Only for an allowed protocol. Anything else is forwarded as an
650        // ordinary request, with the strip below removing the upgrade.
651        let upgrade_protocol = MessageHandler::upgrade_protocol(&forwarded_request).filter(|p| {
652            let allowed = MessageHandler::upgrade_allowed(p, &allowed_upgrades);
653            if !allowed {
654                debug!("Declining an upgrade to {:?} from {}", p, client_addr);
655            }
656            allowed
657        });
658        // The HTTP/2 spelling of the same intent (RFC 8441), which carries no
659        // Upgrade header at all and so survives the strip untouched. It cannot
660        // be declined by forwarding it as something else -- a CONNECT is not
661        // an ordinary request -- so a protocol not allowed is refused.
662        let h2_protocol = MessageHandler::h2_connect_protocol(&forwarded_request);
663        if let Some(ref protocol) = h2_protocol {
664            if !MessageHandler::upgrade_allowed(protocol, &allowed_upgrades) {
665                debug!(
666                    "Refusing extended CONNECT for {:?} from {}",
667                    protocol, client_addr
668                );
669                return Ok(MessageHandler::not_implemented(
670                    "Extended CONNECT protocol not supported",
671                ));
672            }
673        }
674
675        // Drop hop-by-hop headers before anything else, so a client cannot name
676        // our own X-Forwarded-* headers in Connection to suppress them.
677        MessageHandler::strip_hop_by_hop_headers(&mut forwarded_request);
678
679        // Add forwarding headers
680        MessageHandler::add_forwarding_headers(
681            &mut forwarded_request,
682            client_addr,
683            proto,
684            trust_forwarded,
685        );
686
687        // Strip path prefix if configured
688        if route.strip_path {
689            if let Some(ref prefix) = route.match_.path {
690                MessageHandler::strip_path_prefix(&mut forwarded_request, prefix);
691            }
692        }
693
694        // Apply route headers
695        MessageHandler::apply_route_headers(&mut forwarded_request, &route);
696
697        // Rewrite host header
698        MessageHandler::rewrite_host_header(&mut forwarded_request, &backend.address);
699
700        // WebSocket over HTTP/2: translate to an HTTP/1.1 upgrade for the origin.
701        if let Some(protocol) = h2_protocol {
702            debug!(
703                "Extended CONNECT to {} via {} (RFC 8441)",
704                protocol, backend.address
705            );
706            return match self
707                .forwarder
708                .forward_h2_websocket(&backend, forwarded_request, &protocol, tunnel_idle)
709                .await
710            {
711                Ok(response) => Ok(response),
712                Err(e) => {
713                    error!("Extended CONNECT error to {}: {}", backend.address, e);
714                    Ok(MessageHandler::bad_gateway(&format!(
715                        "Extended CONNECT error: {}",
716                        e
717                    )))
718                }
719            };
720        }
721
722        // Tunnel an upgrade rather than treating it as request/response.
723        if let Some(protocol) = upgrade_protocol {
724            MessageHandler::restore_upgrade_headers(&mut forwarded_request, &protocol);
725            debug!(
726                "Upgrading connection to {} via {}",
727                protocol, backend.address
728            );
729            return match self
730                .forwarder
731                .forward_upgrade(&backend, forwarded_request, tunnel_idle)
732                .await
733            {
734                Ok(response) => Ok(response),
735                Err(e) => {
736                    error!("Upgrade error to {}: {}", backend.address, e);
737                    Ok(MessageHandler::bad_gateway(&format!(
738                        "Upgrade error: {}",
739                        e
740                    )))
741                }
742            };
743        }
744
745        // Forward request, under the route's timeout.
746        //
747        // `route.timeout_secs` was declarable and unread: an upstream that
748        // accepted a connection and then never answered held the request open
749        // for as long as it liked, and the timer machinery this uses was sitting
750        // in hyper_ext with nothing calling it.
751        //
752        // 504 rather than 502: the distinction is the difference between "the
753        // upstream refused" and "the upstream is not answering", and an operator
754        // reads them differently.
755        //
756        // The in-flight marker is the listener's, and only a tunnel needs it;
757        // it has no business travelling into the upstream client.
758        forwarded_request.extensions_mut().remove::<InFlight>();
759        let timeout = Duration::from_secs(u64::from(route.timeout_secs));
760        let forwarded = Timeout::new(self.forwarder.forward(&backend, forwarded_request), timeout);
761
762        match forwarded.await {
763            Ok(Ok(response)) => {
764                let (parts, body) = response.into_parts();
765                Ok(Response::from_parts(parts, body.boxed_body()))
766            }
767            Ok(Err(e)) => {
768                error!("Forward error to {}: {}", backend.address, e);
769                Ok(MessageHandler::bad_gateway(&format!(
770                    "Backend error: {}",
771                    e
772                )))
773            }
774            Err(_) => {
775                error!(
776                    "Upstream {} did not answer within {}s (route {})",
777                    backend.address, route.timeout_secs, route.name
778                );
779                Ok(MessageHandler::gateway_timeout(&format!(
780                    "Upstream did not answer within {}s",
781                    route.timeout_secs
782                )))
783            }
784        }
785    }
786}