reverse_proxy/
reverse_proxy.rs1use axum::{
16 body::Body,
17 extract::{ConnectInfo, State},
18 http::{
19 header::{HeaderValue, HOST},
20 Request, StatusCode,
21 },
22 response::{IntoResponse, Response},
23 Router,
24};
25use axum_server::tls_rustls::RustlsConfig;
26use hyper_util::{
27 client::legacy::{connect::HttpConnector, Client},
28 rt::TokioExecutor,
29};
30use std::{net::SocketAddr, sync::Arc};
31
32#[derive(Clone)]
33struct AppState {
34 upstream: String,
35 client: Client<HttpConnector, Body>,
36}
37
38#[tokio::main]
39async fn main() {
40 let upstream = std::env::var("UPSTREAM")
41 .unwrap_or_else(|_| "http://127.0.0.1:8080".to_owned())
42 .trim_end_matches('/')
43 .to_owned();
44
45 upstream.parse::<http::Uri>().expect("invalid UPSTREAM");
47
48 let state = Arc::new(AppState {
49 upstream,
50 client: Client::builder(TokioExecutor::new()).build_http(),
51 });
52
53 let app = Router::new().fallback(proxy).with_state(state);
54
55 let config = RustlsConfig::from_pem_file(
56 "examples/self-signed-certs/cert.pem",
57 "examples/self-signed-certs/key.pem",
58 )
59 .await
60 .unwrap();
61
62 let addr = SocketAddr::from(([127, 0, 0, 1], 3443));
63 println!("listening on https://{addr}");
64
65 axum_server::bind_rustls(addr, config)
66 .serve(app.into_make_service_with_connect_info::<SocketAddr>())
67 .await
68 .unwrap();
69}
70
71async fn proxy(
72 State(state): State<Arc<AppState>>,
73 ConnectInfo(remote): ConnectInfo<SocketAddr>,
74 mut req: Request<Body>,
75) -> Response {
76 let path_and_query = req
77 .uri()
78 .path_and_query()
79 .map(|path| path.as_str())
80 .unwrap_or("/");
81
82 let host = req.headers().get(HOST).cloned();
83
84 *req.uri_mut() = match format!("{}{}", state.upstream, path_and_query).parse() {
85 Ok(uri) => uri,
86 Err(_) => return (StatusCode::BAD_REQUEST, "invalid upstream request URI").into_response(),
87 };
88
89 if let Some(host) = host {
92 req.headers_mut().insert(HOST, host);
93 }
94 req.headers_mut()
95 .insert("x-forwarded-proto", HeaderValue::from_static("https"));
96 if let Ok(ip) = HeaderValue::from_str(&remote.ip().to_string()) {
97 req.headers_mut().insert("x-forwarded-for", ip);
98 }
99
100 match state.client.request(req).await {
101 Ok(response) => response.into_response(),
102 Err(error) => (
103 StatusCode::BAD_GATEWAY,
104 format!("upstream request failed: {error}"),
105 )
106 .into_response(),
107 }
108}