|
| 1 | +use axum::{routing::get, Router}; |
| 2 | +use std::{io, net::SocketAddr, time::Duration}; |
| 3 | +use tls_listener::TlsListener; |
| 4 | +use tokio::net::{TcpListener, TcpStream}; |
| 5 | + |
| 6 | +mod tls_config; |
| 7 | +use tls_config::tls_acceptor; |
| 8 | + |
| 9 | +/// An example of running an axum server with `TlsListener`. |
| 10 | +/// |
| 11 | +/// One can also bypass `axum::serve` and use the `Router` with Hyper's `serve_connection` API |
| 12 | +/// directly. The main advantages of using `axum::serve` are that |
| 13 | +/// - graceful shutdown is made easy with axum's `.with_graceful_shutdown` API, and |
| 14 | +/// - the Hyper server is configured by axum itself, allowing options specific to axum to be set |
| 15 | +/// (for example, axum currently enables the `CONNECT` protocol in order to support HTTP/2 |
| 16 | +/// websockets). |
| 17 | +#[tokio::main(flavor = "current_thread")] |
| 18 | +async fn main() { |
| 19 | + let app = Router::new().route("/", get(|| async { "Hello, World!" })); |
| 20 | + |
| 21 | + let local_addr = "0.0.0.0:3000".parse::<SocketAddr>().unwrap(); |
| 22 | + let tcp_listener = tokio::net::TcpListener::bind(local_addr).await.unwrap(); |
| 23 | + let listener = Listener { |
| 24 | + inner: TlsListener::new(tls_acceptor(), tcp_listener), |
| 25 | + local_addr, |
| 26 | + }; |
| 27 | + |
| 28 | + axum::serve(listener, app).await.unwrap(); |
| 29 | +} |
| 30 | + |
| 31 | +// We use a wrapper type to bridge axum's `Listener` trait to our `TlsListener` type. |
| 32 | +struct Listener { |
| 33 | + inner: TlsListener<TcpListener, tls_config::Acceptor>, |
| 34 | + local_addr: SocketAddr, |
| 35 | +} |
| 36 | + |
| 37 | +impl axum::serve::Listener for Listener { |
| 38 | + type Io = tls_config::Stream<TcpStream>; |
| 39 | + type Addr = SocketAddr; |
| 40 | + async fn accept(&mut self) -> (Self::Io, Self::Addr) { |
| 41 | + loop { |
| 42 | + // To change the TLS certificate dynamically, you could `select!` on this call with a |
| 43 | + // channel receiver, and call `self.inner.replace_acceptor` in the other branch. |
| 44 | + match self.inner.accept().await { |
| 45 | + Ok(tuple) => break tuple, |
| 46 | + Err(tls_listener::Error::ListenerError(e)) if !is_connection_error(&e) => { |
| 47 | + // See https://github.com/tokio-rs/axum/blob/da3539cb0e5eed381361b2e688a776da77c52cd6/axum/src/serve/listener.rs#L145-L157 |
| 48 | + // for the rationale. |
| 49 | + tokio::time::sleep(Duration::from_secs(1)).await |
| 50 | + } |
| 51 | + Err(_) => continue, |
| 52 | + } |
| 53 | + } |
| 54 | + } |
| 55 | + fn local_addr(&self) -> io::Result<Self::Addr> { |
| 56 | + Ok(self.local_addr) |
| 57 | + } |
| 58 | +} |
| 59 | + |
| 60 | +// Taken from https://github.com/tokio-rs/axum/blob/da3539cb0e5eed381361b2e688a776da77c52cd6/axum/src/serve/listener.rs#L160-L167 |
| 61 | +fn is_connection_error(e: &io::Error) -> bool { |
| 62 | + matches!( |
| 63 | + e.kind(), |
| 64 | + io::ErrorKind::ConnectionRefused |
| 65 | + | io::ErrorKind::ConnectionAborted |
| 66 | + | io::ErrorKind::ConnectionReset |
| 67 | + ) |
| 68 | +} |
0 commit comments