use super::{rejection::*, FromRequest, FromRequestParts, Request}; use crate::{body::Body, RequestExt}; use bytes::{BufMut, Bytes, BytesMut}; use http::{request::Parts, Extensions, HeaderMap, Method, Uri, Version}; use http_body_util::BodyExt; use std::convert::Infallible; impl FromRequest for Request where S: Send + Sync, { type Rejection = Infallible; async fn from_request(req: Request, _: &S) -> Result { Ok(req) } } impl FromRequestParts for Method where S: Send + Sync, { type Rejection = Infallible; async fn from_request_parts(parts: &mut Parts, _: &S) -> Result { Ok(parts.method.clone()) } } impl FromRequestParts for Uri where S: Send + Sync, { type Rejection = Infallible; async fn from_request_parts(parts: &mut Parts, _: &S) -> Result { Ok(parts.uri.clone()) } } impl FromRequestParts for Version where S: Send + Sync, { type Rejection = Infallible; async fn from_request_parts(parts: &mut Parts, _: &S) -> Result { Ok(parts.version) } } /// Clone the headers from the request. /// /// Prefer using [`TypedHeader`] to extract only the headers you need. /// /// [`TypedHeader`]: https://docs.rs/axum-extra/0.10/axum_extra/struct.TypedHeader.html impl FromRequestParts for HeaderMap where S: Send + Sync, { type Rejection = Infallible; async fn from_request_parts(parts: &mut Parts, _: &S) -> Result { Ok(parts.headers.clone()) } } impl FromRequest for BytesMut where S: Send + Sync, { type Rejection = BytesRejection; async fn from_request(req: Request, _: &S) -> Result { let mut body = req.into_limited_body(); #[allow(clippy::use_self)] let mut bytes = BytesMut::new(); body_to_bytes_mut(&mut body, &mut bytes).await?; Ok(bytes) } } async fn body_to_bytes_mut(body: &mut Body, bytes: &mut BytesMut) -> Result<(), BytesRejection> { while let Some(frame) = body .frame() .await .transpose() .map_err(FailedToBufferBody::from_err)? { let Ok(data) = frame.into_data() else { return Ok(()); }; bytes.put(data); } Ok(()) } impl FromRequest for Bytes where S: Send + Sync, { type Rejection = BytesRejection; async fn from_request(req: Request, _: &S) -> Result { let bytes = req .into_limited_body() .collect() .await .map_err(FailedToBufferBody::from_err)? .to_bytes(); Ok(bytes) } } impl FromRequest for String where S: Send + Sync, { type Rejection = StringRejection; async fn from_request(req: Request, state: &S) -> Result { let bytes = Bytes::from_request(req, state) .await .map_err(|err| match err { BytesRejection::FailedToBufferBody(inner) => { StringRejection::FailedToBufferBody(inner) } })?; #[allow(clippy::use_self)] let string = String::from_utf8(bytes.into()).map_err(InvalidUtf8::from_err)?; Ok(string) } } impl FromRequestParts for Parts where S: Send + Sync, { type Rejection = Infallible; async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result { Ok(parts.clone()) } } impl FromRequestParts for Extensions where S: Send + Sync, { type Rejection = Infallible; async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result { Ok(parts.extensions.clone()) } } impl FromRequest for Body where S: Send + Sync, { type Rejection = Infallible; async fn from_request(req: Request, _: &S) -> Result { Ok(req.into_body()) } } #[cfg(test)] mod tests { use axum::{extract::Extension, routing::get, test_helpers::*, Router}; use http::{Method, StatusCode}; #[crate::test] async fn extract_request_parts() { #[derive(Clone)] struct Ext; async fn handler(parts: http::request::Parts) { assert_eq!(parts.method, Method::GET); assert_eq!(parts.uri, "/"); assert_eq!(parts.version, http::Version::HTTP_11); assert_eq!(parts.headers["x-foo"], "123"); parts.extensions.get::().unwrap(); } let client = TestClient::new(Router::new().route("/", get(handler)).layer(Extension(Ext))); let res = client.get("/").header("x-foo", "123").await; assert_eq!(res.status(), StatusCode::OK); } }