Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 20 additions & 17 deletions axum/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ allowed = [
"tokio",
# for the `__private` feature
"reqwest",
"rustls",
"tokio-rustls",
]

[features]
Expand Down Expand Up @@ -95,44 +97,45 @@ __private = ["tokio", "http1", "dep:reqwest"]

[dependencies]
axum-core = { path = "../axum-core", version = "0.5.5" }

# optional dependencies
axum-macros = { path = "../axum-macros", version = "0.5.0", optional = true }
base64 = { version = "0.22.1", optional = true }
bytes = "1.7"
form_urlencoded = { version = "1.1.0", optional = true }
futures-core = "0.3"
futures-sink = { version = "0.3", optional = true }
futures-util = { version = "0.3", default-features = false, features = ["alloc"] }
http = "1.0.0"
http-body = "1.0.0"
http-body-util = "0.1.0"
hyper = { version = "1.1.0", optional = true }
hyper-util = { version = "0.1.3", features = ["tokio", "server", "service"], optional = true }
itoa = "1.0.5"
matchit = "=0.8.4"
memchr = "2.4.1"
mime = "0.3.16"
multer = { version = "3.0.0", optional = true }
percent-encoding = "2.1"
pin-project-lite = "0.2.7"
serde_core = "1.0.221"
sync_wrapper = "1.0.0"
tower = { version = "0.5.2", default-features = false, features = ["util"] }
tower-layer = "0.3.2"
tower-service = "0.3"

# optional dependencies
axum-macros = { path = "../axum-macros", version = "0.5.0", optional = true }
base64 = { version = "0.22.1", optional = true }
form_urlencoded = { version = "1.1.0", optional = true }
futures-sink = { version = "0.3", optional = true }
hyper = { version = "1.1.0", optional = true }
hyper-util = { version = "0.1.3", features = ["tokio", "server", "service"], optional = true }
multer = { version = "3.0.0", optional = true }
reqwest = { version = "0.12", optional = true, default-features = false, features = ["json", "stream", "multipart"] }

# doc dependencies
serde = { version = "1.0.211", optional = true }
serde_core = "1.0.221"
serde_json = { version = "1.0", features = ["raw_value"], optional = true }
serde_path_to_error = { version = "0.1.8", optional = true }
serde_urlencoded = { version = "0.7", optional = true }
sha1 = { version = "0.10", optional = true }
sync_wrapper = "1.0.0"
tokio = { package = "tokio", version = "1.44", features = ["time"], optional = true }
tokio-rustls = { version = "0.26", optional = true, default-features = false }
tokio-tungstenite = { version = "0.28.0", optional = true }
tower = { version = "0.5.2", default-features = false, features = ["util"] }
tower-layer = "0.3.2"
tower-service = "0.3"
tracing = { version = "0.1", default-features = false, optional = true }

# doc dependencies
serde = { version = "1.0.211", optional = true }

[dependencies.tower-http]
version = "0.6.0"
optional = true
Expand Down
104 changes: 104 additions & 0 deletions axum/src/serve/listener.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,110 @@ fn is_connection_error(e: &io::Error) -> bool {
)
}

/// A TLS Listener is a simple wrapper to allow axum accept TLS connections natively.
///
/// # Examples
///
/// ```rust,no_run
/// # use tokio::net::TcpListener;
/// # use tokio_rustls::rustls::pki_types::pem::PemObject;
/// # use axum::serve::TlsListener;
/// # async {
/// let cert = tokio_rustls::rustls::pki_types::CertificateDer::from_slice(&[0]);
/// let key = tokio_rustls::rustls::pki_types::PrivateKeyDer::from_pem_slice(&[0]).unwrap();
/// let config = tokio_rustls::rustls::ServerConfig::builder()
/// .with_no_client_auth()
/// .with_single_cert(vec![cert], key).unwrap();
///
/// let tcp = TcpListener::bind(("0.0.0.0", 8443)).await.unwrap();
/// let tls_listener = TlsListener::new(tcp, config);
/// let app = axum::Router::new().route("/", axum::routing::get(|| async { "Hello" }));
///
/// let _ = axum::serve(tls_listener, app.into_make_service());
/// # };
/// # ()
/// ````
#[cfg(feature = "tokio-rustls")]
#[cfg_attr(docsrs, doc(cfg(feature = "tokio-rustls")))]
#[allow(missing_debug_implementations)]
pub struct TlsListener {
_inner: TcpListener,
acceptor: tokio_rustls::TlsAcceptor,
}

