1#![allow(clippy::result_large_err)]
13
14use std::sync::atomic::{AtomicUsize, Ordering};
15use std::sync::{Arc, RwLock};
16use std::time::Duration;
17
18use crate::application::Application;
19use crate::request::Request;
20use crate::response::{Response, STATUS_CODE_REASON_PHRASE};
21
22use super::filters::{self, status, Cors};
23use crate::server::ConnectionInfo;
24
25use super::routes::{CompiledHttpRoute, CompiledRule, Mirror};
26use super::translate::ListenerConfig;
27use super::types::{HttpRouteFilter, HttpRouteMatch};
28
29pub type PortListeners = Arc<RwLock<Arc<Vec<ListenerConfig>>>>;
32
33#[derive(Clone)]
34pub struct PortRouter {
35 pub listeners: PortListeners,
36 counter: Arc<AtomicUsize>,
37}
38
39impl PortRouter {
40 pub fn new(listeners: PortListeners) -> PortRouter {
41 PortRouter { listeners, counter: Arc::new(AtomicUsize::new(0)) }
42 }
43}
44
45#[doc(hidden)]
47pub fn request_host(request: &Request) -> String {
48 let host = request.header("host").map(|h| h.value.to_string()).unwrap_or_default();
49 let host = if host.starts_with('[') {
50 host.split(']').next().map(|h| format!("{h}]")).unwrap_or(host)
51 } else {
52 host.rsplit_once(':').map(|(h, _)| h.to_string()).unwrap_or(host)
53 };
54 host.to_ascii_lowercase()
55}
56
57#[doc(hidden)]
61pub fn host_score(pattern: &str, host: &str) -> Option<(usize, usize)> {
62 let pattern = pattern.to_ascii_lowercase();
63 match pattern.strip_prefix("*.") {
64 None => (pattern == host).then_some((pattern.len(), pattern.len())),
65 Some(suffix) => (host.len() > suffix.len() + 1 && host.ends_with(&format!(".{suffix}"))).then_some((0, pattern.len())),
66 }
67}
68
69#[doc(hidden)]
72pub fn pick_listener<'a>(listeners: &'a [ListenerConfig], host: &str) -> Option<&'a ListenerConfig> {
73 listeners
74 .iter()
75 .filter_map(|l| match l.hostname.as_deref().filter(|h| !h.is_empty()) {
76 None => Some(((0, 0), l)),
77 Some(pattern) => host_score(pattern, host).map(|s| (s, l)),
78 })
79 .max_by_key(|(score, _)| *score)
80 .map(|(_, l)| l)
81}
82
83fn path_of(request: &Request) -> &str {
84 let uri = request.request_uri.as_str();
85 uri.split(['?', '#']).next().unwrap_or(uri)
86}
87
88fn query_value(request: &Request, name: &str) -> Option<String> {
89 let query = request.request_uri.split_once('?')?.1;
90 let query = query.split('#').next().unwrap_or(query);
91 query.split('&').find_map(|pair| {
92 let (k, v) = pair.split_once('=').unwrap_or((pair, ""));
93 (crate::url::URL::percent_decode(k) == name).then(|| crate::url::URL::percent_decode(v))
94 })
95}
96
97fn grpc_method(path: &str) -> Option<&str> {
99 let (service, method) = path.strip_prefix('/')?.split_once('/')?;
100 (!service.is_empty() && !method.is_empty() && !method.contains('/')).then_some(method)
101}
102
103#[doc(hidden)]
106pub fn prefix_matches(prefix: &str, path: &str) -> bool {
107 let prefix = if prefix.len() > 1 { prefix.trim_end_matches('/') } else { prefix };
108 prefix == "/" || path == prefix || (path.starts_with(prefix) && path.as_bytes().get(prefix.len()) == Some(&b'/'))
109}
110
111fn match_score(m: &HttpRouteMatch, request: &Request) -> Option<(u8, usize, u8, usize, usize)> {
114 let path = path_of(request);
115 let (exact, length) = match &m.path {
116 None => (0, 1),
117 Some(p) => {
118 let value = p.value.as_deref().unwrap_or("/");
119 match p.match_type.as_deref().unwrap_or("PathPrefix") {
120 "Exact" if path == value => (1, value.len()),
121 "PathPrefix" if prefix_matches(value, path) => (0, value.len()),
122 super::routes::GRPC_METHOD_MATCH if grpc_method(path) == Some(value) => (0, value.len()),
123 _ => return None,
124 }
125 }
126 };
127 if let Some(method) = &m.method {
128 if &request.method != method {
129 return None;
130 }
131 }
132 for header in &m.headers {
133 if header.match_type.as_deref().unwrap_or("Exact") != "Exact" {
134 return None;
135 }
136 if request.header(&header.name).map(|h| h.value.as_ref()) != Some(header.value.as_str()) {
137 return None;
138 }
139 }
140 for param in &m.query_params {
141 if param.match_type.as_deref().unwrap_or("Exact") != "Exact" {
142 return None;
143 }
144 if query_value(request, ¶m.name).as_deref() != Some(param.value.as_str()) {
145 return None;
146 }
147 }
148 Some((exact, length, m.method.is_some() as u8, m.headers.len(), m.query_params.len()))
149}
150
151#[doc(hidden)]
153pub fn select<'a>(listener: &'a ListenerConfig, request: &Request, host: &str) -> Option<(&'a CompiledHttpRoute, &'a CompiledRule, &'a HttpRouteMatch)> {
154 type Key<'k> = ((usize, usize), (u8, usize, u8, usize, usize), std::cmp::Reverse<(&'k str, &'k (String, String), usize, usize)>);
155 let mut best: Option<(Key, &CompiledHttpRoute, &CompiledRule, &HttpRouteMatch)> = None;
156 for route in &listener.http_routes {
157 let host_match = if route.hostnames.is_empty() {
158 Some((0, 0))
159 } else {
160 route.hostnames.iter().filter_map(|h| host_score(h, host)).max()
161 };
162 let Some(host_match) = host_match else { continue };
163 for (rule_index, rule) in route.rules.iter().enumerate() {
164 for (match_index, m) in rule.matches.iter().enumerate() {
165 let Some(score) = match_score(m, request) else { continue };
166 let key = (host_match, score, std::cmp::Reverse((route.created.as_str(), &route.key, rule_index, match_index)));
167 if best.as_ref().is_none_or(|(b, ..)| key > *b) {
168 best = Some((key, route, rule, m));
169 }
170 }
171 }
172 }
173 best.map(|(_, route, rule, m)| (route, rule, m))
174}
175
176fn sample(share: f64) -> bool {
178 use std::hash::{BuildHasher, Hasher};
179 static KEYS: std::sync::OnceLock<std::collections::hash_map::RandomState> = std::sync::OnceLock::new();
180 static COUNTER: AtomicUsize = AtomicUsize::new(0);
181 if share >= 1.0 {
182 return true;
183 }
184 let mut hasher = KEYS.get_or_init(Default::default).build_hasher();
185 hasher.write_usize(COUNTER.fetch_add(1, Ordering::Relaxed));
186 ((hasher.finish() >> 11) as f64 / (1u64 << 53) as f64) < share
187}
188
189fn send_mirror(mirror: &Mirror, request: &Request, client: crate::proxy::Peer<'_>) {
192 let Ok(endpoints) = &mirror.endpoints else { return };
193 let Some(endpoint) = endpoints.get(MIRROR_COUNTER.fetch_add(1, Ordering::Relaxed) % endpoints.len().max(1)).cloned() else { return };
194 let request = request.clone();
195 let (client_ip, tls) = (client.ip.to_string(), client.tls);
196 std::thread::spawn(move || {
197 let Some((ip, port)) = endpoint.rsplit_once(':') else { return };
198 let ip = ip.trim_start_matches('[').trim_end_matches(']');
199 let Ok(port) = port.parse::<u16>() else { return };
200 let host = request.header("host").map(|h| h.value.to_string()).unwrap_or_else(|| endpoint.clone());
201 let _ = crate::proxy::forward_pooled_with_host(crate::proxy::shared_pool(), &request, crate::proxy::Peer { ip: &client_ip, tls }, ip, port, &host, Duration::from_secs(2), Duration::from_secs(10));
202 });
203}
204
205static MIRROR_COUNTER: AtomicUsize = AtomicUsize::new(0);
206
207enum Attempt {
209 Response(Response),
211 Failed { timed_out: bool, error: String, next_hop: String },
214}
215
216const DEFAULT_TIMEOUT: Duration = Duration::from_secs(60);
218const DEFAULT_BACKOFF: Duration = Duration::from_millis(25);
220
221struct Policy<'a> {
226 rule: &'a CompiledRule,
227 deadline: Option<std::time::Instant>,
228 per_attempt: Option<Duration>,
229 retries: u32,
230 backoff: Duration,
231 attempt: u32,
232}
233
234impl<'a> Policy<'a> {
235 fn new(rule: &'a CompiledRule) -> Policy<'a> {
236 let limit = |value: Option<&String>| value.and_then(|v| filters::duration(v)).filter(|d| !d.is_zero());
237 let timeouts = rule.timeouts.as_ref();
238 Policy {
239 rule,
240 deadline: limit(timeouts.and_then(|t| t.request.as_ref())).map(|d| std::time::Instant::now() + d),
241 per_attempt: limit(timeouts.and_then(|t| t.backend_request.as_ref())),
242 retries: rule.retry.as_ref().map(|r| r.attempts.unwrap_or(1).max(0) as u32).unwrap_or(0),
243 backoff: rule.retry.as_ref().and_then(|r| r.backoff.as_deref()).and_then(filters::duration).unwrap_or(DEFAULT_BACKOFF),
244 attempt: 0,
245 }
246 }
247
248 fn timeout(&self) -> Result<Duration, Response> {
250 let left = self.deadline.map(|d| d.saturating_duration_since(std::time::Instant::now()));
251 if left.is_some_and(|l| l.is_zero()) {
252 return Err(status(STATUS_CODE_REASON_PHRASE.n504_gateway_timeout));
253 }
254 Ok([self.per_attempt, left].into_iter().flatten().min().unwrap_or(DEFAULT_TIMEOUT))
255 }
256
257 fn settle(&mut self, outcome: Attempt) -> Result<Response, Duration> {
259 let retry_on = |code: i16| self.rule.retry.as_ref().is_some_and(|r| r.codes.contains(&(code as i32)));
260 let retry = self.attempt < self.retries
261 && match &outcome {
262 Attempt::Response(response) => retry_on(response.status_code),
263 Attempt::Failed { .. } => true,
264 };
265 if !retry {
266 return Ok(match outcome {
267 Attempt::Response(response) => response,
268 Attempt::Failed { timed_out: true, error, next_hop } => {
269 let mut response = status(STATUS_CODE_REASON_PHRASE.n504_gateway_timeout);
270 crate::proxy::status::failed(&mut response, crate::proxy::status::ProxyError::HttpResponseTimeout, Some(&next_hop), Some(&error));
271 response
272 }
273 Attempt::Failed { timed_out: false, error, next_hop } => {
274 let mut response = status(STATUS_CODE_REASON_PHRASE.n502_bad_gateway);
275 crate::proxy::status::failed_with(&mut response, &error, Some(&next_hop));
276 response
277 }
278 });
279 }
280 self.attempt += 1;
281 Err(self.deadline.map_or(self.backoff, |d| self.backoff.min(d.saturating_duration_since(std::time::Instant::now()))))
282 }
283}
284
285struct Target<'a> {
288 backend: &'a super::routes::Backend,
289 request: std::borrow::Cow<'a, Request>,
290 ip: String,
291 port: u16,
292 host_header: String,
293}
294
295fn connect_timeout(timeout: Duration) -> Duration {
297 timeout.min(Duration::from_secs(10))
298}
299
300impl PortRouter {
301 #[cfg(test)]
302 fn forward(&self, rule: &CompiledRule, request: &Request, connection: &ConnectionInfo) -> Response {
303 self.forward_with(rule, request, connection, None)
304 }
305
306 fn forward_with(&self, rule: &CompiledRule, request: &Request, connection: &ConnectionInfo, client_cert: Option<&(Vec<u8>, Vec<u8>)>) -> Response {
309 let mut policy = Policy::new(rule);
310 loop {
311 let timeout = match policy.timeout() {
312 Ok(timeout) => timeout,
313 Err(response) => return response,
314 };
315 match policy.settle(self.attempt(rule, request, connection, timeout, client_cert)) {
316 Ok(response) => return response,
317 Err(pause) => std::thread::sleep(pause),
318 }
319 }
320 }
321
322 async fn forward_async(&self, rule: &CompiledRule, request: &Request, connection: &ConnectionInfo, client_cert: Option<&(Vec<u8>, Vec<u8>)>) -> Response {
326 let mut policy = Policy::new(rule);
327 loop {
328 let timeout = match policy.timeout() {
329 Ok(timeout) => timeout,
330 Err(response) => return response,
331 };
332 match policy.settle(self.attempt_async(rule, request, connection, timeout, client_cert).await) {
333 Ok(response) => return response,
334 Err(pause) => tokio::time::sleep(pause).await,
335 }
336 }
337 }
338
339 fn target<'a>(&self, rule: &'a CompiledRule, request: &'a Request) -> Result<Target<'a>, Response> {
342 let total: u32 = rule.backends.iter().map(|b| b.weight).sum();
343 if total == 0 {
344 return Err(status(STATUS_CODE_REASON_PHRASE.n500_internal_server_error));
346 }
347 let roll = (self.counter.fetch_add(1, Ordering::Relaxed) as u64).wrapping_mul(0x9E37_79B9_7F4A_7C15) >> 32;
348 let mut pick = (roll % total as u64) as u32;
349 let backend = rule
350 .backends
351 .iter()
352 .find(|b| {
353 if pick < b.weight {
354 true
355 } else {
356 pick -= b.weight;
357 false
358 }
359 })
360 .expect("weights sum to total");
361 let request = if backend.filters.is_empty() {
362 std::borrow::Cow::Borrowed(request)
363 } else {
364 let mut copy = request.clone();
365 for filter in &backend.filters {
366 if let Some(f) = filter.request_header_modifier.as_ref().filter(|_| filter.filter_type == "RequestHeaderModifier") {
367 filters::modify_headers(&mut copy.headers, f);
368 }
369 }
370 std::borrow::Cow::Owned(copy)
371 };
372 let endpoints = match &backend.endpoints {
373 Err(_) => return Err(status(STATUS_CODE_REASON_PHRASE.n500_internal_server_error)),
374 Ok(e) if e.is_empty() => return Err(status(STATUS_CODE_REASON_PHRASE.n503_service_unavailable)),
375 Ok(e) => e,
376 };
377 let endpoint = &endpoints[self.counter.fetch_add(1, Ordering::Relaxed) % endpoints.len()];
378 let (ip, port) = endpoint
379 .rsplit_once(':')
380 .and_then(|(ip, port)| Some((ip.trim_start_matches('[').trim_end_matches(']').to_string(), port.parse::<u16>().ok()?)))
381 .ok_or_else(|| status(STATUS_CODE_REASON_PHRASE.n500_internal_server_error))?;
382 let host_header = request.header("host").map(|h| h.value.to_string()).unwrap_or_else(|| endpoint.clone());
383 Ok(Target { backend, request, ip, port, host_header })
384 }
385
386 fn backend_tls(target: &Target, client_cert: Option<&(Vec<u8>, Vec<u8>)>) -> Result<Option<super::backend_tls::BackendTls>, Response> {
389 match &target.backend.tls {
390 None => Ok(None),
391 Some(Err(_)) => Err(status(STATUS_CODE_REASON_PHRASE.n500_internal_server_error)),
393 Some(Ok(tls)) => match client_cert {
394 None => Ok(Some(tls.clone())),
395 Some((chain, key)) => tls.with_client_cert(chain, key).map(Some).map_err(|_| status(STATUS_CODE_REASON_PHRASE.n500_internal_server_error)),
396 },
397 }
398 }
399
400 fn attempt(&self, rule: &CompiledRule, request: &Request, connection: &ConnectionInfo, timeout: Duration, client_cert: Option<&(Vec<u8>, Vec<u8>)>) -> Attempt {
402 let target = match self.target(rule, request) {
403 Ok(target) => target,
404 Err(response) => return Attempt::Response(response),
405 };
406 let tls = match Self::backend_tls(&target, client_cert) {
407 Ok(tls) => tls,
408 Err(response) => return Attempt::Response(response),
409 };
410 let (request, ip, port, host_header) = (target.request.as_ref(), target.ip.as_str(), target.port, target.host_header.as_str());
411 let started = std::time::Instant::now();
412 let next_hop = format!("{}:{}", target.ip, target.port);
413 let forwarded = if let Some(tls) = &tls {
414 crate::proxy::forward_tls_with_host(&tls.config, &tls.label, request, crate::proxy::Peer::of(connection), ip, port, &tls.server_name, host_header, connect_timeout(timeout), timeout)
415 } else if super::upgrade::is_upgrade(request) {
416 super::upgrade::forward(request, crate::proxy::Peer::of(connection), ip, port, host_header, timeout)
417 } else if target.backend.app_protocol.as_deref() == Some("kubernetes.io/h2c") {
418 crate::proxy::forward_h2c_with_host(request, crate::proxy::Peer::of(connection), ip, port, host_header, connect_timeout(timeout), timeout)
419 } else {
420 crate::proxy::forward_pooled_with_host(crate::proxy::shared_pool(), request, crate::proxy::Peer::of(connection), ip, port, host_header, connect_timeout(timeout), timeout)
421 };
422 match forwarded {
423 Ok(mut response) => {
424 crate::proxy::status::received(&mut response, Some(&next_hop));
425 Attempt::Response(response)
426 }
427 Err(error) => Attempt::Failed { timed_out: started.elapsed() >= timeout, error, next_hop },
428 }
429 }
430
431 async fn attempt_async(&self, rule: &CompiledRule, request: &Request, connection: &ConnectionInfo, timeout: Duration, client_cert: Option<&(Vec<u8>, Vec<u8>)>) -> Attempt {
434 let target = match self.target(rule, request) {
435 Ok(target) => target,
436 Err(response) => return Attempt::Response(response),
437 };
438 let tls = match Self::backend_tls(&target, client_cert) {
439 Ok(tls) => tls,
440 Err(response) => return Attempt::Response(response),
441 };
442 let started = std::time::Instant::now();
443 let next_hop = format!("{}:{}", target.ip, target.port);
444 let forwarded = if tls.is_none() && target.backend.app_protocol.as_deref() == Some("kubernetes.io/h2c") {
445 let (request, client_ip, tls_client, ip, port, host) = (target.request.into_owned(), connection.client.ip.clone(), connection.tls, target.ip, target.port, target.host_header);
447 tokio::task::spawn_blocking(move || crate::proxy::forward_h2c_with_host(&request, crate::proxy::Peer { ip: &client_ip, tls: tls_client }, &ip, port, &host, connect_timeout(timeout), timeout))
448 .await
449 .unwrap_or_else(|e| Err(e.to_string()))
450 } else {
451 let tls = tls.as_ref().map(|t| (t.server_name.as_str(), crate::proxy::async_upstream::AsyncTls { config: std::sync::Arc::clone(&t.config), pool: crate::proxy::async_upstream::gateway_tls_pool(&t.label) }));
452 crate::proxy::async_upstream::forward_gateway(&target.request, crate::proxy::Peer::of(connection), &target.ip, target.port, &target.host_header, tls, connect_timeout(timeout), timeout).await
453 };
454 match forwarded {
455 Ok(mut response) => {
456 crate::proxy::status::received(&mut response, Some(&next_hop));
457 Attempt::Response(response)
458 }
459 Err(error) => Attempt::Failed { timed_out: started.elapsed() >= timeout, error, next_hop },
460 }
461 }
462}
463
464struct Prepared<'a> {
466 request: Option<Request>,
468 response_filters: Vec<&'a HttpRouteFilter>,
469 cors_headers: Vec<crate::header::Header>,
470}
471
472impl PortRouter {
473 fn prepare<'a>(&self, listener: &ListenerConfig, rule: &'a CompiledRule, matched: &HttpRouteMatch, request: &Request, host: &str, connection: &ConnectionInfo) -> Result<Prepared<'a>, Response> {
478 let mut prepared = Prepared { request: None, response_filters: Vec::new(), cors_headers: Vec::new() };
479 if rule.filters.is_empty() {
480 return Ok(prepared);
481 }
482 let mut request = request.clone();
483 let mut mirrored = 0;
484 for filter in &rule.filters {
485 match filter.filter_type.as_str() {
486 "RequestHeaderModifier" => {
487 if let Some(f) = &filter.request_header_modifier {
488 filters::modify_headers(&mut request.headers, f);
489 }
490 }
491 "ResponseHeaderModifier" => prepared.response_filters.push(filter),
492 "RequestRedirect" => {
493 if let Some(f) = &filter.request_redirect {
494 return Err(filters::redirect(&request, host, listener.protocol == "HTTPS", listener.port as u16, matched, f));
495 }
496 }
497 "URLRewrite" => {
498 if let Some(f) = &filter.url_rewrite {
499 if let Some(hostname) = &f.hostname {
500 request.headers.retain(|h| !h.name.eq_ignore_ascii_case("host"));
501 request.headers.push(crate::header::Header::new("Host", hostname.clone()));
502 }
503 if let Some(modifier) = &f.path {
504 let (path, query) = filters::split_target(&request.request_uri);
505 request.request_uri = format!("{}{query}", filters::modified_path(path, matched, modifier));
506 }
507 }
508 }
509 "CORS" => {
510 if let Some(f) = &filter.cors {
511 match filters::cors(&request, f) {
512 Cors::Pass => {}
513 Cors::Preflight(headers) => {
514 let mut response = status(STATUS_CODE_REASON_PHRASE.n204_no_content);
515 response.headers.extend(headers);
516 return Err(response);
517 }
518 Cors::Actual(headers) => prepared.cors_headers = headers,
519 }
520 }
521 }
522 "RequestMirror" => mirrored += filter.request_mirror.is_some() as usize,
523 _ => return Err(status(STATUS_CODE_REASON_PHRASE.n500_internal_server_error)),
525 }
526 }
527 for mirror in rule.mirrors.iter().take(mirrored) {
529 if sample(mirror.share) {
530 send_mirror(mirror, &request, crate::proxy::Peer::of(connection));
531 }
532 }
533 prepared.request = Some(request);
534 Ok(prepared)
535 }
536
537 fn finish(prepared: Prepared, mut response: Response) -> Response {
539 for filter in prepared.response_filters {
540 if let Some(f) = &filter.response_header_modifier {
541 filters::modify_headers(&mut response.headers, f);
542 }
543 }
544 for header in prepared.cors_headers {
545 response.headers.retain(|h| !h.name.eq_ignore_ascii_case(&header.name));
546 response.headers.push(header);
547 }
548 response
549 }
550
551 fn route(&self, listener: &ListenerConfig, rule: &CompiledRule, matched: &HttpRouteMatch, request: &Request, host: &str, connection: &ConnectionInfo) -> Response {
553 match self.prepare(listener, rule, matched, request, host, connection) {
554 Err(response) => response,
555 Ok(prepared) => {
556 let response = self.forward_with(rule, prepared.request.as_ref().unwrap_or(request), connection, listener.backend_client_cert.as_ref());
557 Self::finish(prepared, response)
558 }
559 }
560 }
561}
562
563fn misdirected(listeners: &[ListenerConfig], by_host: &ListenerConfig, connection: &ConnectionInfo) -> bool {
566 let Some(sni) = connection.sni_hostname.as_deref() else { return false };
567 pick_listener(listeners, &sni.to_ascii_lowercase()).is_some_and(|by_sni| !std::ptr::eq(by_sni, by_host))
568}
569
570impl PortRouter {
571 fn resolve<'l>(listeners: &'l [ListenerConfig], request: &Request, host: &str, connection: &ConnectionInfo) -> Result<(&'l ListenerConfig, &'l CompiledRule, &'l HttpRouteMatch), Response> {
574 let Some(listener) = pick_listener(listeners, host) else {
575 return Err(status(STATUS_CODE_REASON_PHRASE.n404_not_found));
576 };
577 if misdirected(listeners, listener, connection) {
578 return Err(status(STATUS_CODE_REASON_PHRASE.n421_misdirected_request));
579 }
580 match select(listener, request, host) {
581 Some((_, rule, matched)) => Ok((listener, rule, matched)),
582 None => Err(status(STATUS_CODE_REASON_PHRASE.n404_not_found)),
583 }
584 }
585}
586
587impl Application for PortRouter {
588 fn execute(&self, request: &Request, connection: &ConnectionInfo) -> Result<Response, String> {
589 let listeners = self.listeners.read().unwrap_or_else(|e| e.into_inner()).clone();
590 let host = request_host(request);
591 Ok(match Self::resolve(&listeners, request, &host, connection) {
592 Ok((listener, rule, matched)) => self.route(listener, rule, matched, request, &host, connection),
593 Err(response) => response,
594 })
595 }
596
597 #[cfg(feature = "http2")]
601 fn execute_async<'a>(&'a self, request: &'a Request, connection: &'a ConnectionInfo) -> Option<crate::application::AsyncResponse<'a>> {
602 if super::upgrade::is_upgrade(request) {
603 return None;
604 }
605 Some(Box::pin(async move {
606 let listeners = self.listeners.read().unwrap_or_else(|e| e.into_inner()).clone();
607 let host = request_host(request);
608 let (listener, rule, matched) = match Self::resolve(&listeners, request, &host, connection) {
609 Ok(found) => found,
610 Err(response) => return Ok(response),
611 };
612 let prepared = match self.prepare(listener, rule, matched, request, &host, connection) {
613 Ok(prepared) => prepared,
614 Err(response) => return Ok(response),
615 };
616 let response = self.forward_async(rule, prepared.request.as_ref().unwrap_or(request), connection, listener.backend_client_cert.as_ref()).await;
617 Ok(Self::finish(prepared, response))
618 }))
619 }
620}
621
622#[cfg(test)]
623mod tests;