Expose the SNI when possible. (#2943)

Grabs the SNI from TLS connections and saves to ConenctionMeta struct. Provides useful methods for exracting the SNI from requests.

Closes #2942
This commit is contained in:
Dan Dumont 2025-12-27 23:35:44 -05:00 committed by GitHub
parent 3ccfd8bd77
commit 3d1de7bac5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 42 additions and 3 deletions

8
.editorconfig Normal file
View File

@ -0,0 +1,8 @@
root = true
[*.rs]
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 4
tab_width = 4

View File

@ -22,6 +22,8 @@ pub trait Connection: AsyncRead + AsyncWrite + Send + Unpin {
/// Defaults to an empty vector to indicate that no certificates were
/// presented.
fn certificates(&self) -> Option<Certificates<'_>> { None }
fn server_name(&self) -> Option<&str> { None }
}
impl<A: Connection, B: Connection> Connection for Either<A, B> {

View File

@ -45,13 +45,19 @@ pub(crate) struct ConnectionMeta {
pub peer_endpoint: Option<Endpoint>,
#[cfg_attr(not(feature = "mtls"), allow(dead_code))]
pub peer_certs: Option<Arc<Certificates<'static>>>,
#[cfg_attr(feature = "tls", allow(dead_code))]
pub server_name: Option<String>,
}
impl ConnectionMeta {
pub fn new(endpoint: io::Result<Endpoint>, certs: Option<Certificates<'_>>) -> Self {
pub fn new(
endpoint: io::Result<Endpoint>,
certs: Option<Certificates<'_>>,
server_name: Option<&str>) -> Self {
ConnectionMeta {
peer_endpoint: endpoint.ok(),
peer_certs: certs.map(|c| c.into_owned()).map(Arc::new),
server_name: server_name.map(|s| s.to_string()),
}
}
}
@ -295,6 +301,16 @@ impl<'r> Request<'r> {
self.state.host.as_ref()
}
/// Returns the resolved SNI server name requested in the TLS handshake, if
/// any.
///
/// Ideally, this will match the `Host` header in the request.
#[cfg(feature = "tls")]
#[inline(always)]
pub fn sni(&mut self) -> Option<&str> {
self.connection.server_name.as_deref()
}
/// Sets the host of `self` to `host`.
///
/// # Example

View File

@ -172,7 +172,11 @@ impl Rocket<Orbit> {
let (listener, rocket, server) = (listener.clone(), self.clone(), server.clone());
spawn_inspect(|e| log_server_error(&**e), async move {
let conn = listener.connect(accept).race_io(rocket.shutdown()).await?;
let meta = ConnectionMeta::new(conn.endpoint(), conn.certificates());
let meta = ConnectionMeta::new(
conn.endpoint(),
conn.certificates(),
conn.server_name()
);
let service = service_fn(|mut req| {
let upgrade = hyper::upgrade::on(&mut req);
let (parts, incoming) = req.into_parts();
@ -205,7 +209,7 @@ impl Rocket<Orbit> {
while let Some(mut conn) = stream.accept().race_io(rocket.shutdown()).await? {
let rocket = rocket.clone();
spawn_inspect(|e: &io::Error| log_server_error(e), async move {
let meta = ConnectionMeta::new(conn.endpoint(), None);
let meta = ConnectionMeta::new(conn.endpoint(), None, None);
let rx = conn.rx.cancellable(rocket.shutdown.clone());
let response = rocket.clone()
.service(conn.parts, rx, None, meta)

View File

@ -96,4 +96,13 @@ impl<C: Connection> Connection for TlsStream<C> {
#[cfg(not(feature = "mtls"))]
None
}
fn server_name(&self) -> Option<&str> {
#[cfg(feature = "tls")] {
self.get_ref().1.server_name()
}
#[cfg(not(feature = "tls"))]
None
}
}