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 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
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}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 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}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).