#[cfg(feature = "tokio-rustls")]
#[cfg_attr(docsrs, doc(cfg(feature = "tokio-rustls")))]
impl TlsListener {
/// Construct a new `TlsListener` from given [`TcpListener`] with provided
/// [`ServerConfig`].
///
/// `TlsListener` wraps underlying `TcpListener` which enables axum server to listen HTTPS
/// connections natively with `rustls`.
///
/// # Examples
///
/// ```rust
/// # use tokio::net::TcpListener;
/// # use tokio_rustls::rustls::pki_types::pem::PemObject;
/// # use axum::serve::TlsListener;
/// # async {
/// let cert = tokio_rustls::rustls::pki_types::CertificateDer::from_slice(&[0]);
/// let key = tokio_rustls::rustls::pki_types::PrivateKeyDer::from_pem_slice(&[0]).unwrap();
/// let config = tokio_rustls::rustls::ServerConfig::builder()
/// .with_no_client_auth()
/// .with_single_cert(vec![cert], key).unwrap();
///
/// let tcp = TcpListener::bind(("0.0.0.0", 8443)).await.unwrap();
/// let tls_listener = TlsListener::new(tcp, config);
/// # };
/// # ()
/// ````
///
/// [`ServerConfig`]: https://docs.rs/rustls/0.23.31/rustls/server/struct.ServerConfig.html
/// [`TcpListener`]: https://docs.rs/tokio/latest/tokio/net/struct.TcpListener.html
pub fn new(listener: TcpListener, server_config: tokio_rustls::rustls::ServerConfig) -> Self {
Self {
_inner: listener,
acceptor: tokio_rustls::TlsAcceptor::from(Arc::new(server_config)),
}
}

async fn accept(
&self,
) -> io::Result<(
tokio_rustls::server::TlsStream<TcpStream>,
std::net::SocketAddr,
)> {
let (tcp, addr) = self._inner.accept().await?;

// with valid tcp, try to acquire tls connection
match self.acceptor.accept(tcp).await {
Ok(tls) => Ok((tls, addr)),
Err(error) => Err(error),
}
}
}

#[cfg(feature = "tokio-rustls")]
#[cfg_attr(docsrs, doc(cfg(feature = "tokio-rustls")))]
impl Listener for TlsListener {
type Io = tokio_rustls::server::TlsStream<TcpStream>;
type Addr = std::net::SocketAddr;

async fn accept(&mut self) -> (Self::Io, Self::Addr) {
loop {
match Self::accept(self).await {
Ok(tup) => return tup,
Err(e) => handle_accept_error(e).await,
}
}
}

fn local_addr(&self) -> io::Result<Self::Addr> {
self._inner.local_addr()
}
}

