Skip to main content

http2_proxy/
http2-proxy.rs

1mod server {
2    use std::convert::Infallible;
3    use std::net::SocketAddr;
4
5    use http_body_util::Full;
6    use hyper::body::Bytes;
7    use hyper::server::conn::http2;
8    use hyper::service::service_fn;
9    use hyper::{Request, Response};
10    use hyper_util::rt::{TokioExecutor, TokioIo};
11    use tokio::net::TcpListener;
12
13    async fn hello(
14        request: Request<hyper::body::Incoming>,
15    ) -> Result<Response<Full<Bytes>>, Infallible> {
16        let uri = request.uri();
17
18        let response = match uri.path() {
19            "/healthz" => Bytes::from("We're healthy"),
20            "/hello" => Bytes::from("Hello world"),
21            _ => Bytes::from("Standard response"),
22        };
23
24        Ok(Response::new(Full::new(response)))
25    }
26
27    pub(super) async fn serve() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
28        let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
29
30        // Bind to the port and listen for incoming TCP connections
31        let listener = TcpListener::bind(addr).await?;
32
33        loop {
34            let (stream, _) = listener.accept().await?;
35            let io = TokioIo::new(stream);
36
37            tokio::task::spawn(async move {
38                if let Err(error) = http2::Builder::new(TokioExecutor::new())
39                    .serve_connection(io, service_fn(hello))
40                    .await
41                {
42                    eprintln!("Error serving connection: {}", error);
43                }
44            });
45        }
46    }
47}
48
49mod proxy {
50    use axum::Router;
51    use http::uri::Scheme;
52    use tower_proxy::builder;
53    use tower_proxy::client::Builder;
54
55    pub(super) async fn serve() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
56        let proxy_service_builder = builder(
57            Builder::new(hyper_util::rt::TokioExecutor::new())
58                .http2_only(true)
59                .build_http(),
60            Scheme::HTTP,
61            "127.0.0.1:3000",
62        )?;
63
64        let svc: tower_proxy::ReusedService<
65            tower_proxy::Identity,
66            tower_proxy::client::HttpConnector,
67            axum::body::Body,
68        > = proxy_service_builder.build(tower_proxy::rewrite::Identity {});
69
70        let router = Router::new().fallback_service(svc);
71
72        let listener = tokio::net::TcpListener::bind("127.0.0.1:2000")
73            .await
74            .unwrap();
75
76        axum::serve(listener, router).await?;
77
78        Ok(())
79    }
80}
81
82#[tokio::main]
83async fn main() {
84    let _server = tokio::task::spawn(server::serve());
85
86    let _proxy = tokio::task::spawn(proxy::serve());
87
88    let text = reqwest::get("http://127.0.0.1:2000/hello")
89        .await
90        .unwrap()
91        .text()
92        .await;
93
94    assert!(
95        matches!(text.as_deref(), Ok("Hello world")),
96        "Server returned response"
97    );
98}