1use axum::{
6 extract::{Request, State},
7 http::HeaderMap,
8 middleware::Next,
9 response::Response,
10};
11use std::sync::Arc;
12
13use crate::{client::{proxy_request, ProxyConfig}, error::ProxyError, routing::{extract_subdomain, RouteResolver}};
14
15#[derive(Debug, Clone)]
17pub struct Subdomain(pub String);
18
19pub async fn subdomain_extractor(
31 headers: HeaderMap,
32 mut request: Request,
33 next: Next,
34) -> Response {
35 let subdomain = headers
36 .get("host")
37 .and_then(|host| host.to_str().ok())
38 .and_then(extract_subdomain);
39
40 if let Some(subdomain) = subdomain {
41 request.extensions_mut().insert(Subdomain(subdomain));
42 }
43
44 next.run(request).await
45}
46
47pub async fn subdomain_proxy<R>(
68 State((resolver, config)): State<(Arc<R>, ProxyConfig)>,
69 request: Request,
70) -> Result<Response, ProxyError>
71where
72 R: RouteResolver,
73{
74 let subdomain = request
76 .extensions()
77 .get::<Subdomain>()
78 .map(|s| s.0.clone());
79
80 let subdomain = subdomain.ok_or_else(|| ProxyError::RouteNotFound("No subdomain found".to_string()))?;
81
82 let backend_url = resolver
84 .resolve(&subdomain)
85 .await
86 .ok_or_else(|| ProxyError::RouteNotFound(format!("No route for subdomain: {}", subdomain)))?;
87
88 proxy_request(request, &backend_url, config).await
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 #[test]
97 fn test_subdomain_type() {
98 let subdomain = Subdomain("api".to_string());
99 assert_eq!(subdomain.0, "api");
100 }
101}