#[cfg(test)]
mod tests {
use std::sync::atomic::{AtomicUsize, Ordering};
Expand Down
2 changes: 2 additions & 0 deletions axum/src/serve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ use tower_service::Service;

mod listener;

#[cfg(feature = "tokio-rustls")]
pub use self::listener::TlsListener;
pub use self::listener::{ConnLimiter, ConnLimiterIo, Listener, ListenerExt, TapIo};

/// Serve the service with the supplied listener.
Expand Down
1 change: 1 addition & 0 deletions deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ allow = [
"BSD-3-Clause",
"MIT",
"Unicode-3.0",
"ISC"
]

[bans]
Expand Down
13 changes: 13 additions & 0 deletions examples/tokio-rustls/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "tokio-rustls"
version = "0.1.0"
edition = "2021"
publish = false

[dependencies]
axum = { path = "../../axum", features = ["tokio-rustls"] }
tokio = { version = "1", features = ["full"] }
tokio-rustls = "0.26"
tower-service = "0.3.2"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
10 changes: 10 additions & 0 deletions examples/tokio-rustls/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# tokio-rustls

This example demonstrates axum with `tokio-rustls` natively which can be enabled with `tokio-rustls` feature flag.

```bash
cargo run -p tokio-rustls
```


Browse to `https://localhost:8433/` or try with curl `curl --insecure https://localhost:8443/`.
22 changes: 22 additions & 0 deletions examples/tokio-rustls/self_signed_certs/cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDkzCCAnugAwIBAgIUXVYkRCrM/ge03DVymDtXCuybp7gwDQYJKoZIhvcNAQEL
BQAwWTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4X
DTIxMDczMTE0MjIxMloXDTIyMDczMTE0MjIxMlowWTELMAkGA1UEBhMCVVMxEzAR
BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5
IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEA02V5ZjmqLB/VQwTarrz/35qsa83L+DbAoa0001+jVmmC+G9Nufi0
daroFWj/Uicv2fZWETU8JoZKUrX4BK9og5cg5rln/CtBRWCUYIwRgY9R/CdBGPn4
kp+XkSJaCw74ZIyLy/Zfux6h8ES1m9YRnBza+s7U+ImRBRf4MRPtXQ3/mqJxAZYq
dOnKnvssRyD2qutgVTAxwMUvJWIivRhRYDj7WOpS4CEEeQxP1iH1/T5P7FdtTGdT
bVBABCA8JhL96uFGPpOYHcM/7R5EIA3yZ5FNg931QzoDITjtXGtQ6y9/l/IYkWm6
J67RWcN0IoTsZhz0WNU4gAeslVtJLofn8QIDAQABo1MwUTAdBgNVHQ4EFgQUzFnK
NfS4LAYuKeWwHbzooER0yZ0wHwYDVR0jBBgwFoAUzFnKNfS4LAYuKeWwHbzooER0
yZ0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAk4O+e9jia59W
ZwetN4GU7OWcYhmOgSizRSs6u7mTfp62LDMt96WKU3THksOnZ44HnqWQxsSfdFVU
XJD12tjvVU8Z4FWzQajcHeemUYiDze8EAh6TnxnUcOrU8IcwiKGxCWRY/908jnWg
+MMscfMCMYTRdeTPqD8fGzAlUCtmyzH6KLE3s4Oo/r5+NR+Uvrwpdvb7xe0MwwO9
Q/zR4N8ep/HwHVEObcaBofE1ssZLksX7ZgCP9wMgXRWpNAtC5EWxMbxYjBfWFH24
fDJlBMiGJWg8HHcxK7wQhFh+fuyNzE+xEWPsI9VL1zDftd9x8/QsOagyEOnY8Vxr
AopvZ09uEQ==
-----END CERTIFICATE-----
28 changes: 28 additions & 0 deletions examples/tokio-rustls/self_signed_certs/key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDTZXlmOaosH9VD
BNquvP/fmqxrzcv4NsChrTTTX6NWaYL4b025+LR1qugVaP9SJy/Z9lYRNTwmhkpS
tfgEr2iDlyDmuWf8K0FFYJRgjBGBj1H8J0EY+fiSn5eRIloLDvhkjIvL9l+7HqHw
RLWb1hGcHNr6ztT4iZEFF/gxE+1dDf+aonEBlip06cqe+yxHIPaq62BVMDHAxS8l
YiK9GFFgOPtY6lLgIQR5DE/WIfX9Pk/sV21MZ1NtUEAEIDwmEv3q4UY+k5gdwz/t
HkQgDfJnkU2D3fVDOgMhOO1ca1DrL3+X8hiRabonrtFZw3QihOxmHPRY1TiAB6yV
W0kuh+fxAgMBAAECggEADltu8k1qTFLhJgsXWxTFAAe+PBgfCT2WuaRM2So+qqjB
12Of0MieYPt5hbK63HaC3nfHgqWt7yPhulpXfOH45C8IcgMXl93MMg0MJr58leMI
+2ojFrIrerHSFm5R1TxwDEwrVm/mMowzDWFtQCc6zPJ8wNn5RuP48HKfTZ3/2fjw
zEjSwPO2wFMfo1EJNTjlI303lFbdFBs67NaX6puh30M7Tn+gznHKyO5a7F57wkIt
fkgnEy/sgMedQlwX7bRpUoD6f0fZzV8Qz4cHFywtYErczZJh3VGitJoO/VCIDdty
RPXOAqVDd7EpP1UUehZlKVWZ0OZMEfRgKbRCel5abQKBgQDwgwrIQ5+BiZv6a0VT
ETeXB+hRbvBinRykNo/RvLc3j1enRh9/zO/ShadZIXgOAiM1Jnr5Gp8KkNGca6K1
myhtad7xYPODYzNXXp6T1OPgZxHZLIYzVUj6ypXeV64Te5ZiDaJ1D49czsq+PqsQ
XRcgBJSNpFtDFiXWpjXWfx8PxwKBgQDhAnLY5Sl2eeQo+ud0MvjwftB/mN2qCzJY
5AlQpRI4ThWxJgGPuHTR29zVa5iWNYuA5LWrC1y/wx+t5HKUwq+5kxvs+npYpDJD
ZX/w0Glc6s0Jc/mFySkbw9B2LePedL7lRF5OiAyC6D106Sc9V2jlL4IflmOzt4CD
ZTNbLtC6hwKBgHfIzBXxl/9sCcMuqdg1Ovp9dbcZCaATn7ApfHd5BccmHQGyav27
k7XF2xMJGEHhzqcqAxUNrSgV+E9vTBomrHvRvrd5Ec7eGTPqbBA0d0nMC5eeFTh7
wV0miH20LX6Gjt9G6yJiHYSbeV5G1+vOcTYBEft5X/qJjU7aePXbWh0BAoGBAJlV
5tgCCuhvFloK6fHYzqZtdT6O+PfpW20SMXrgkvMF22h2YvgDFrDwqKRUB47NfHzg
3yBpxNH1ccA5/w97QO8w3gX3h6qicpJVOAPusu6cIBACFZfjRv1hyszOZwvw+Soa
Fj5kHkqTY1YpkREPYS9V2dIW1Wjic1SXgZDw7VM/AoGAP/cZ3ZHTSCDTFlItqy5C
rIy2AiY0WJsx+K0qcvtosPOOwtnGjWHb1gdaVdfX/IRkSsX4PAOdnsyidNC5/l/m
y8oa+5WEeGFclWFhr4dnTA766o8HrM2UjIgWWYBF2VKdptGnHxFeJWFUmeQC/xeW
w37pCS7ykL+7gp7V0WShYsw=
-----END PRIVATE KEY-----
Loading
Loading