mirror of
https://github.com/tokio-rs/axum.git
synced 2026-09-07 00:00:12 +02:00
Implement the OptionalFromRequestParts trait for the Host extractor (#3177)
This commit is contained in:
@@ -1,10 +1,14 @@
|
|||||||
use super::rejection::{FailedToResolveHost, HostRejection};
|
use super::rejection::{FailedToResolveHost, HostRejection};
|
||||||
use axum::extract::FromRequestParts;
|
use axum::{
|
||||||
|
extract::{FromRequestParts, OptionalFromRequestParts},
|
||||||
|
RequestPartsExt,
|
||||||
|
};
|
||||||
use http::{
|
use http::{
|
||||||
header::{HeaderMap, FORWARDED},
|
header::{HeaderMap, FORWARDED},
|
||||||
request::Parts,
|
request::Parts,
|
||||||
uri::Authority,
|
uri::Authority,
|
||||||
};
|
};
|
||||||
|
use std::convert::Infallible;
|
||||||
|
|
||||||
const X_FORWARDED_HOST_HEADER_KEY: &str = "X-Forwarded-Host";
|
const X_FORWARDED_HOST_HEADER_KEY: &str = "X-Forwarded-Host";
|
||||||
|
|
||||||
@@ -31,8 +35,27 @@ where
|
|||||||
type Rejection = HostRejection;
|
type Rejection = HostRejection;
|
||||||
|
|
||||||
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
|
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
|
||||||
|
parts
|
||||||
|
.extract::<Option<Host>>()
|
||||||
|
.await
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
|
.ok_or(HostRejection::FailedToResolveHost(FailedToResolveHost))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> OptionalFromRequestParts<S> for Host
|
||||||
|
where
|
||||||
|
S: Send + Sync,
|
||||||
|
{
|
||||||
|
type Rejection = Infallible;
|
||||||
|
|
||||||
|
async fn from_request_parts(
|
||||||
|
parts: &mut Parts,
|
||||||
|
_state: &S,
|
||||||
|
) -> Result<Option<Self>, Self::Rejection> {
|
||||||
if let Some(host) = parse_forwarded(&parts.headers) {
|
if let Some(host) = parse_forwarded(&parts.headers) {
|
||||||
return Ok(Host(host.to_owned()));
|
return Ok(Some(Host(host.to_owned())));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(host) = parts
|
if let Some(host) = parts
|
||||||
@@ -40,7 +63,7 @@ where
|
|||||||
.get(X_FORWARDED_HOST_HEADER_KEY)
|
.get(X_FORWARDED_HOST_HEADER_KEY)
|
||||||
.and_then(|host| host.to_str().ok())
|
.and_then(|host| host.to_str().ok())
|
||||||
{
|
{
|
||||||
return Ok(Host(host.to_owned()));
|
return Ok(Some(Host(host.to_owned())));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(host) = parts
|
if let Some(host) = parts
|
||||||
@@ -48,14 +71,14 @@ where
|
|||||||
.get(http::header::HOST)
|
.get(http::header::HOST)
|
||||||
.and_then(|host| host.to_str().ok())
|
.and_then(|host| host.to_str().ok())
|
||||||
{
|
{
|
||||||
return Ok(Host(host.to_owned()));
|
return Ok(Some(Host(host.to_owned())));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(authority) = parts.uri.authority() {
|
if let Some(authority) = parts.uri.authority() {
|
||||||
return Ok(Host(parse_authority(authority).to_owned()));
|
return Ok(Some(Host(parse_authority(authority).to_owned())));
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(HostRejection::FailedToResolveHost(FailedToResolveHost))
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,7 +171,7 @@ mod tests {
|
|||||||
async fn ip4_uri_host() {
|
async fn ip4_uri_host() {
|
||||||
let mut parts = Request::new(()).into_parts().0;
|
let mut parts = Request::new(()).into_parts().0;
|
||||||
parts.uri = "https://127.0.0.1:1234/image.jpg".parse().unwrap();
|
parts.uri = "https://127.0.0.1:1234/image.jpg".parse().unwrap();
|
||||||
let host = Host::from_request_parts(&mut parts, &()).await.unwrap();
|
let host = parts.extract::<Host>().await.unwrap();
|
||||||
assert_eq!(host.0, "127.0.0.1:1234");
|
assert_eq!(host.0, "127.0.0.1:1234");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,10 +179,32 @@ mod tests {
|
|||||||
async fn ip6_uri_host() {
|
async fn ip6_uri_host() {
|
||||||
let mut parts = Request::new(()).into_parts().0;
|
let mut parts = Request::new(()).into_parts().0;
|
||||||
parts.uri = "http://cool:user@[::1]:456/file.txt".parse().unwrap();
|
parts.uri = "http://cool:user@[::1]:456/file.txt".parse().unwrap();
|
||||||
let host = Host::from_request_parts(&mut parts, &()).await.unwrap();
|
let host = parts.extract::<Host>().await.unwrap();
|
||||||
assert_eq!(host.0, "[::1]:456");
|
assert_eq!(host.0, "[::1]:456");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[crate::test]
|
||||||
|
async fn missing_host() {
|
||||||
|
let mut parts = Request::new(()).into_parts().0;
|
||||||
|
let host = parts.extract::<Host>().await.unwrap_err();
|
||||||
|
assert!(matches!(host, HostRejection::FailedToResolveHost(_)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[crate::test]
|
||||||
|
async fn optional_extractor() {
|
||||||
|
let mut parts = Request::new(()).into_parts().0;
|
||||||
|
parts.uri = "https://127.0.0.1:1234/image.jpg".parse().unwrap();
|
||||||
|
let host = parts.extract::<Option<Host>>().await.unwrap();
|
||||||
|
assert!(host.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[crate::test]
|
||||||
|
async fn optional_extractor_none() {
|
||||||
|
let mut parts = Request::new(()).into_parts().0;
|
||||||
|
let host = parts.extract::<Option<Host>>().await.unwrap();
|
||||||
|
assert!(host.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn forwarded_parsing() {
|
fn forwarded_parsing() {
|
||||||
// the basic case
|
// the basic case
|
||||||
|
|||||||
Reference in New Issue
Block a user