Skip to content

Commit fd3793a

Browse files
authored
Merge pull request #54 from SabrinaJewson/axum
Add axum example
2 parents 24beb23 + adbc206 commit fd3793a

File tree

4 files changed

+82
-0
lines changed

4 files changed

+82
-0
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ tokio-openssl = { version = "0.6.3", optional = true }
3131
openssl_impl = { package = "openssl", version = "0.10.32", optional = true }
3232

3333
[dev-dependencies]
34+
axum = "0.8.1"
3435
hyper = { version = "1.0", features = ["http1", "server"] }
3536
hyper-util = { version = "0.1.1", features = ["tokio"] }
3637
tokio = { version = "1.0", features = [

examples/axum.rs

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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+
}

examples/test_examples.py

+4
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,7 @@ def test_http_stream(self):
133133
def test_http_plain(self):
134134
with run_example("http"):
135135
self.http_test()
136+
137+
def test_axum(self):
138+
with run_example("axum"):
139+
self.http_test()

examples/tls_config/mod.rs

+9
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ mod config {
1515

1616
pub type Acceptor = tokio_rustls::TlsAcceptor;
1717

18+
#[allow(dead_code)]
19+
pub type Stream<T> = tokio_rustls::server::TlsStream<T>;
20+
1821
fn tls_acceptor_impl(key_der: &[u8], cert_der: &[u8]) -> Acceptor {
1922
let key = PrivateKeyDer::Pkcs1(key_der.to_owned().into());
2023
let cert = CertificateDer::from(cert_der).into_owned();
@@ -49,6 +52,9 @@ mod config {
4952

5053
pub type Acceptor = tokio_native_tls::TlsAcceptor;
5154

55+
#[allow(dead_code)]
56+
pub type Stream<T> = tokio_native_tls::TlsStream<T>;
57+
5258
fn tls_acceptor_impl(pfx: &[u8]) -> Acceptor {
5359
let identity = Identity::from_pkcs12(pfx, "").unwrap();
5460
TlsAcceptor::builder(identity).build().unwrap().into()
@@ -73,6 +79,9 @@ mod config {
7379

7480
pub type Acceptor = openssl_impl::ssl::SslContext;
7581

82+
#[allow(dead_code)]
83+
pub type Stream<T> = tokio_openssl::SslStream<T>;
84+
7685
fn tls_acceptor_impl<P: AsRef<Path>>(cert_file: P, key_file: P) -> Acceptor {
7786
let mut builder = SslContext::builder(SslMethod::tls_server()).unwrap();
7887
builder

0 commit comments

Comments
 (0)