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 an explicit 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 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 // For this demo, we only check Content-Type and return a fixed token.
108 // Real applications should parse the body and validate credentials.
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:
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.
Sourcepub fn get_extension_mut<T>(&mut self) -> Option<&mut T>
pub fn get_extension_mut<T>(&mut self) -> Option<&mut T>
Get a mutable typed extension value.
Sourcepub fn take_extension<T>(&mut self) -> Option<T>
pub fn take_extension<T>(&mut self) -> Option<T>
Remove and return a typed extension value.
Sourcepub fn background_tasks(&mut self) -> &BackgroundTasks
pub fn background_tasks(&mut self) -> &BackgroundTasks
Access (and lazily create) the request-scoped background tasks container.
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 UnsafeUnpin 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).