Skip to content

Commit d9e3030

Browse files
authored
Prioritize H2 via ALPN. (#30)
1 parent 3eca98b commit d9e3030

File tree

3 files changed

+98
-11
lines changed

3 files changed

+98
-11
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ hex = "0.4.3"
4141
nom = "8.0.0"
4242

4343
tokio-rustls = { version = "0.26", default-features = false }
44+
rustls-pemfile = "2.0"
4445
futures-util = { version = "0.3.31", default-features = false }
4546
httlib-hpack = "0.1.3"
4647
pin-project-lite = "0.2.9"

src/server/certificate.rs

Lines changed: 86 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,94 @@
1+
use std::{io, path::Path, sync::Arc};
2+
3+
use axum_server::tls_rustls::RustlsConfig;
14
use rcgen::{
25
date_time_ymd, BasicConstraints, CertificateParams, DistinguishedName, DnType, IsCa, KeyPair,
36
KeyUsagePurpose, SanType,
47
};
8+
use rustls_pemfile::Item;
9+
use tokio_rustls::rustls::{
10+
pki_types::{CertificateDer, PrivateKeyDer},
11+
ServerConfig,
12+
};
13+
14+
// Load TLS configuration from self-signed PEM files
15+
pub async fn config_self_signed() -> crate::Result<RustlsConfig> {
16+
let (cert, key) = get_self_signed_cert()?;
17+
let cert = rustls_pemfile::certs(&mut cert.as_ref())
18+
.map(|it| it.map(|it| it.to_vec()))
19+
.collect::<Result<Vec<_>, _>>()?;
20+
21+
// Check the entire PEM file for the key in case it is not first section
22+
let mut key_vec: Vec<Vec<u8>> = rustls_pemfile::read_all(&mut key.as_ref())
23+
.filter_map(|i| match i.ok()? {
24+
Item::Sec1Key(key) => Some(key.secret_sec1_der().to_vec()),
25+
Item::Pkcs1Key(key) => Some(key.secret_pkcs1_der().to_vec()),
26+
Item::Pkcs8Key(key) => Some(key.secret_pkcs8_der().to_vec()),
27+
_ => None,
28+
})
29+
.collect();
30+
31+
// Make sure file contains only one key
32+
if key_vec.len() != 1 {
33+
return Err(io::Error::other("private key format not supported").into());
34+
}
35+
36+
let cert = cert.into_iter().map(CertificateDer::from).collect();
37+
let key = PrivateKeyDer::try_from(
38+
key_vec
39+
.pop()
40+
.expect("private key should be present in the file"),
41+
)
42+
.map_err(io::Error::other)?;
43+
44+
Ok(config_from_der(cert, key)?)
45+
}
46+
47+
/// Load TLS configuration from PEM files
48+
pub async fn config_from_pem_chain_file(
49+
cert: impl AsRef<Path>,
50+
chain: impl AsRef<Path>,
51+
) -> crate::Result<RustlsConfig> {
52+
let cert = tokio::fs::read(cert.as_ref()).await?;
53+
let cert = rustls_pemfile::certs(&mut cert.as_ref())
54+
.map(|it| it.map(|it| CertificateDer::from(it.to_vec())))
55+
.collect::<Result<Vec<_>, _>>()?;
56+
57+
let key = tokio::fs::read(chain.as_ref()).await?;
58+
let key_cert: PrivateKeyDer = match rustls_pemfile::read_one(&mut key.as_ref())?
59+
.ok_or_else(|| io::Error::other("could not parse pem file"))?
60+
{
61+
Item::Pkcs8Key(key) => Ok(key.into()),
62+
Item::Sec1Key(key) => Ok(key.into()),
63+
Item::Pkcs1Key(key) => Ok(key.into()),
64+
x => Err(io::Error::other(format!(
65+
"invalid certificate format, received: {x:?}"
66+
))),
67+
}?;
68+
69+
Ok(config_from_der(cert, key_cert)?)
70+
}
71+
72+
fn config_from_der(
73+
cert_chain: Vec<CertificateDer<'static>>,
74+
key_der: PrivateKeyDer<'static>,
75+
) -> io::Result<RustlsConfig> {
76+
let mut config = ServerConfig::builder()
77+
.with_no_client_auth()
78+
.with_single_cert(cert_chain, key_der)
79+
.map_err(io::Error::other)?;
80+
81+
config.alpn_protocols = vec![
82+
b"h2".to_vec(),
83+
b"http/1.1".to_vec(),
84+
b"http/1.0".to_vec(),
85+
b"http/0.9".to_vec(),
86+
];
87+
88+
Ok(RustlsConfig::from_config(Arc::new(config)))
89+
}
590

6-
/// Get self-signed certificate and key.
7-
pub fn get_self_signed_cert() -> crate::Result<(Vec<u8>, Vec<u8>)> {
91+
fn get_self_signed_cert() -> crate::Result<(Vec<u8>, Vec<u8>)> {
892
let temp_dir = std::env::temp_dir().join(env!("CARGO_PKG_NAME"));
993
if !temp_dir.exists() {
1094
tracing::info!("Creating temp cert directory: {}", temp_dir.display());
@@ -25,7 +109,6 @@ pub fn get_self_signed_cert() -> crate::Result<(Vec<u8>, Vec<u8>)> {
25109
Ok((cert, key))
26110
}
27111

28-
/// Generate self-signed certificate and key.
29112
fn generate_self_signed() -> crate::Result<(Vec<u8>, Vec<u8>)> {
30113
let mut params = CertificateParams::default();
31114
params.not_before = date_time_ymd(1975, 1, 1);

src/server/mod.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use axum::{
1313
Extension, Router,
1414
};
1515
use axum_extra::response::ErasedJson;
16-
use axum_server::{tls_rustls::RustlsConfig, Handle};
16+
use axum_server::Handle;
1717
use tower::limit::ConcurrencyLimitLayer;
1818
use tower_http::{
1919
cors::{AllowHeaders, AllowMethods, AllowOrigin, CorsLayer},
@@ -74,17 +74,20 @@ pub async fn run(args: Args) -> Result<()> {
7474
// Spawn a task to gracefully shutdown server.
7575
tokio::spawn(signal::graceful_shutdown(handle.clone()));
7676

77-
// Load TLS configuration
78-
let tls_config = match (args.tls_cert.as_ref(), args.tls_key.as_ref()) {
79-
(Some(cert), Some(key)) => RustlsConfig::from_pem_chain_file(cert, key).await,
77+
// Load TLS configuration with HTTP/2 ALPN preference
78+
let config = match (args.tls_cert.as_ref(), args.tls_key.as_ref()) {
79+
(Some(cert_path), Some(key_path)) => {
80+
// Load TLS configuration from PEM files
81+
certificate::config_from_pem_chain_file(cert_path, key_path).await?
82+
}
8083
_ => {
81-
let (cert, key) = certificate::get_self_signed_cert()?;
82-
RustlsConfig::from_pem(cert, key).await
84+
// Generate self-signed certificate configuration
85+
certificate::config_self_signed().await?
8386
}
84-
}?;
87+
};
8588

8689
// Use TLS configuration to create a secure server
87-
let mut server = axum_server::bind_rustls(args.bind, tls_config);
90+
let mut server = axum_server::bind_rustls(args.bind, config);
8891
server
8992
.http_builder()
9093
.http2()

0 commit comments

Comments
 (0)