pub struct Request { /* private fields */ }Expand description
HTTP request.
Implementations§
Source§impl Request
impl Request
Sourcepub fn with_version(
method: Method,
path: impl Into<String>,
version: HttpVersion,
) -> Request
pub fn with_version( method: Method, path: impl Into<String>, version: HttpVersion, ) -> Request
Create a new request with a specific HTTP version.
Sourcepub fn version(&self) -> HttpVersion
pub fn version(&self) -> HttpVersion
Get the HTTP version.
Sourcepub fn set_version(&mut self, version: HttpVersion)
pub fn set_version(&mut self, version: HttpVersion)
Set the HTTP version.
Sourcepub fn set_path(&mut self, path: String)
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.
Sourcepub fn headers(&self) -> &Headers
pub fn headers(&self) -> &Headers
Get the headers.
Examples found in repository?
examples/crud_api.rs (line 97)
95fn parse_json_body<T: serde::de::DeserializeOwned>(req: &mut Request) -> Result<T, Response> {
96 let is_json = req
97 .headers()
98 .get("content-type")
99 .is_some_and(|ct| ct.starts_with(b"application/json"));
100
101 if !is_json {
102 return Err(json_error(
103 StatusCode::UNSUPPORTED_MEDIA_TYPE,
104 "Content-Type must be application/json",
105 ));
106 }
107
108 let Body::Bytes(body) = req.take_body() else {
109 return Err(json_error(StatusCode::BAD_REQUEST, "Missing request body"));
110 };
111 serde_json::from_slice(&body)
112 .map_err(|e| json_error(StatusCode::BAD_REQUEST, &format!("Invalid JSON: {e}")))
113}More examples
examples/auth_example.rs (line 112)
106fn login_handler(_ctx: &RequestContext, req: &mut Request) -> std::future::Ready<Response> {
107 // In a real app, we would parse the JSON body and validate credentials.
108 // For this demo, we just check that it's a POST with some body.
109
110 // Check Content-Type
111 let is_json = req
112 .headers()
113 .get("content-type")
114 .is_some_and(|ct| ct.starts_with(b"application/json"));
115
116 if !is_json {
117 let error = serde_json::json!({
118 "detail": "Content-Type must be application/json"
119 });
120 return std::future::ready(
121 Response::with_status(StatusCode::UNSUPPORTED_MEDIA_TYPE)
122 .header("content-type", b"application/json".to_vec())
123 .body(ResponseBody::Bytes(error.to_string().into_bytes())),
124 );
125 }
126
127 // For demo purposes, we don't validate credentials - just return the token
128 // In production, you would:
129 // 1. Parse the request body as LoginRequest
130 // 2. Verify username/password against your database
131 // 3. Generate a unique, cryptographically secure token
132 // 4. Store token -> user_id mapping (with expiration)
133
134 let response = LoginResponse {
135 access_token: DEMO_BEARER_VALUE.to_string(),
136 token_type: "bearer",
137 };
138
139 std::future::ready(
140 Response::ok()
141 .header("content-type", b"application/json".to_vec())
142 .body(ResponseBody::Bytes(json_bytes(&response))),
143 )
144}
145
146/// Handler for protected endpoint - requires valid bearer token.
147///
148/// This handler manually extracts and validates the bearer token:
149/// 1. Gets the Authorization header
150/// 2. Verifies it uses the Bearer scheme
151/// 3. Validates the token against our secret using constant-time comparison
152///
153/// Returns appropriate error responses for each failure mode.
154fn protected_handler(_ctx: &RequestContext, req: &mut Request) -> std::future::Ready<Response> {
155 // Step 1: Get the Authorization header
156 let Some(auth_header) = req.headers().get("authorization") else {
157 // Missing header -> 401 Unauthorized
158 let body = serde_json::json!({
159 "detail": "Not authenticated"
160 });
161 return std::future::ready(
162 Response::with_status(StatusCode::UNAUTHORIZED)
163 .header("www-authenticate", b"Bearer".to_vec())
164 .header("content-type", b"application/json".to_vec())
165 .body(ResponseBody::Bytes(body.to_string().into_bytes())),
166 );
167 };
168
169 // Step 2: Parse the Authorization header
170 let Ok(auth_str) = std::str::from_utf8(auth_header) else {
171 // Invalid UTF-8 -> 401 Unauthorized
172 let body = serde_json::json!({
173 "detail": "Invalid authentication credentials"
174 });
175 return std::future::ready(
176 Response::with_status(StatusCode::UNAUTHORIZED)
177 .header("www-authenticate", b"Bearer".to_vec())
178 .header("content-type", b"application/json".to_vec())
179 .body(ResponseBody::Bytes(body.to_string().into_bytes())),
180 );
181 };
182
183 // Step 3: Check for "Bearer " prefix (case-insensitive for the scheme)
184 let Some(bearer_value) = auth_str
185 .strip_prefix("Bearer ")
186 .or_else(|| auth_str.strip_prefix("bearer "))
187 else {
188 // Wrong scheme -> 401 Unauthorized
189 let body = serde_json::json!({
190 "detail": "Invalid authentication credentials"
191 });
192 return std::future::ready(
193 Response::with_status(StatusCode::UNAUTHORIZED)
194 .header("www-authenticate", b"Bearer".to_vec())
195 .header("content-type", b"application/json".to_vec())
196 .body(ResponseBody::Bytes(body.to_string().into_bytes())),
197 );
198 };
199
200 let bearer_value = bearer_value.trim();
201 if bearer_value.is_empty() {
202 // Empty token -> 401 Unauthorized
203 let body = serde_json::json!({
204 "detail": "Invalid authentication credentials"
205 });
206 return std::future::ready(
207 Response::with_status(StatusCode::UNAUTHORIZED)
208 .header("www-authenticate", b"Bearer".to_vec())
209 .header("content-type", b"application/json".to_vec())
210 .body(ResponseBody::Bytes(body.to_string().into_bytes())),
211 );
212 }
213
214 // Step 4: Validate the bearer value using constant-time comparison
215 if !bearer_value.secure_eq(DEMO_BEARER_VALUE) {
216 // Invalid token -> 403 Forbidden
217 let body = serde_json::json!({
218 "detail": "Invalid token"
219 });
220 return std::future::ready(
221 Response::with_status(StatusCode::FORBIDDEN)
222 .header("content-type", b"application/json".to_vec())
223 .body(ResponseBody::Bytes(body.to_string().into_bytes())),
224 );
225 }
226
227 // Token is valid - return protected data
228 let user_info = UserInfo {
229 username: "demo_user".to_string(),
230 message: "You have accessed a protected resource!".to_string(),
231 };
232
233 std::future::ready(
234 Response::ok()
235 .header("content-type", b"application/json".to_vec())
236 .body(ResponseBody::Bytes(json_bytes(&user_info))),
237 )
238}Sourcepub fn headers_mut(&mut self) -> &mut Headers
pub fn headers_mut(&mut self) -> &mut Headers
Get mutable headers.
Sourcepub fn take_body(&mut self) -> Body
pub fn take_body(&mut self) -> Body
Take the body, replacing with Empty.
Examples found in repository?
examples/crud_api.rs (line 108)
95fn parse_json_body<T: serde::de::DeserializeOwned>(req: &mut Request) -> Result<T, Response> {
96 let is_json = req
97 .headers()
98 .get("content-type")
99 .is_some_and(|ct| ct.starts_with(b"application/json"));
100
101 if !is_json {
102 return Err(json_error(
103 StatusCode::UNSUPPORTED_MEDIA_TYPE,
104 "Content-Type must be application/json",
105 ));
106 }
107
108 let Body::Bytes(body) = req.take_body() else {
109 return Err(json_error(StatusCode::BAD_REQUEST, "Missing request body"));
110 };
111 serde_json::from_slice(&body)
112 .map_err(|e| json_error(StatusCode::BAD_REQUEST, &format!("Invalid JSON: {e}")))
113}Sourcepub fn insert_extension<T>(&mut self, value: T)
pub fn insert_extension<T>(&mut self, value: T)
Insert a typed extension value.
Sourcepub fn get_extension<T>(&self) -> Option<&T>
pub fn get_extension<T>(&self) -> Option<&T>
Get a typed extension value.
Trait Implementations§
Auto Trait Implementations§
impl Freeze for Request
impl !RefUnwindSafe for Request
impl Send for Request
impl Sync for Request
impl Unpin for Request
impl !UnwindSafe for Request
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, _span: NoopSpan) -> Self
fn instrument(self, _span: NoopSpan) -> Self
Instruments this future with a span (no-op when disabled).
Source§fn in_current_span(self) -> Self
fn in_current_span(self) -> Self
Instruments this future with the current span (no-op when disabled).