Skip to content

Commit

Permalink
Allow TLS without HTTP2 and fallback to HTTP 1.1 (#858)
Browse files Browse the repository at this point in the history
* Allow Tls without HTTP2 and fallback to HTTP 1.1

* wip

* wip

* fix ci
  • Loading branch information
chrislearn authored Aug 10, 2024
1 parent 88d53b8 commit bdf8bd3
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 36 deletions.
6 changes: 3 additions & 3 deletions crates/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ http1 = []
http2 = ["hyper/http2"]
http2-cleartext = ["http2"]
quinn = ["dep:salvo-http3", "dep:quinn", "rustls"]
rustls = ["http1", "http2", "dep:tokio-rustls", "dep:rustls-pemfile", "ring"]
native-tls = ["http1", "http2", "dep:tokio-native-tls", "dep:native-tls"]
openssl = ["http2", "dep:openssl", "dep:tokio-openssl"]
rustls = ["dep:tokio-rustls", "dep:rustls-pemfile", "ring"]
native-tls = ["dep:tokio-native-tls", "dep:native-tls"]
openssl = ["dep:openssl", "dep:tokio-openssl"]
unix = ["http1"]
test = ["dep:brotli", "dep:flate2", "dep:zstd", "dep:encoding_rs", "dep:serde_urlencoded", "dep:url", "tokio/macros"]
acme = ["http1", "http2", "hyper-util/http1", "hyper-util/http2", "hyper-util/client-legacy", "dep:hyper-rustls", "dep:rcgen", "dep:ring", "ring", "dep:x509-parser", "dep:tokio-rustls", "dep:rustls-pemfile"]
Expand Down
11 changes: 6 additions & 5 deletions crates/core/src/conn/native_tls/listener.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use tokio_native_tls::TlsStream;

use crate::conn::{Accepted, Acceptor, HandshakeStream, Holding, IntoConfigStream, Listener};
use crate::fuse::ArcFuseFactory;
use crate::http::{HttpConnection, Version};
use crate::http::{HttpConnection,};

use super::Identity;

Expand Down Expand Up @@ -77,14 +77,15 @@ where
.holdings()
.iter()
.map(|h| {
#[allow(unused_mut)]
let mut versions = h.http_versions.clone();
#[cfg(feature = "http1")]
if !versions.contains(&Version::HTTP_11) {
versions.push(Version::HTTP_11);
if !versions.contains(&crate::http::Version::HTTP_11) {
versions.push(crate::http::Version::HTTP_11);
}
#[cfg(feature = "http2")]
if !versions.contains(&Version::HTTP_2) {
versions.push(Version::HTTP_2);
if !versions.contains(&crate::http::Version::HTTP_2) {
versions.push(crate::http::Version::HTTP_2);
}
Holding {
local_addr: h.local_addr.clone(),
Expand Down
31 changes: 27 additions & 4 deletions crates/core/src/conn/openssl/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,28 @@ impl Keycert {
}
}

fn alpn_protocols() -> Vec<u8> {
#[allow(unused_mut)]
let mut alpn_protocols: Vec<Vec<u8>> = Vec::with_capacity(3);
#[cfg(feature = "quinn")]
alpn_protocols.push(b"\x02h3".to_vec());
#[cfg(feature = "http2")]
alpn_protocols.push(b"\x02h2".to_vec());
#[cfg(feature = "http1")]
alpn_protocols.push(b"\x08http/1.1".to_vec());
alpn_protocols.into_iter().flatten().collect()
}

type BuilderModifier = Box<dyn FnMut(&mut SslAcceptorBuilder) + Send + 'static>;
/// Builder to set the configuration for the Tls server.
#[non_exhaustive]
pub struct OpensslConfig {
keycert: Keycert,
/// Key and certificate.
pub keycert: Keycert,
/// Builder modifier.
pub builder_modifier: Option<BuilderModifier>,
/// Protocols through ALPN (Application-Layer Protocol Negotiation).
pub alpn_protocols: Vec<u8>,
}

impl fmt::Debug for OpensslConfig {
Expand All @@ -111,6 +126,7 @@ impl OpensslConfig {
OpensslConfig {
keycert,
builder_modifier: None,
alpn_protocols: alpn_protocols(),
}
}

Expand All @@ -123,6 +139,13 @@ impl OpensslConfig {
self
}

/// Set specific protocols through ALPN (Application-Layer Protocol Negotiation).
#[inline]
pub fn alpn_protocols(mut self, alpn_protocols: impl Into<Vec<u8>>) -> Self {
self.alpn_protocols = alpn_protocols.into();
self
}

/// Create [`SslAcceptorBuilder`]
pub fn create_acceptor_builder(&mut self) -> IoResult<SslAcceptorBuilder> {
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls())?;
Expand All @@ -139,11 +162,11 @@ impl OpensslConfig {
builder.set_private_key(PKey::private_key_from_pem(self.keycert.key()?)?.as_ref())?;

// set ALPN protocols
static PROTOS: &[u8] = b"\x02h2\x08http/1.1";
builder.set_alpn_protos(PROTOS)?;
let alpn_protocols = self.alpn_protocols.clone();
builder.set_alpn_protos(&alpn_protocols)?;
// set uo ALPN selection routine - as select_next_proto
builder.set_alpn_select_callback(move |_: &mut SslRef, list: &[u8]| {
openssl::ssl::select_next_proto(PROTOS, list).ok_or(openssl::ssl::AlpnError::NOACK)
openssl::ssl::select_next_proto(&alpn_protocols, list).ok_or(openssl::ssl::AlpnError::NOACK)
});
if let Some(modifier) = &mut self.builder_modifier {
modifier(&mut builder);
Expand Down
13 changes: 7 additions & 6 deletions crates/core/src/conn/openssl/listener.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use super::SslAcceptorBuilder;

use crate::conn::{Accepted, Acceptor, HandshakeStream, Holding, IntoConfigStream, Listener};
use crate::fuse::ArcFuseFactory;
use crate::http::{HttpConnection, Version};
use crate::http::{HttpConnection,};

/// OpensslListener
pub struct OpensslListener<S, C, T, E> {
Expand Down Expand Up @@ -82,14 +82,15 @@ where
.holdings()
.iter()
.map(|h| {
#[allow(unused_mut)]
let mut versions = h.http_versions.clone();
#[cfg(feature = "http1")]
if !versions.contains(&Version::HTTP_11) {
versions.push(Version::HTTP_11);
if !versions.contains(&crate::http::Version::HTTP_11) {
versions.push(crate::http::Version::HTTP_11);
}
#[cfg(feature = "http2")]
if !versions.contains(&Version::HTTP_2) {
versions.push(Version::HTTP_2);
if !versions.contains(&crate::http::Version::HTTP_2) {
versions.push(crate::http::Version::HTTP_2);
}
Holding {
local_addr: h.local_addr.clone(),
Expand Down Expand Up @@ -153,7 +154,7 @@ where
}
let tls_acceptor = match &self.tls_acceptor {
Some(tls_acceptor) => tls_acceptor.clone(),
None => return Err(IoError::new(ErrorKind::Other, "openssl: invalid tls config.")),
None => return Err(IoError::new(ErrorKind::Other, "openssl: tls_acceptor is none.")),
};

let Accepted {
Expand Down
38 changes: 25 additions & 13 deletions crates/core/src/conn/rustls/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ impl Keycert {

/// Tls client authentication configuration.
#[derive(Clone, Debug)]
pub(crate) enum TlsClientAuth {
pub enum TlsClientAuth {
/// No client auth.
Off,
/// Allow any anonymous or authenticated client.
Expand All @@ -138,15 +138,32 @@ pub(crate) enum TlsClientAuth {
Required(Vec<u8>),
}

fn alpn_protocols() -> Vec<Vec<u8>> {
#[allow(unused_mut)]
let mut alpn_protocols = Vec::with_capacity(3);
#[cfg(feature = "quinn")]
alpn_protocols.push(b"h3".to_vec());
#[cfg(feature = "http2")]
alpn_protocols.push(b"h2".to_vec());
#[cfg(feature = "http1")]
alpn_protocols.push(b"http/1.1".to_vec());
alpn_protocols
}

/// Builder to set the configuration for the Tls server.
#[derive(Clone, Debug)]
pub struct RustlsConfig {
fallback: Option<Keycert>,
keycerts: HashMap<String, Keycert>,
client_auth: TlsClientAuth,
alpn_protocols: Vec<Vec<u8>>,
/// Fallback keycert.
pub fallback: Option<Keycert>,
/// Keycerts.
pub keycerts: HashMap<String, Keycert>,
/// Client auth.
pub client_auth: TlsClientAuth,
/// Protocols through ALPN (Application-Layer Protocol Negotiation).
pub alpn_protocols: Vec<Vec<u8>>,
}


impl RustlsConfig {
/// Create new `RustlsConfig`
#[inline]
Expand All @@ -155,7 +172,7 @@ impl RustlsConfig {
fallback: fallback.into(),
keycerts: HashMap::new(),
client_auth: TlsClientAuth::Off,
alpn_protocols: vec![b"h2".to_vec(), b"http/1.1".to_vec()],
alpn_protocols: alpn_protocols(),
}
}

Expand Down Expand Up @@ -209,7 +226,7 @@ impl RustlsConfig {
self
}

/// Add a new keycert to be used for the given SNI `name`.
/// Set specific protocols through ALPN (Application-Layer Protocol Negotiation).
#[inline]
pub fn alpn_protocols(mut self, alpn_protocols: impl Into<Vec<Vec<u8>>>) -> Self {
self.alpn_protocols = alpn_protocols.into();
Expand Down Expand Up @@ -251,12 +268,7 @@ impl RustlsConfig {
certified_keys,
fallback,
}));
let mut alpn_protocols = self.alpn_protocols;
let h3_alpn = b"h3".to_vec();
if !alpn_protocols.contains(&h3_alpn) {
alpn_protocols.push(h3_alpn);
}
config.alpn_protocols = alpn_protocols;
config.alpn_protocols = self.alpn_protocols;
Ok(config)
}

Expand Down
11 changes: 6 additions & 5 deletions crates/core/src/conn/rustls/listener.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use tokio_rustls::server::TlsStream;
use crate::conn::{Accepted, Acceptor, HandshakeStream, Holding, IntoConfigStream, Listener};
use crate::fuse::ArcFuseFactory;
use crate::http::uri::Scheme;
use crate::http::{HttpConnection, Version};
use crate::http::{HttpConnection};

use super::ServerConfig;

Expand Down Expand Up @@ -82,14 +82,15 @@ where
.holdings()
.iter()
.map(|h| {
#[allow(unused_mut)]
let mut versions = h.http_versions.clone();
#[cfg(feature = "http1")]
if !versions.contains(&Version::HTTP_11) {
versions.push(Version::HTTP_11);
if !versions.contains(&crate::http::Version::HTTP_11) {
versions.push(crate::http::Version::HTTP_11);
}
#[cfg(feature = "http2")]
if !versions.contains(&Version::HTTP_2) {
versions.push(Version::HTTP_2);
if !versions.contains(&crate::http::Version::HTTP_2) {
versions.push(crate::http::Version::HTTP_2);
}
Holding {
local_addr: h.local_addr.clone(),
Expand Down
1 change: 1 addition & 0 deletions crates/oapi-macros/src/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ impl ToTokens for Required {
}
}

#[allow(dead_code)]
#[derive(Default, Debug)]
pub(crate) struct ExternalDocs {
url: String,
Expand Down

0 comments on commit bdf8bd3

Please sign in to comment.