Skip to content

Commit 591d9c9

Browse files
committed
PoC of introducing SpoofableValue
PoC to check which solution to pick for #2998
1 parent ffeb4f9 commit 591d9c9

File tree

5 files changed

+32
-14
lines changed

5 files changed

+32
-14
lines changed

axum-extra/src/extract/host.rs

+10-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use super::rejection::{FailedToResolveHost, HostRejection};
1+
use super::{
2+
rejection::{FailedToResolveHost, HostRejection},
3+
SpoofableValue,
4+
};
25
use axum::extract::FromRequestParts;
36
use http::{
47
header::{HeaderMap, FORWARDED},
@@ -18,7 +21,7 @@ const X_FORWARDED_HOST_HEADER_KEY: &str = "X-Forwarded-Host";
1821
/// Note that user agents can set `X-Forwarded-Host` and `Host` headers to arbitrary values so make
1922
/// sure to validate them to avoid security issues.
2023
#[derive(Debug, Clone)]
21-
pub struct Host(pub String);
24+
pub struct Host(pub SpoofableValue);
2225

2326
impl<S> FromRequestParts<S> for Host
2427
where
@@ -28,27 +31,27 @@ where
2831

2932
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
3033
if let Some(host) = parse_forwarded(&parts.headers) {
31-
return Ok(Host(host.to_owned()));
34+
return Ok(Host(SpoofableValue::new(host.to_owned())));
3235
}
3336

3437
if let Some(host) = parts
3538
.headers
3639
.get(X_FORWARDED_HOST_HEADER_KEY)
3740
.and_then(|host| host.to_str().ok())
3841
{
39-
return Ok(Host(host.to_owned()));
42+
return Ok(Host(SpoofableValue::new(host.to_owned())));
4043
}
4144

4245
if let Some(host) = parts
4346
.headers
4447
.get(http::header::HOST)
4548
.and_then(|host| host.to_str().ok())
4649
{
47-
return Ok(Host(host.to_owned()));
50+
return Ok(Host(SpoofableValue::new(host.to_owned())));
4851
}
4952

5053
if let Some(host) = parts.uri.host() {
51-
return Ok(Host(host.to_owned()));
54+
return Ok(Host(SpoofableValue::new(host.to_owned())));
5255
}
5356

5457
Err(HostRejection::FailedToResolveHost(FailedToResolveHost))
@@ -81,7 +84,7 @@ mod tests {
8184

8285
fn test_client() -> TestClient {
8386
async fn host_as_body(Host(host): Host) -> String {
84-
host
87+
host.spoofable_value()
8588
}
8689

8790
TestClient::new(Router::new().route("/", get(host_as_body)))

axum-extra/src/extract/mod.rs

+13
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,16 @@ pub use crate::json_lines::JsonLines;
6363
#[cfg(feature = "typed-header")]
6464
#[doc(no_inline)]
6565
pub use crate::typed_header::TypedHeader;
66+
67+
#[derive(Debug, Clone)]
68+
pub struct SpoofableValue(String);
69+
70+
impl SpoofableValue {
71+
pub fn new(value: String) -> Self {
72+
Self(value)
73+
}
74+
75+
pub fn spoofable_value(self) -> String {
76+
self.0
77+
}
78+
}

axum-extra/src/extract/scheme.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ use http::{
99
header::{HeaderMap, FORWARDED},
1010
request::Parts,
1111
};
12+
13+
use super::SpoofableValue;
1214
const X_FORWARDED_PROTO_HEADER_KEY: &str = "X-Forwarded-Proto";
1315

1416
/// Extractor that resolves the scheme / protocol of a request.
@@ -21,7 +23,7 @@ const X_FORWARDED_PROTO_HEADER_KEY: &str = "X-Forwarded-Proto";
2123
/// Note that user agents can set the `X-Forwarded-Proto` header to arbitrary values so make
2224
/// sure to validate them to avoid security issues.
2325
#[derive(Debug, Clone)]
24-
pub struct Scheme(pub String);
26+
pub struct Scheme(pub SpoofableValue);
2527

2628
/// Rejection type used if the [`Scheme`] extractor is unable to
2729
/// resolve a scheme.
@@ -43,7 +45,7 @@ where
4345
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
4446
// Within Forwarded header
4547
if let Some(scheme) = parse_forwarded(&parts.headers) {
46-
return Ok(Scheme(scheme.to_owned()));
48+
return Ok(Scheme(SpoofableValue::new(scheme.to_owned())));
4749
}
4850

4951
// X-Forwarded-Proto
@@ -52,12 +54,12 @@ where
5254
.get(X_FORWARDED_PROTO_HEADER_KEY)
5355
.and_then(|scheme| scheme.to_str().ok())
5456
{
55-
return Ok(Scheme(scheme.to_owned()));
57+
return Ok(Scheme(SpoofableValue::new(scheme.to_owned())));
5658
}
5759

5860
// From parts of an HTTP/2 request
5961
if let Some(scheme) = parts.uri.scheme_str() {
60-
return Ok(Scheme(scheme.to_owned()));
62+
return Ok(Scheme(SpoofableValue::new(scheme.to_owned())));
6163
}
6264

6365
Err(SchemeMissing)
@@ -89,7 +91,7 @@ mod tests {
8991

9092
fn test_client() -> TestClient {
9193
async fn scheme_as_body(Scheme(scheme): Scheme) -> String {
92-
scheme
94+
scheme.spoofable_value()
9395
}
9496

9597
TestClient::new(Router::new().route("/", get(scheme_as_body)))

examples/tls-graceful-shutdown/src/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ where
122122
}
123123

124124
let redirect = move |Host(host): Host, uri: Uri| async move {
125-
match make_https(host, uri, ports) {
125+
match make_https(host.spoofable_value(), uri, ports) {
126126
Ok(uri) => Ok(Redirect::permanent(&uri.to_string())),
127127
Err(error) => {
128128
tracing::warn!(%error, "failed to convert URI to HTTPS");

examples/tls-rustls/src/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ async fn redirect_http_to_https(ports: Ports) {
8888
}
8989

9090
let redirect = move |Host(host): Host, uri: Uri| async move {
91-
match make_https(host, uri, ports) {
91+
match make_https(host.spoofable_value(), uri, ports) {
9292
Ok(uri) => Ok(Redirect::permanent(&uri.to_string())),
9393
Err(error) => {
9494
tracing::warn!(%error, "failed to convert URI to HTTPS");

0 commit comments

Comments
 (0)