user_agent.rs
use pavex::http::header::{ToStrError, USER_AGENT};
use pavex::request::RequestHead;
use pavex::response::Response;
pub enum UserAgent {
Unknown,
Known(String),
}
impl UserAgent {
pub fn extract(request_head: &RequestHead) -> Result<Self, ToStrError > {
let Some(user_agent) = request_head.headers.get(USER_AGENT) else {
return Ok(UserAgent::Unknown);
};
user_agent.to_str().map(|s| UserAgent::Known(s.into()))
}
}
pub fn invalid_user_agent(_e: &ToStrError) -> Response {
Response::bad_request()
.set_typed_body("The `User-Agent` header value must be a valid UTF-8 string")
}
blueprint.rs
pub fn blueprint() -> Blueprint {
let mut bp = Blueprint::new();
register_common_constructors(&mut bp);
add_telemetry_middleware(&mut bp);
bp.constructor(
f!(crate::user_agent::UserAgent::extract),
Lifecycle::RequestScoped,
)
.error_handler(f!(crate::user_agent::invalid_user_agent));
bp.route(GET, "/api/ping", f!(crate::routes::status::ping));
bp.route(
GET,
"/api/greet/:name",
f!(crate::routes::greet::greet),
);
bp
}
test
#[tokio::test]
async fn non_utf8_user_agent_is_rejected() {
let api = TestApi::spawn().await;
let name = "Ursula";
let response = api
.api_client
.get(&format!("{}/api/greet/{name}", &api.api_address))
.header("User-Agent", b"hello\xfa".as_slice())
.send()
.await
.expect("Failed to execute request.");
assert_eq!(response.status().as_u16(), StatusCode::BAD_REQUEST.as_u16());
assert_eq!(
response.text().await.unwrap(),
"The `User-Agent` header value must be a valid UTF-8 string"
);
}