1use crate::dep::DepResolver;
5use crate::error::{Error, Result};
6use crate::response::Json;
7use bytes::Bytes;
8use serde::de::DeserializeOwned;
9use std::future::Future;
10
11pub(crate) type StreamLane =
15 http_body_util::combinators::UnsyncBoxBody<Bytes, Box<dyn std::error::Error + Send + Sync>>;
16
17pub(crate) enum BodyLane {
21 Buffered(Bytes),
22 Stream(Option<StreamLane>),
24}
25
26#[derive(Clone, Copy, Debug)]
30pub struct ClientAddr(pub std::net::SocketAddr);
31
32pub struct RequestCtx {
35 pub(crate) parts: http::request::Parts,
36 pub(crate) body: BodyLane,
37 pub(crate) params: Vec<(String, String)>,
39 pub(crate) deps: DepResolver,
40 pub(crate) is_task: bool,
43}
44
45impl RequestCtx {
46 pub(crate) fn new(parts: http::request::Parts, body: Bytes, deps: DepResolver) -> Self {
50 Self::with_lane(parts, BodyLane::Buffered(body), deps)
51 }
52
53 pub(crate) fn with_lane(
56 parts: http::request::Parts,
57 body: BodyLane,
58 deps: DepResolver,
59 ) -> Self {
60 Self {
61 parts,
62 body,
63 params: Vec::new(),
64 deps,
65 is_task: false,
66 }
67 }
68
69 pub(crate) async fn drain_body(&mut self) -> Result<Bytes> {
73 match &mut self.body {
74 BodyLane::Buffered(bytes) => Ok(bytes.clone()),
75 BodyLane::Stream(slot) => {
76 let stream = slot
80 .take()
81 .ok_or_else(|| Error::internal("request body was already consumed"))?;
82 use http_body_util::BodyExt;
83 let collected = stream.collect().await.map_err(map_stream_error)?;
84 let bytes = collected.to_bytes();
85 self.body = BodyLane::Buffered(bytes.clone());
86 Ok(bytes)
87 }
88 }
89 }
90
91 pub fn method(&self) -> &http::Method {
92 &self.parts.method
93 }
94 pub fn uri(&self) -> &http::Uri {
95 &self.parts.uri
96 }
97 pub fn headers(&self) -> &http::HeaderMap {
98 &self.parts.headers
99 }
100
101 pub fn param(&self, name: &str) -> Option<&str> {
105 self.params
106 .iter()
107 .find(|(k, _)| k == name)
108 .map(|(_, v)| v.as_str())
109 }
110
111 pub fn peer_addr(&self) -> Option<std::net::SocketAddr> {
116 self.parts.extensions.get::<ClientAddr>().map(|c| c.0)
117 }
118
119 pub fn take_extension<T: Send + Sync + 'static>(&mut self) -> Option<T> {
123 self.parts.extensions.remove::<T>()
124 }
125}
126
127pub(crate) fn map_stream_error(e: Box<dyn std::error::Error + Send + Sync>) -> Error {
131 if e.downcast_ref::<http_body_util::LengthLimitError>()
132 .is_some()
133 {
134 return Error::payload_too_large();
135 }
136 if e.downcast_ref::<crate::serve::RecvTimeout>().is_some() {
137 return Error::new(
138 http::StatusCode::REQUEST_TIMEOUT,
139 "JC0408",
140 "timed out reading the request body",
141 );
142 }
143 Error::bad_request("request body failed mid-read")
144}
145
146pub trait FromRequest: Sized + Send {
149 fn from_request(ctx: &mut RequestCtx) -> impl Future<Output = Result<Self>> + Send;
150}
151
152pub struct Path<T>(pub T);
158
159#[doc(hidden)]
163pub mod sealed {
164 pub trait Sealed {}
165}
166
167pub trait PathParam: sealed::Sealed + Sized + Send {
172 fn parse_param(name: &str, raw: &str) -> Result<Self>;
173}
174
175macro_rules! impl_path_param {
176 ($($t:ty),* $(,)?) => {$(
177 impl sealed::Sealed for $t {}
178 impl PathParam for $t {
179 fn parse_param(name: &str, raw: &str) -> Result<Self> {
180 raw.parse::<$t>().map_err(|e| {
181 Error::bad_request(format!("invalid path parameter `{name}`: {e}"))
182 })
183 }
184 }
185 )*};
186}
187
188#[macro_export]
203macro_rules! path_param {
204 ($($t:ty),* $(,)?) => {$(
205 impl $crate::extract::sealed::Sealed for $t {}
206 impl $crate::extract::PathParam for $t {
207 fn parse_param(name: &str, raw: &str) -> $crate::Result<Self> {
208 raw.parse::<$t>().map_err(|e| {
209 $crate::Error::bad_request(format!("invalid path parameter `{name}`: {e}"))
210 })
211 }
212 }
213 )*};
214}
215impl_path_param!(
216 i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64, bool, char, String,
217);
218
219impl<T: PathParam> FromRequest for Path<T> {
220 async fn from_request(ctx: &mut RequestCtx) -> Result<Self> {
221 if ctx.is_task {
222 return Err(Error::task_context());
223 }
224 let (name, raw) = ctx
228 .params
229 .last()
230 .ok_or_else(|| Error::internal("route has no path parameters"))?;
231 T::parse_param(name, raw).map(Path)
232 }
233}
234
235impl<A: PathParam, B: PathParam> FromRequest for Path<(A, B)> {
236 async fn from_request(ctx: &mut RequestCtx) -> Result<Self> {
237 if ctx.is_task {
238 return Err(Error::task_context());
239 }
240 let [a, b] = take_params::<2>(ctx)?;
241 Ok(Path((
242 A::parse_param(&a.0, &a.1)?,
243 B::parse_param(&b.0, &b.1)?,
244 )))
245 }
246}
247
248impl<A: PathParam, B: PathParam, C: PathParam> FromRequest for Path<(A, B, C)> {
249 async fn from_request(ctx: &mut RequestCtx) -> Result<Self> {
250 if ctx.is_task {
251 return Err(Error::task_context());
252 }
253 let [a, b, c] = take_params::<3>(ctx)?;
254 Ok(Path((
255 A::parse_param(&a.0, &a.1)?,
256 B::parse_param(&b.0, &b.1)?,
257 C::parse_param(&c.0, &c.1)?,
258 )))
259 }
260}
261
262fn take_params<const N: usize>(ctx: &RequestCtx) -> Result<[(String, String); N]> {
265 if ctx.params.len() < N {
266 return Err(Error::internal(format!(
267 "route captures {} path parameter(s) but the handler expects {N}",
268 ctx.params.len()
269 )));
270 }
271 Ok(std::array::from_fn(|i| ctx.params[i].clone()))
272}
273
274pub struct PathParams(Vec<(String, String)>);
283
284impl PathParams {
285 pub fn get(&self, name: &str) -> Option<&str> {
288 self.0
289 .iter()
290 .find(|(k, _)| k == name)
291 .map(|(_, v)| v.as_str())
292 }
293}
294
295impl FromRequest for PathParams {
296 async fn from_request(ctx: &mut RequestCtx) -> Result<Self> {
297 if ctx.is_task {
298 return Err(Error::task_context());
299 }
300 Ok(PathParams(ctx.params.clone()))
301 }
302}
303
304pub struct Query<T>(pub T);
306
307impl<T: DeserializeOwned + Send> FromRequest for Query<T> {
308 async fn from_request(ctx: &mut RequestCtx) -> Result<Self> {
309 if ctx.is_task {
310 return Err(Error::task_context());
311 }
312 let q = ctx.parts.uri.query().unwrap_or("");
313 serde_urlencoded::from_str::<T>(q)
314 .map(Query)
315 .map_err(|e| Error::bad_request(format!("invalid query string: {e}")))
316 }
317}
318
319impl<T: DeserializeOwned + Send> FromRequest for Json<T> {
320 async fn from_request(ctx: &mut RequestCtx) -> Result<Self> {
321 if ctx.is_task {
322 return Err(Error::task_context());
323 }
324 let body = ctx.drain_body().await?;
325 serde_json::from_slice::<T>(&body)
326 .map(Json)
327 .map_err(|e| Error::unprocessable(format!("invalid JSON body: {e}")))
328 }
329}
330
331pub struct Headers(pub(crate) http::HeaderMap);
333
334impl Headers {
335 pub fn get(&self, name: &str) -> Option<&str> {
337 self.0.get(name).and_then(|v| v.to_str().ok())
338 }
339}
340
341impl FromRequest for Headers {
342 async fn from_request(ctx: &mut RequestCtx) -> Result<Self> {
343 if ctx.is_task {
344 return Err(Error::task_context());
345 }
346 Ok(Headers(ctx.headers().clone()))
347 }
348}
349
350pub struct RawBody(pub Bytes);
355
356impl FromRequest for RawBody {
357 async fn from_request(ctx: &mut RequestCtx) -> Result<Self> {
358 if ctx.is_task {
359 return Err(Error::task_context());
360 }
361 Ok(RawBody(ctx.drain_body().await?))
362 }
363}
364
365impl<T: FromRequest> FromRequest for Option<T> {
372 async fn from_request(ctx: &mut RequestCtx) -> Result<Self> {
373 Ok(T::from_request(ctx).await.ok())
374 }
375}
376
377impl<T: FromRequest> FromRequest for Result<T, Error> {
387 async fn from_request(ctx: &mut RequestCtx) -> Result<Self> {
388 Ok(T::from_request(ctx).await)
389 }
390}
391
392#[cfg(test)]
393mod tests {
394 use super::*;
395 use crate::dep::DepEnv;
396 use std::sync::Arc;
397
398 fn ctx(uri: &str, body: &str) -> RequestCtx {
399 let req = http::Request::builder()
400 .method(http::Method::GET)
401 .uri(uri)
402 .body(())
403 .unwrap();
404 let (parts, ()) = req.into_parts();
405 RequestCtx::new(
406 parts,
407 Bytes::from(body.to_string()),
408 DepResolver::new(Arc::new(DepEnv::default()), Default::default()),
409 )
410 }
411
412 #[tokio::test]
413 async fn peer_addr_is_none_without_a_socket_and_readable_when_set() {
414 let mut c = ctx("/x", "");
415 assert!(c.peer_addr().is_none());
416 let addr: std::net::SocketAddr = "203.0.113.7:5000".parse().unwrap();
417 c.parts.extensions.insert(crate::extract::ClientAddr(addr));
418 assert_eq!(c.peer_addr(), Some(addr));
419 }
420
421 #[tokio::test]
422 async fn path_extracts_typed_param() {
423 let mut c = ctx("/todos/42", "");
424 c.params.push(("id".into(), "42".into()));
425 let Path(id): Path<i64> = Path::<i64>::from_request(&mut c).await.unwrap();
426 assert_eq!(id, 42);
427 }
428
429 #[test]
430 fn ctx_param_reads_a_named_captured_param() {
431 let mut c = ctx("/x", "");
436 c.params.push(("club_id".into(), "42".into()));
437 c.params.push(("id".into(), "7".into()));
438 assert_eq!(c.param("club_id"), Some("42"));
439 assert_eq!(c.param("id"), Some("7"));
440 assert_eq!(c.param("missing"), None);
441 }
442
443 #[tokio::test]
444 async fn path_params_reads_named_and_rejects_task() {
445 let mut c = ctx("/x", "");
451 c.params.push(("club_id".into(), "42".into()));
452 c.params.push(("id".into(), "7".into()));
453 let p = PathParams::from_request(&mut c).await.unwrap();
454 assert_eq!(p.get("club_id"), Some("42"));
455 assert_eq!(p.get("id"), Some("7"));
456 assert_eq!(p.get("missing"), None);
457
458 let mut task = ctx("/x", "");
460 task.is_task = true;
461 let err = PathParams::from_request(&mut task).await.err().unwrap();
462 assert_eq!(err.code(), "JC1003");
463 }
464
465 #[tokio::test]
466 async fn path_with_wrong_type_is_400() {
467 let mut c = ctx("/todos/abc", "");
468 c.params.push(("id".into(), "abc".into()));
469 let err = Path::<i64>::from_request(&mut c).await.err().unwrap();
470 assert_eq!(err.code(), "JC0400");
471 }
472
473 #[tokio::test]
474 async fn path_missing_param_is_500() {
475 let mut c = ctx("/todos", "");
478 let err = Path::<i64>::from_request(&mut c).await.err().unwrap();
479 assert_eq!(err.code(), "JC0500");
480 }
481
482 #[tokio::test]
483 async fn query_deserializes_struct() {
484 #[derive(serde::Deserialize)]
485 struct Page {
486 limit: u32,
487 offset: u32,
488 }
489 let mut c = ctx("/todos?limit=10&offset=20", "");
490 let Query(p): Query<Page> = Query::from_request(&mut c).await.unwrap();
491 assert_eq!((p.limit, p.offset), (10, 20));
492 }
493
494 #[tokio::test]
495 async fn option_extractor_yields_none_on_failure_and_some_on_success() {
496 #[derive(serde::Deserialize)]
500 struct P {
501 n: i64,
502 }
503 async fn probe(q: Option<Query<P>>) -> Result<Json<Option<i64>>> {
504 Ok(Json(q.map(|Query(p)| p.n)))
505 }
506 let t = crate::App::new()
507 .route("/probe", crate::get(probe))
508 .into_test();
509 assert_eq!(t.get("/probe?n=7").await.text(), "7");
510 assert_eq!(t.get("/probe").await.text(), "null");
512 assert_eq!(t.get("/probe?n=not-a-number").await.text(), "null");
513 }
514
515 #[tokio::test]
516 async fn result_extractor_preserves_the_inner_error() {
517 struct Gate;
523 impl FromRequest for Gate {
524 async fn from_request(_ctx: &mut RequestCtx) -> Result<Self> {
525 Err(Error::forbidden())
526 }
527 }
528 async fn probe(gate: Result<Gate, Error>) -> Result<Json<&'static str>> {
529 let _gate = gate?;
530 Ok(Json("open"))
531 }
532 let t = crate::App::new()
533 .route("/probe", crate::get(probe))
534 .into_test();
535 let res = t.get("/probe").await;
536 assert_eq!(
537 res.status().as_u16(),
538 403,
539 "the inner error's status survives; body: {}",
540 res.text()
541 );
542 assert!(res.text().contains("JC0403"), "body: {}", res.text());
543 let mut c = ctx("/x", "");
545 let ok = <Result<Headers, Error> as FromRequest>::from_request(&mut c)
546 .await
547 .expect("outer extraction never fails");
548 assert!(ok.is_ok(), "success is Ok(Ok(_))");
549 }
550
551 #[tokio::test]
552 async fn single_path_param_binds_the_leaf_segment() {
553 use crate::prelude::*;
554 async fn show(Path(id): Path<i64>) -> Result<Json<i64>> {
555 Ok(Json(id))
556 }
557 let t = App::new()
558 .mount(
559 "/ws/{ws}",
560 Module::new("leads").route("/leads/{id}", get(show)),
561 )
562 .into_test();
563 assert_eq!(
564 t.get("/ws/7/leads/42").await.json::<i64>(),
565 42,
566 "leaf param, not mount param"
567 );
568 }
569
570 #[tokio::test]
571 async fn tuples_still_read_root_to_leaf() {
572 use crate::prelude::*;
573 async fn pair(Path((ws, id)): Path<(i64, i64)>) -> Result<Json<(i64, i64)>> {
574 Ok(Json((ws, id)))
575 }
576 let t = App::new()
577 .mount(
578 "/ws/{ws}",
579 Module::new("leads").route("/leads/{id}", get(pair)),
580 )
581 .into_test();
582 assert_eq!(t.get("/ws/7/leads/42").await.json::<(i64, i64)>(), (7, 42));
583 }
584
585 #[tokio::test]
586 async fn path_param_macro_admits_custom_newtypes() {
587 use crate::prelude::*;
588 #[derive(Debug)]
589 struct LeadId(i64);
590 impl std::str::FromStr for LeadId {
591 type Err = std::num::ParseIntError;
592 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
593 Ok(LeadId(s.parse()?))
594 }
595 }
596 crate::path_param!(LeadId);
597 async fn show(Path(id): Path<LeadId>) -> Result<Json<i64>> {
598 Ok(Json(id.0))
599 }
600 let t = App::new().route("/leads/{id}", get(show)).into_test();
601 assert_eq!(t.get("/leads/42").await.json::<i64>(), 42);
602 }
603
604 #[tokio::test]
605 async fn raw_body_yields_exact_bytes_and_coexists_with_headers() {
606 use crate::prelude::*;
607 async fn verify(headers: Headers, body: RawBody) -> Result<Json<(usize, bool)>> {
608 let signed = headers.get("x-signature").is_some();
609 Ok(Json((body.0.len(), signed)))
610 }
611 let t = App::new().route("/hook", post(verify)).into_test();
612 let res = t
613 .post_bytes_with("/hook", b"{\"raw\": 1}", &[("x-signature", "abc")])
614 .await;
615 assert_eq!(res.status().as_u16(), 200);
616 assert_eq!(res.json::<(usize, bool)>(), (10, true));
617 }
618
619 #[tokio::test]
620 async fn raw_body_drains_a_stream_route_transparently() {
621 use crate::prelude::*;
622 async fn len(body: RawBody) -> Result<Json<usize>> {
623 Ok(Json(body.0.len()))
624 }
625 let t = App::new().route("/up", post(len).stream_body()).into_test();
626 let payload = vec![b'x'; 100]; let res = t.post_bytes("/up", &payload).await;
628 assert_eq!(res.json::<usize>(), 100);
629 }
630
631 #[tokio::test]
632 async fn json_body_deserializes_and_bad_json_is_422() {
633 #[derive(serde::Deserialize)]
634 struct NewTodo {
635 title: String,
636 }
637 let mut c = ctx("/todos", r#"{"title":"x"}"#);
638 let Json(t): Json<NewTodo> = Json::from_request(&mut c).await.unwrap();
639 assert_eq!(t.title, "x");
640
641 let mut bad = ctx("/todos", r#"{"title":"#);
642 let err = Json::<NewTodo>::from_request(&mut bad).await.err().unwrap();
643 assert_eq!(err.code(), "JC0422");
644 }
645
646 fn stream_ctx(body: &[u8], limit: Option<usize>) -> RequestCtx {
651 use http_body_util::BodyExt;
652 use http_body_util::combinators::UnsyncBoxBody;
653 let req = http::Request::builder().uri("/up").body(()).unwrap();
654 let (parts, ()) = req.into_parts();
655 let bytes = Bytes::copy_from_slice(body);
656 let lane: StreamLane = match limit {
657 Some(limit) => {
658 let limited = http_body_util::Limited::new(
659 http_body_util::Full::<Bytes>::new(bytes).map_err(
660 |never| -> Box<dyn std::error::Error + Send + Sync> { match never {} },
661 ),
662 limit,
663 );
664 UnsyncBoxBody::new(limited.map_err(Into::into))
665 }
666 None => {
667 let full = http_body_util::Full::<Bytes>::new(bytes);
668 UnsyncBoxBody::new(full.map_err(
669 |never| -> Box<dyn std::error::Error + Send + Sync> { match never {} },
670 ))
671 }
672 };
673 RequestCtx::with_lane(
674 parts,
675 BodyLane::Stream(Some(lane)),
676 DepResolver::new(Arc::new(DepEnv::default()), Default::default()),
677 )
678 }
679
680 #[tokio::test]
681 async fn stream_routes_deliver_the_body_and_enforce_the_limit() {
682 use crate::prelude::*;
683 async fn echo(Json(v): Json<serde_json::Value>) -> Result<Json<serde_json::Value>> {
684 Ok(Json(v))
685 }
686 let t = App::new()
687 .route("/up", post(echo).stream_body().body_limit(64))
688 .into_test();
689 let res = t.post_json("/up", &serde_json::json!({"k": "v"})).await;
691 assert_eq!(res.status().as_u16(), 200);
692 let big = serde_json::json!({"k": "x".repeat(200)});
694 let res = t.post_json("/up", &big).await;
695 assert_eq!(res.status().as_u16(), 413, "body: {}", res.text());
696 }
697
698 #[tokio::test]
699 async fn drain_body_twice_caches_the_stream_bytes() {
700 use bytes::Bytes;
704 let mut c = stream_ctx(br#"{"k":"v"}"#, None);
705 let first = c.drain_body().await.unwrap();
706 assert_eq!(first, Bytes::from_static(br#"{"k":"v"}"#));
707 let second = c.drain_body().await.unwrap();
708 assert_eq!(second, first, "second drain returns the cached bytes");
709 }
710
711 #[tokio::test]
712 async fn stream_lane_over_limit_maps_to_413() {
713 let mut c = stream_ctx(&[b'x'; 200], Some(64));
716 let err = c.drain_body().await.err().unwrap();
717 assert_eq!(err.code(), "JC0413");
718 }
719
720 #[tokio::test]
721 async fn limit_trips_through_the_timed_recv_wrapper_still_map_to_413() {
722 use crate::serve::TimedRecvBody;
729 use http_body_util::BodyExt;
730 use http_body_util::combinators::UnsyncBoxBody;
731 use std::time::Duration;
732
733 let req = http::Request::builder().uri("/up").body(()).unwrap();
734 let (parts, ()) = req.into_parts();
735 let over_limit_body = http_body_util::Full::<Bytes>::new(Bytes::from_static(&[b'x'; 200]))
736 .map_err(|never| -> Box<dyn std::error::Error + Send + Sync> { match never {} });
737 let lane: StreamLane = UnsyncBoxBody::new(TimedRecvBody::new(
738 http_body_util::Limited::new(over_limit_body, 64),
739 Duration::from_secs(5),
740 ));
741 let mut c = RequestCtx::with_lane(
742 parts,
743 BodyLane::Stream(Some(lane)),
744 DepResolver::new(Arc::new(DepEnv::default()), Default::default()),
745 );
746 let err = c.drain_body().await.err().unwrap();
747 assert_eq!(err.code(), "JC0413");
748 assert_eq!(err.status().as_u16(), 413);
749 }
750}