Skip to main content

Request

Struct Request 

Source
pub struct Request { /* private fields */ }
Expand description

HTTP request.

Implementations§

Source§

impl Request

Source

pub fn new(method: Method, path: impl Into<String>) -> Request

Create a new request.

Source

pub fn with_version( method: Method, path: impl Into<String>, version: HttpVersion, ) -> Request

Create a new request with a specific HTTP version.

Source

pub fn version(&self) -> HttpVersion

Get the HTTP version.

Source

pub fn set_version(&mut self, version: HttpVersion)

Set the HTTP version.

Source

pub fn method(&self) -> Method

Get the HTTP method.

Source

pub fn path(&self) -> &str

Get the request path.

Examples found in repository?
examples/crud_api.rs (line 111)
110fn extract_user_id(req: &Request) -> Result<u64, Response> {
111    let path = req.path();
112    let segments: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
113    segments
114        .get(1)
115        .and_then(|s| s.parse::<u64>().ok())
116        .ok_or_else(|| json_error(StatusCode::BAD_REQUEST, "Invalid user ID"))
117}
Source

pub fn set_path(&mut self, path: String)

Set the request path.

This is used internally for mounted sub-applications, where the mount prefix is stripped from the path before forwarding.

Source

pub fn query(&self) -> Option<&str>

Get the query string.

Source

pub fn headers(&self) -> &Headers

Get the headers.

Examples found in repository?
examples/crud_api.rs (line 92)
90fn parse_json_body<T: serde::de::DeserializeOwned>(req: &mut Request) -> Result<T, Response> {
91    let is_json = req
92        .headers()
93        .get("content-type")
94        .is_some_and(|ct| ct.starts_with(b"application/json"));
95
96    if !is_json {
97        return Err(json_error(
98            StatusCode::UNSUPPORTED_MEDIA_TYPE,
99            "Content-Type must be application/json",
100        ));
101    }
102
103    let Body::Bytes(body) = req.take_body() else {
104        return Err(json_error(StatusCode::BAD_REQUEST, "Missing request body"));
105    };
106    serde_json::from_slice(&body)
107        .map_err(|e| json_error(StatusCode::BAD_REQUEST, &format!("Invalid JSON: {e}")))
108}
More examples
Hide additional examples
examples/auth_example.rs (line 113)
107fn login_handler(_ctx: &RequestContext, req: &mut Request) -> std::future::Ready<Response> {
108    // In a real app, we would parse the JSON body and validate credentials.
109    // For this demo, we just check that it's a POST with some body.
110
111    // Check Content-Type
112    let is_json = req
113        .headers()
114        .get("content-type")
115        .is_some_and(|ct| ct.starts_with(b"application/json"));
116
117    if !is_json {
118        let error = serde_json::json!({
119            "detail": "Content-Type must be application/json"
120        });
121        return std::future::ready(
122            Response::with_status(StatusCode::UNSUPPORTED_MEDIA_TYPE)
123                .header("content-type", b"application/json".to_vec())
124                .body(ResponseBody::Bytes(error.to_string().into_bytes())),
125        );
126    }
127
128    // For demo purposes, we don't validate credentials - just return the token
129    // In production, you would:
130    // 1. Parse the request body as LoginRequest
131    // 2. Verify username/password against your database
132    // 3. Generate a unique, cryptographically secure token
133    // 4. Store token -> user_id mapping (with expiration)
134
135    let response = LoginResponse {
136        access_token: SECRET_TOKEN.to_string(),
137        token_type: "bearer",
138    };
139
140    std::future::ready(
141        Response::ok()
142            .header("content-type", b"application/json".to_vec())
143            .body(ResponseBody::Bytes(
144                serde_json::to_string(&response).unwrap().into_bytes(),
145            )),
146    )
147}
148
149/// Handler for protected endpoint - requires valid bearer token.
150///
151/// This handler manually extracts and validates the bearer token:
152/// 1. Gets the Authorization header
153/// 2. Verifies it uses the Bearer scheme
154/// 3. Validates the token against our secret using constant-time comparison
155///
156/// Returns appropriate error responses for each failure mode.
157fn protected_handler(_ctx: &RequestContext, req: &mut Request) -> std::future::Ready<Response> {
158    // Step 1: Get the Authorization header
159    let Some(auth_header) = req.headers().get("authorization") else {
160        // Missing header -> 401 Unauthorized
161        let body = serde_json::json!({
162            "detail": "Not authenticated"
163        });
164        return std::future::ready(
165            Response::with_status(StatusCode::UNAUTHORIZED)
166                .header("www-authenticate", b"Bearer".to_vec())
167                .header("content-type", b"application/json".to_vec())
168                .body(ResponseBody::Bytes(body.to_string().into_bytes())),
169        );
170    };
171
172    // Step 2: Parse the Authorization header
173    let Ok(auth_str) = std::str::from_utf8(auth_header) else {
174        // Invalid UTF-8 -> 401 Unauthorized
175        let body = serde_json::json!({
176            "detail": "Invalid authentication credentials"
177        });
178        return std::future::ready(
179            Response::with_status(StatusCode::UNAUTHORIZED)
180                .header("www-authenticate", b"Bearer".to_vec())
181                .header("content-type", b"application/json".to_vec())
182                .body(ResponseBody::Bytes(body.to_string().into_bytes())),
183        );
184    };
185
186    // Step 3: Check for "Bearer " prefix (case-insensitive for the scheme)
187    let Some(token) = auth_str
188        .strip_prefix("Bearer ")
189        .or_else(|| auth_str.strip_prefix("bearer "))
190    else {
191        // Wrong scheme -> 401 Unauthorized
192        let body = serde_json::json!({
193            "detail": "Invalid authentication credentials"
194        });
195        return std::future::ready(
196            Response::with_status(StatusCode::UNAUTHORIZED)
197                .header("www-authenticate", b"Bearer".to_vec())
198                .header("content-type", b"application/json".to_vec())
199                .body(ResponseBody::Bytes(body.to_string().into_bytes())),
200        );
201    };
202
203    let token = token.trim();
204    if token.is_empty() {
205        // Empty token -> 401 Unauthorized
206        let body = serde_json::json!({
207            "detail": "Invalid authentication credentials"
208        });
209        return std::future::ready(
210            Response::with_status(StatusCode::UNAUTHORIZED)
211                .header("www-authenticate", b"Bearer".to_vec())
212                .header("content-type", b"application/json".to_vec())
213                .body(ResponseBody::Bytes(body.to_string().into_bytes())),
214        );
215    }
216
217    // Step 4: Validate the token using constant-time comparison
218    // Create a BearerToken for secure comparison
219    let bearer_token = BearerToken::new(token);
220    if !bearer_token.secure_eq(SECRET_TOKEN) {
221        // Invalid token -> 403 Forbidden
222        let body = serde_json::json!({
223            "detail": "Invalid token"
224        });
225        return std::future::ready(
226            Response::with_status(StatusCode::FORBIDDEN)
227                .header("content-type", b"application/json".to_vec())
228                .body(ResponseBody::Bytes(body.to_string().into_bytes())),
229        );
230    }
231
232    // Token is valid - return protected data
233    let user_info = UserInfo {
234        username: "demo_user".to_string(),
235        message: "You have accessed a protected resource!".to_string(),
236    };
237
238    std::future::ready(
239        Response::ok()
240            .header("content-type", b"application/json".to_vec())
241            .body(ResponseBody::Bytes(
242                serde_json::to_string(&user_info).unwrap().into_bytes(),
243            )),
244    )
245}
Source

pub fn headers_mut(&mut self) -> &mut Headers

Get mutable headers.

Source

pub fn body(&self) -> &Body

Get the body.

Source

pub fn take_body(&mut self) -> Body

Take the body, replacing with Empty.

Examples found in repository?
examples/crud_api.rs (line 103)
90fn parse_json_body<T: serde::de::DeserializeOwned>(req: &mut Request) -> Result<T, Response> {
91    let is_json = req
92        .headers()
93        .get("content-type")
94        .is_some_and(|ct| ct.starts_with(b"application/json"));
95
96    if !is_json {
97        return Err(json_error(
98            StatusCode::UNSUPPORTED_MEDIA_TYPE,
99            "Content-Type must be application/json",
100        ));
101    }
102
103    let Body::Bytes(body) = req.take_body() else {
104        return Err(json_error(StatusCode::BAD_REQUEST, "Missing request body"));
105    };
106    serde_json::from_slice(&body)
107        .map_err(|e| json_error(StatusCode::BAD_REQUEST, &format!("Invalid JSON: {e}")))
108}
Source

pub fn set_body(&mut self, body: Body)

Set the body.

Source

pub fn set_query(&mut self, query: Option<String>)

Set the query string.

Source

pub fn insert_extension<T>(&mut self, value: T)
where T: Any + Send + Sync,

Insert a typed extension value.

Source

pub fn get_extension<T>(&self) -> Option<&T>
where T: Any + Send + Sync,

Get a typed extension value.

Source

pub fn get_extension_mut<T>(&mut self) -> Option<&mut T>
where T: Any + Send + Sync,

Get a mutable typed extension value.

Trait Implementations§

Source§

impl Debug for Request

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, _span: NoopSpan) -> Self

Instruments this future with a span (no-op when disabled).
Source§

fn in_current_span(self) -> Self

Instruments this future with the current span (no-op when disabled).
Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

impl<T> ResponseProduces<T> for T