Skip to main content

tachyon_web/routing/
extract.rs

1//! Type-safe request extractors.
2
3/// WebSocket upgrade extractor and connection types (`WebSocketUpgrade`,
4/// `WebSocket`, `Message`, ...) — re-exported here at the same path Axum uses
5/// (`axum::extract::ws`), so `use tachyon_web::extract::ws::*;` matches
6/// `use axum::extract::ws::*;` verbatim. See [`crate::ws`] for the full docs.
7/// Requires the `ws` feature.
8#[cfg(feature = "ws")]
9pub use crate::ws;
10/// Flattened re-export matching `axum::extract::WebSocketUpgrade`.
11#[cfg(feature = "ws")]
12pub use crate::ws::WebSocketUpgrade;
13
14use crate::http::error::Error;
15use crate::http::response::Body;
16use bytes::Bytes;
17
18#[cfg(feature = "cookies")]
19use cookie::{Cookie, CookieJar};
20use hyper::header::HeaderMap;
21use hyper::{Method, StatusCode, Uri};
22use serde::de::DeserializeOwned;
23use std::convert::Infallible;
24use std::future::Future;
25
26/// Trait for extracting data from request parts (metadata).
27pub trait FromRequestParts<S>: Sized + Send {
28    /// The rejection type returned if extraction fails.
29    type Rejection: crate::http::response::IntoResponse + Send + 'static;
30
31    /// Extract this type from the request parts and state.
32    ///
33    /// # Errors
34    ///
35    /// Returns a rejection if the extraction from the request parts fails.
36    fn from_request_parts(
37        parts: &mut hyper::http::request::Parts,
38        state: &S,
39    ) -> Result<Self, Self::Rejection>;
40}
41
42/// Trait for extracting data from a request (possibly consuming the body).
43///
44/// This is `async` so extractors can await the body being streamed in — the
45/// request body is not necessarily fully buffered before your handler runs (see
46/// [`crate::routing::extract::BodyStream`]). Extractors that only need the parts
47/// (headers, method, URI, state) should implement [`FromRequestParts`] instead,
48/// which stays synchronous and is cheaper to call.
49pub trait FromRequest<S: Sync>: Sized + Send {
50    /// The rejection type returned if extraction fails.
51    type Rejection: crate::http::response::IntoResponse + Send + 'static;
52
53    /// Extract this type from the request and state.
54    ///
55    /// # Errors
56    ///
57    /// Returns a rejection if the extraction from the request body/parts fails.
58    fn from_request(
59        req: hyper::Request<Body>,
60        state: &S,
61    ) -> impl Future<Output = Result<Self, Self::Rejection>> + Send;
62}
63
64/// The maximum request-body size assumed by body-buffering extractors
65/// (`Bytes`, `String`, `Json`, `Form`) when no [`MaxBodySize`] extension is
66/// present on the request — e.g. when calling [`crate::routing::CompiledRouter::handle_request`]
67/// directly rather than through [`crate::server::Server`], which always sets it
68/// from `Server::max_body_size`.
69///
70/// 2 MiB, matching Axum's `DefaultBodyLimit` default exactly (Axum: "for
71/// security reasons, `Bytes` will, by default, not accept bodies larger than
72/// 2MB"). Override per-deployment via [`crate::server::Server::max_body_size`].
73pub(crate) const DEFAULT_MAX_BODY_SIZE: usize = 2 * 1024 * 1024;
74
75/// Internal: the configured maximum request-body size, threaded through request
76/// extensions (by the connection layer) so body-buffering extractors can enforce
77/// it without needing direct access to the `Server` that's handling the request.
78#[derive(Debug, Clone, Copy)]
79pub(crate) struct MaxBodySize(pub usize);
80
81pub(crate) fn max_body_size(extensions: &hyper::http::Extensions) -> usize {
82    extensions
83        .get::<MaxBodySize>()
84        .map_or(DEFAULT_MAX_BODY_SIZE, |m| m.0)
85}
86
87/// Overrides the request-body size limit enforced by the `Bytes`/`String`/
88/// `Json`/`Form` extractors, for a specific set of routes. Mirrors
89/// `axum::extract::DefaultBodyLimit`.
90///
91/// # Applying it
92///
93/// Axum applies this as a `tower::Layer`: `.layer(DefaultBodyLimit::max(n))`.
94/// Tachyon's body-size check is a plain extension read rather than a
95/// byte-buffering Tower layer (buffering happens lazily, only when an
96/// extractor that needs the body actually runs) — bridging this through
97/// `.layer()` would buffer the body under the *old* limit before the layer
98/// ever got a chance to install the new one, silently defeating the override.
99/// Apply it the native way instead, via [`DefaultBodyLimit::into_middleware`]
100/// and [`crate::routing::Router::hoop`]/[`crate::routing::MethodRouter::hoop`]:
101///
102/// ```rust,no_run
103/// use tachyon_web::extract::DefaultBodyLimit;
104/// use tachyon_web::{Router, get};
105///
106/// async fn upload() -> &'static str { "ok" }
107///
108/// let _app: Router<()> = Router::new()
109///     .route("/upload", get(upload))
110///     .hoop(DefaultBodyLimit::max(50 * 1024 * 1024).into_middleware());
111/// ```
112#[derive(Debug, Clone, Copy)]
113pub struct DefaultBodyLimit {
114    /// `None` means disabled (`usize::MAX`).
115    limit: Option<usize>,
116}
117
118impl DefaultBodyLimit {
119    /// Sets the maximum accepted request-body size, in bytes, for the routes
120    /// this is applied to.
121    #[must_use]
122    pub const fn max(limit: usize) -> Self {
123        Self { limit: Some(limit) }
124    }
125
126    /// Disables the body-size limit entirely for the routes this is applied
127    /// to. Matches `axum::extract::DefaultBodyLimit::disable`.
128    #[must_use]
129    pub const fn disable() -> Self {
130        Self { limit: None }
131    }
132
133    /// Turns this into native middleware, for use with `.hoop()`/`.hoop_at()`.
134    pub fn into_middleware<S>(
135        self,
136    ) -> impl Fn(hyper::Request<Body>, crate::routing::middleware::Next<S>) -> BoxedResponseFuture
137    + Clone
138    + Send
139    + Sync
140    + 'static
141    where
142        S: Send + Sync + 'static,
143    {
144        let limit = self.limit.unwrap_or(usize::MAX);
145        move |mut req: hyper::Request<Body>, next: crate::routing::middleware::Next<S>| {
146            let _ = req.extensions_mut().insert(MaxBodySize(limit));
147            Box::pin(next.run(req)) as BoxedResponseFuture
148        }
149    }
150}
151
152/// A boxed future resolving to an HTTP response, used by
153/// [`DefaultBodyLimit::into_middleware`]'s returned closure.
154type BoxedResponseFuture = std::pin::Pin<Box<dyn Future<Output = hyper::Response<Body>> + Send>>;
155
156/// Helper trait to obtain sub-state from app state.
157pub trait FromRef<S> {
158    /// Extract a reference/clone from the parent state.
159    fn from_ref(state: &S) -> Self;
160}
161
162impl<T: Clone> FromRef<T> for T {
163    fn from_ref(state: &T) -> Self {
164        state.clone()
165    }
166}
167
168/// Extractor for application state.
169#[derive(Debug, Clone, Copy)]
170pub struct State<T>(pub T);
171
172impl<S, T> FromRequestParts<S> for State<T>
173where
174    T: FromRef<S> + Send + Sync + 'static,
175{
176    type Rejection = Infallible;
177
178    fn from_request_parts(
179        _parts: &mut hyper::http::request::Parts,
180        state: &S,
181    ) -> Result<Self, Self::Rejection> {
182        Ok(Self(T::from_ref(state)))
183    }
184}
185
186impl<S, T> FromRequest<S> for State<T>
187where
188    S: Sync,
189    T: FromRef<S> + Send + Sync + 'static,
190{
191    type Rejection = Infallible;
192
193    async fn from_request(req: hyper::Request<Body>, state: &S) -> Result<Self, Self::Rejection> {
194        let (mut parts, _) = req.into_parts();
195        Self::from_request_parts(&mut parts, state)
196    }
197}
198
199/// Extractor for path parameters.
200#[cfg(any(feature = "query", feature = "form"))]
201#[derive(Debug, Clone)]
202struct QueryIter<'de> {
203    input: &'de str,
204}
205
206struct CoercingCowDeserializer<'de> {
207    val: std::borrow::Cow<'de, str>,
208}
209
210impl<'de> serde::de::Deserializer<'de> for CoercingCowDeserializer<'de> {
211    type Error = serde::de::value::Error;
212
213    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
214    where
215        V: serde::de::Visitor<'de>,
216    {
217        match self.val {
218            std::borrow::Cow::Borrowed(s) => visitor.visit_borrowed_str(s),
219            std::borrow::Cow::Owned(s) => visitor.visit_string(s),
220        }
221    }
222
223    fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>
224    where
225        V: serde::de::Visitor<'de>,
226    {
227        match self.val {
228            std::borrow::Cow::Borrowed(s) => visitor.visit_borrowed_str(s),
229            std::borrow::Cow::Owned(s) => visitor.visit_string(s),
230        }
231    }
232
233    fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error>
234    where
235        V: serde::de::Visitor<'de>,
236    {
237        self.deserialize_str(visitor)
238    }
239
240    fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
241    where
242        V: serde::de::Visitor<'de>,
243    {
244        let n = self
245            .val
246            .parse::<u8>()
247            .map_err(|e| serde::de::Error::custom(e.to_string()))?;
248        visitor.visit_u8(n)
249    }
250
251    fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
252    where
253        V: serde::de::Visitor<'de>,
254    {
255        let n = self
256            .val
257            .parse::<u16>()
258            .map_err(|e| serde::de::Error::custom(e.to_string()))?;
259        visitor.visit_u16(n)
260    }
261
262    fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
263    where
264        V: serde::de::Visitor<'de>,
265    {
266        let n = self
267            .val
268            .parse::<u32>()
269            .map_err(|e| serde::de::Error::custom(e.to_string()))?;
270        visitor.visit_u32(n)
271    }
272
273    fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
274    where
275        V: serde::de::Visitor<'de>,
276    {
277        let n = self
278            .val
279            .parse::<u64>()
280            .map_err(|e| serde::de::Error::custom(e.to_string()))?;
281        visitor.visit_u64(n)
282    }
283
284    fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
285    where
286        V: serde::de::Visitor<'de>,
287    {
288        let n = self
289            .val
290            .parse::<i8>()
291            .map_err(|e| serde::de::Error::custom(e.to_string()))?;
292        visitor.visit_i8(n)
293    }
294
295    fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
296    where
297        V: serde::de::Visitor<'de>,
298    {
299        let n = self
300            .val
301            .parse::<i16>()
302            .map_err(|e| serde::de::Error::custom(e.to_string()))?;
303        visitor.visit_i16(n)
304    }
305
306    fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
307    where
308        V: serde::de::Visitor<'de>,
309    {
310        let n = self
311            .val
312            .parse::<i32>()
313            .map_err(|e| serde::de::Error::custom(e.to_string()))?;
314        visitor.visit_i32(n)
315    }
316
317    fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
318    where
319        V: serde::de::Visitor<'de>,
320    {
321        let n = self
322            .val
323            .parse::<i64>()
324            .map_err(|e| serde::de::Error::custom(e.to_string()))?;
325        visitor.visit_i64(n)
326    }
327
328    fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
329    where
330        V: serde::de::Visitor<'de>,
331    {
332        let n = self
333            .val
334            .parse::<f32>()
335            .map_err(|e| serde::de::Error::custom(e.to_string()))?;
336        visitor.visit_f32(n)
337    }
338
339    fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
340    where
341        V: serde::de::Visitor<'de>,
342    {
343        let n = self
344            .val
345            .parse::<f64>()
346            .map_err(|e| serde::de::Error::custom(e.to_string()))?;
347        visitor.visit_f64(n)
348    }
349
350    fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
351    where
352        V: serde::de::Visitor<'de>,
353    {
354        let b = match self.val.as_ref() {
355            "true" | "1" => true,
356            "false" | "0" => false,
357            _ => self
358                .val
359                .parse::<bool>()
360                .map_err(|e| serde::de::Error::custom(e.to_string()))?,
361        };
362        visitor.visit_bool(b)
363    }
364
365    fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
366    where
367        V: serde::de::Visitor<'de>,
368    {
369        visitor.visit_some(self)
370    }
371
372    fn deserialize_enum<V>(
373        self,
374        _name: &'static str,
375        _variants: &'static [&'static str],
376        visitor: V,
377    ) -> Result<V::Value, Self::Error>
378    where
379        V: serde::de::Visitor<'de>,
380    {
381        use serde::de::IntoDeserializer;
382        visitor.visit_enum(self.val.into_deserializer())
383    }
384
385    serde::forward_to_deserialize_any! {
386        char bytes byte_buf unit unit_struct newtype_struct
387        seq tuple tuple_struct map struct identifier ignored_any
388    }
389}
390
391impl<'de> serde::de::IntoDeserializer<'de, serde::de::value::Error>
392    for CoercingCowDeserializer<'de>
393{
394    type Deserializer = Self;
395    fn into_deserializer(self) -> Self {
396        self
397    }
398}
399
400#[cfg(any(feature = "query", feature = "form"))]
401impl<'de> Iterator for QueryIter<'de> {
402    type Item = (std::borrow::Cow<'de, str>, CoercingCowDeserializer<'de>);
403
404    fn next(&mut self) -> Option<Self::Item> {
405        if self.input.is_empty() {
406            return None;
407        }
408
409        let bytes = self.input.as_bytes();
410        let len = bytes.len();
411        let end = bytes.iter().position(|&b| b == b'&').unwrap_or(len);
412        let pair_str = &self.input[..end];
413
414        if end < len {
415            self.input = &self.input[end + 1..];
416        } else {
417            self.input = "";
418        }
419
420        if pair_str.is_empty() {
421            return self.next();
422        }
423
424        let pair_bytes = pair_str.as_bytes();
425        let (key_raw, val_raw) = pair_bytes
426            .iter()
427            .position(|&b| b == b'=')
428            .map_or((pair_str, ""), |eq_idx| {
429                (&pair_str[..eq_idx], &pair_str[eq_idx + 1..])
430            });
431
432        let key = decode_query_param(key_raw);
433        let val = decode_query_param(val_raw);
434        Some((key, CoercingCowDeserializer { val }))
435    }
436}
437
438#[cfg(any(feature = "query", feature = "form"))]
439fn decode_query_param(s: &str) -> std::borrow::Cow<'_, str> {
440    let bytes = s.as_bytes();
441    if !bytes.iter().any(|&b| b == b'%' || b == b'+') {
442        return std::borrow::Cow::Borrowed(s);
443    }
444
445    let mut decoded = Vec::with_capacity(bytes.len());
446    let mut i = 0;
447    while i < bytes.len() {
448        match bytes[i] {
449            b'%' if i + 2 < bytes.len() => {
450                if let Ok(hex) = std::str::from_utf8(&bytes[i + 1..i + 3])
451                    && let Ok(val) = u8::from_str_radix(hex, 16)
452                {
453                    decoded.push(val);
454                    i += 3;
455                    continue;
456                }
457                decoded.push(b'%');
458                i += 1;
459            }
460            b'+' => {
461                decoded.push(b' ');
462                i += 1;
463            }
464            b => {
465                decoded.push(b);
466                i += 1;
467            }
468        }
469    }
470    String::from_utf8(decoded)
471        .map_or_else(|_| std::borrow::Cow::Borrowed(s), std::borrow::Cow::Owned)
472}
473
474/// Extractor for URI path parameters.
475///
476/// Supports three shapes, matching Axum:
477/// - A single scalar: `Path<u32>` on a route with exactly one param.
478/// - A tuple: `Path<(String, u32)>`, deserialized positionally in route order.
479/// - A struct/map: `Path<MyStruct>`, deserialized by param name (the common case).
480///
481/// Routes with no path parameters (e.g. `/health`) never populate a params
482/// list, so `Path<()>` or any other zero-field extractor deserializes
483/// successfully against an empty parameter set on those routes.
484#[derive(Debug, Clone)]
485pub struct Path<T>(pub T);
486
487/// Internal path parameters container stored in request extensions.
488#[derive(Debug, Clone)]
489pub struct PathParams(pub Vec<(std::sync::Arc<str>, String)>);
490
491/// A `serde::Deserializer` over route path parameters that supports scalar,
492/// tuple, and map/struct deserialization targets, mirroring Axum's `Path`
493/// extractor semantics.
494struct PathDeserializer<'de> {
495    params: &'de [(std::sync::Arc<str>, String)],
496}
497
498impl<'de> PathDeserializer<'de> {
499    fn single_value(&self) -> Result<&'de str, serde::de::value::Error> {
500        match self.params {
501            [(_, v)] => Ok(v.as_str()),
502            _ => Err(serde::de::Error::custom(format!(
503                "wrong number of path parameters: expected 1, got {}",
504                self.params.len()
505            ))),
506        }
507    }
508
509    const fn value_deserializer(val: &'de str) -> CoercingCowDeserializer<'de> {
510        CoercingCowDeserializer {
511            val: std::borrow::Cow::Borrowed(val),
512        }
513    }
514
515    fn map_deserializer(
516        &self,
517    ) -> serde::de::value::MapDeserializer<
518        'de,
519        impl Iterator<Item = (std::borrow::Cow<'de, str>, CoercingCowDeserializer<'de>)>,
520        serde::de::value::Error,
521    > {
522        serde::de::value::MapDeserializer::new(self.params.iter().map(|(k, v)| {
523            (
524                std::borrow::Cow::Borrowed(k.as_ref()),
525                CoercingCowDeserializer {
526                    val: std::borrow::Cow::Borrowed(v.as_str()),
527                },
528            )
529        }))
530    }
531}
532
533struct PathParamsSeqAccess<'de> {
534    iter: std::slice::Iter<'de, (std::sync::Arc<str>, String)>,
535}
536
537impl<'de> serde::de::SeqAccess<'de> for PathParamsSeqAccess<'de> {
538    type Error = serde::de::value::Error;
539
540    fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
541    where
542        T: serde::de::DeserializeSeed<'de>,
543    {
544        match self.iter.next() {
545            Some((_, v)) => seed
546                .deserialize(CoercingCowDeserializer {
547                    val: std::borrow::Cow::Borrowed(v.as_str()),
548                })
549                .map(Some),
550            None => Ok(None),
551        }
552    }
553
554    fn size_hint(&self) -> Option<usize> {
555        Some(self.iter.len())
556    }
557}
558
559macro_rules! path_deserialize_scalar {
560    ($($method:ident),* $(,)?) => {
561        $(
562            fn $method<V>(self, visitor: V) -> Result<V::Value, Self::Error>
563            where
564                V: serde::de::Visitor<'de>,
565            {
566                let val = self.single_value()?;
567                PathDeserializer::value_deserializer(val).$method(visitor)
568            }
569        )*
570    };
571}
572
573impl<'de> serde::de::Deserializer<'de> for PathDeserializer<'de> {
574    type Error = serde::de::value::Error;
575
576    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
577    where
578        V: serde::de::Visitor<'de>,
579    {
580        // Single-param routes are ambiguous between "scalar" and "1-field struct" at
581        // this point; defer to visit_map, which handles both since serde's derived
582        // struct visitors accept single-entry maps and scalar newtypes forward here too.
583        visitor.visit_map(self.map_deserializer())
584    }
585
586    path_deserialize_scalar!(
587        deserialize_bool,
588        deserialize_u8,
589        deserialize_u16,
590        deserialize_u32,
591        deserialize_u64,
592        deserialize_i8,
593        deserialize_i16,
594        deserialize_i32,
595        deserialize_i64,
596        deserialize_f32,
597        deserialize_f64,
598        deserialize_char,
599        deserialize_str,
600        deserialize_string,
601        deserialize_bytes,
602        deserialize_byte_buf,
603    );
604
605    fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
606    where
607        V: serde::de::Visitor<'de>,
608    {
609        visitor.visit_some(self)
610    }
611
612    fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
613    where
614        V: serde::de::Visitor<'de>,
615    {
616        visitor.visit_unit()
617    }
618
619    fn deserialize_unit_struct<V>(
620        self,
621        _name: &'static str,
622        visitor: V,
623    ) -> Result<V::Value, Self::Error>
624    where
625        V: serde::de::Visitor<'de>,
626    {
627        visitor.visit_unit()
628    }
629
630    fn deserialize_newtype_struct<V>(
631        self,
632        _name: &'static str,
633        visitor: V,
634    ) -> Result<V::Value, Self::Error>
635    where
636        V: serde::de::Visitor<'de>,
637    {
638        visitor.visit_newtype_struct(self)
639    }
640
641    fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
642    where
643        V: serde::de::Visitor<'de>,
644    {
645        visitor.visit_seq(PathParamsSeqAccess {
646            iter: self.params.iter(),
647        })
648    }
649
650    fn deserialize_tuple<V>(self, len: usize, visitor: V) -> Result<V::Value, Self::Error>
651    where
652        V: serde::de::Visitor<'de>,
653    {
654        if self.params.len() != len {
655            return Err(serde::de::Error::custom(format!(
656                "wrong number of path parameters: expected {len}, got {}",
657                self.params.len()
658            )));
659        }
660        self.deserialize_seq(visitor)
661    }
662
663    fn deserialize_tuple_struct<V>(
664        self,
665        _name: &'static str,
666        len: usize,
667        visitor: V,
668    ) -> Result<V::Value, Self::Error>
669    where
670        V: serde::de::Visitor<'de>,
671    {
672        self.deserialize_tuple(len, visitor)
673    }
674
675    fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
676    where
677        V: serde::de::Visitor<'de>,
678    {
679        visitor.visit_map(self.map_deserializer())
680    }
681
682    fn deserialize_struct<V>(
683        self,
684        _name: &'static str,
685        _fields: &'static [&'static str],
686        visitor: V,
687    ) -> Result<V::Value, Self::Error>
688    where
689        V: serde::de::Visitor<'de>,
690    {
691        self.deserialize_map(visitor)
692    }
693
694    fn deserialize_enum<V>(
695        self,
696        _name: &'static str,
697        _variants: &'static [&'static str],
698        visitor: V,
699    ) -> Result<V::Value, Self::Error>
700    where
701        V: serde::de::Visitor<'de>,
702    {
703        use serde::de::IntoDeserializer;
704        let val = self.single_value()?;
705        visitor.visit_enum(val.into_deserializer())
706    }
707
708    fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value, Self::Error>
709    where
710        V: serde::de::Visitor<'de>,
711    {
712        self.deserialize_str(visitor)
713    }
714
715    fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
716    where
717        V: serde::de::Visitor<'de>,
718    {
719        visitor.visit_unit()
720    }
721}
722
723impl<S, T> FromRequestParts<S> for Path<T>
724where
725    T: DeserializeOwned + Send + Sync + 'static,
726{
727    type Rejection = Error;
728
729    fn from_request_parts(
730        parts: &mut hyper::http::request::Parts,
731        _state: &S,
732    ) -> Result<Self, Self::Rejection> {
733        // Routes with no path parameters never insert a `PathParams`
734        // extension (see `CompiledRouter::handle_request`'s parameterless
735        // fast path), so a missing extension means "zero params" rather than
736        // an error — extractors like `Path<()>` must still succeed on those
737        // routes instead of getting a spurious 500.
738        let params = parts
739            .extensions
740            .get::<PathParams>()
741            .map_or(&[][..], |p| p.0.as_slice());
742
743        T::deserialize(PathDeserializer { params })
744            .map(Path)
745            .map_err(|e: serde::de::value::Error| Error::Rejection {
746                status: StatusCode::BAD_REQUEST,
747                message: format!("Failed to deserialize path parameters: {e}"),
748            })
749    }
750}
751
752impl<S, T> FromRequest<S> for Path<T>
753where
754    S: Sync,
755    T: DeserializeOwned + Send + Sync + 'static,
756{
757    type Rejection = Error;
758
759    async fn from_request(req: hyper::Request<Body>, state: &S) -> Result<Self, Self::Rejection> {
760        let (mut parts, _) = req.into_parts();
761        Self::from_request_parts(&mut parts, state)
762    }
763}
764
765/// Extractor for query parameters. Requires the `query` feature.
766#[cfg(feature = "query")]
767#[derive(Debug, Clone)]
768pub struct Query<T>(pub T);
769
770#[cfg(feature = "query")]
771impl<S, T> FromRequestParts<S> for Query<T>
772where
773    T: DeserializeOwned + Send + Sync + 'static,
774{
775    type Rejection = Error;
776
777    fn from_request_parts(
778        parts: &mut hyper::http::request::Parts,
779        _state: &S,
780    ) -> Result<Self, Self::Rejection> {
781        let query_str = parts.uri.query().unwrap_or("");
782        let iter = QueryIter { input: query_str };
783        let map_de = serde::de::value::MapDeserializer::new(iter);
784        T::deserialize(map_de)
785            .map(Query)
786            .map_err(|e: serde::de::value::Error| Error::Rejection {
787                status: StatusCode::BAD_REQUEST,
788                message: format!("Failed to deserialize query parameters: {e}"),
789            })
790    }
791}
792
793#[cfg(feature = "query")]
794impl<S, T> FromRequest<S> for Query<T>
795where
796    S: Sync,
797    T: DeserializeOwned + Send + Sync + 'static,
798{
799    type Rejection = Error;
800
801    async fn from_request(req: hyper::Request<Body>, state: &S) -> Result<Self, Self::Rejection> {
802        let (mut parts, _) = req.into_parts();
803        Self::from_request_parts(&mut parts, state)
804    }
805}
806
807/// Extracts the raw, un-deserialized query string (`None` if the request has
808/// none), matching `axum::extract::RawQuery`. Infallible — unlike [`Query`],
809/// this never rejects, since it does no parsing at all.
810#[derive(Debug, Clone)]
811pub struct RawQuery(pub Option<String>);
812
813impl<S> FromRequestParts<S> for RawQuery {
814    type Rejection = std::convert::Infallible;
815
816    fn from_request_parts(
817        parts: &mut hyper::http::request::Parts,
818        _state: &S,
819    ) -> Result<Self, Self::Rejection> {
820        Ok(Self(parts.uri.query().map(str::to_string)))
821    }
822}
823
824/// Returns `true` if `content_type` denotes a JSON media type, matching Axum's check:
825/// the type must be `application` and the subtype must be `json` or end in `+json`
826/// (e.g. `application/json`, `application/json; charset=utf-8`, `application/vnd.api+json`).
827#[cfg(feature = "json")]
828fn is_json_content_type(content_type: &str) -> bool {
829    let essence = content_type.split(';').next().unwrap_or("").trim();
830    let Some((ty, subtype)) = essence.split_once('/') else {
831        return false;
832    };
833    if !ty.eq_ignore_ascii_case("application") {
834        return false;
835    }
836    subtype.eq_ignore_ascii_case("json") || subtype.to_ascii_lowercase().ends_with("+json")
837}
838
839/// Extractor for JSON payloads. Requires the `json` feature.
840#[cfg(feature = "json")]
841#[derive(Debug, Clone)]
842pub struct Json<T>(pub T);
843
844#[cfg(feature = "json")]
845impl<S, T> FromRequest<S> for Json<T>
846where
847    S: Sync,
848    T: DeserializeOwned + Send + Sync + 'static,
849{
850    type Rejection = Error;
851
852    async fn from_request(req: hyper::Request<Body>, _state: &S) -> Result<Self, Self::Rejection> {
853        // Validate Content-Type: must be a JSON media type (`application/json`, optionally
854        // with parameters, or any `application/*+json` vendor/suffix type).
855        let ct = req
856            .headers()
857            .get(hyper::header::CONTENT_TYPE)
858            .and_then(|v| v.to_str().ok())
859            .unwrap_or("");
860        if !is_json_content_type(ct) {
861            return Err(Error::Rejection {
862                status: StatusCode::UNSUPPORTED_MEDIA_TYPE,
863                message: format!("Expected Content-Type: application/json, got: '{ct}'"),
864            });
865        }
866        let limit = max_body_size(req.extensions());
867        let body = req.into_body().collect_bytes(limit).await?;
868        serde_json::from_slice::<T>(&body).map(Json).map_err(|e| {
869            // Matches Axum's `JsonRejection`: malformed JSON (unbalanced braces,
870            // trailing commas, invalid escapes, truncated input, ...) is a client
871            // syntax error (`400`), while well-formed JSON that doesn't match the
872            // target type's shape (wrong field types, missing required fields) is
873            // `422` — the payload was understood but semantically rejected.
874            let status = match e.classify() {
875                serde_json::error::Category::Syntax | serde_json::error::Category::Eof => {
876                    StatusCode::BAD_REQUEST
877                }
878                serde_json::error::Category::Data | serde_json::error::Category::Io => {
879                    StatusCode::UNPROCESSABLE_ENTITY
880                }
881            };
882            Error::Rejection {
883                status,
884                message: format!("Failed to deserialize JSON payload: {e}"),
885            }
886        })
887    }
888}
889
890impl<S> FromRequestParts<S> for HeaderMap {
891    type Rejection = Infallible;
892
893    fn from_request_parts(
894        parts: &mut hyper::http::request::Parts,
895        _state: &S,
896    ) -> Result<Self, Self::Rejection> {
897        Ok(parts.headers.clone())
898    }
899}
900
901impl<S> FromRequestParts<S> for Method {
902    type Rejection = Infallible;
903
904    fn from_request_parts(
905        parts: &mut hyper::http::request::Parts,
906        _state: &S,
907    ) -> Result<Self, Self::Rejection> {
908        Ok(parts.method.clone())
909    }
910}
911
912impl<S> FromRequestParts<S> for Uri {
913    type Rejection = Infallible;
914
915    fn from_request_parts(
916        parts: &mut hyper::http::request::Parts,
917        _state: &S,
918    ) -> Result<Self, Self::Rejection> {
919        Ok(parts.uri.clone())
920    }
921}
922
923impl<S: Sync> FromRequest<S> for Bytes {
924    type Rejection = Error;
925
926    async fn from_request(req: hyper::Request<Body>, _state: &S) -> Result<Self, Self::Rejection> {
927        let limit = max_body_size(req.extensions());
928        req.into_body().collect_bytes(limit).await
929    }
930}
931
932impl<S: Sync> FromRequest<S> for String {
933    type Rejection = Error;
934
935    async fn from_request(req: hyper::Request<Body>, _state: &S) -> Result<Self, Self::Rejection> {
936        let limit = max_body_size(req.extensions());
937        let body = req.into_body().collect_bytes(limit).await?;
938        Self::from_utf8(body.to_vec()).map_err(|e| Error::Rejection {
939            status: StatusCode::BAD_REQUEST,
940            message: format!("Request body is not valid UTF-8: {e}"),
941        })
942    }
943}
944
945/// Extractor for form-urlencoded payloads. Requires the `form` feature.
946#[cfg(feature = "form")]
947#[derive(Debug, Clone)]
948pub struct Form<T>(pub T);
949
950#[cfg(feature = "form")]
951impl<S, T> FromRequestParts<S> for Form<T>
952where
953    T: DeserializeOwned + Send + Sync + 'static,
954{
955    type Rejection = Error;
956
957    /// Deserializes from the URL query string — matches Axum's `Form` extractor,
958    /// which reads `GET`/`HEAD` requests from the query string rather than the
959    /// (typically absent) body. See [`FromRequest`] for the `POST`/body path.
960    fn from_request_parts(
961        parts: &mut hyper::http::request::Parts,
962        _state: &S,
963    ) -> Result<Self, Self::Rejection> {
964        let query_str = parts.uri.query().unwrap_or("");
965        let iter = QueryIter { input: query_str };
966        let map_de = serde::de::value::MapDeserializer::new(iter);
967        T::deserialize(map_de)
968            .map(Form)
969            .map_err(|e: serde::de::value::Error| Error::Rejection {
970                status: StatusCode::UNPROCESSABLE_ENTITY,
971                message: format!("Failed to deserialize form payload: {e}"),
972            })
973    }
974}
975
976#[cfg(feature = "form")]
977impl<S, T> FromRequest<S> for Form<T>
978where
979    S: Sync,
980    T: DeserializeOwned + Send + Sync + 'static,
981{
982    type Rejection = Error;
983
984    async fn from_request(req: hyper::Request<Body>, state: &S) -> Result<Self, Self::Rejection> {
985        // Matches Axum: `GET`/`HEAD` requests are read from the query string (no
986        // body/Content-Type expected — this is the common "search form" pattern);
987        // every other method reads and deserializes the request body.
988        if req.method() == hyper::Method::GET || req.method() == hyper::Method::HEAD {
989            let (mut parts, _body) = req.into_parts();
990            return Self::from_request_parts(&mut parts, state);
991        }
992
993        // Validate Content-Type: must be application/x-www-form-urlencoded.
994        let ct = req
995            .headers()
996            .get(hyper::header::CONTENT_TYPE)
997            .and_then(|v| v.to_str().ok())
998            .unwrap_or("");
999        let essence = ct.split(';').next().unwrap_or("").trim();
1000        if !essence.eq_ignore_ascii_case("application/x-www-form-urlencoded") {
1001            return Err(Error::Rejection {
1002                status: StatusCode::UNSUPPORTED_MEDIA_TYPE,
1003                message: format!(
1004                    "Expected Content-Type: application/x-www-form-urlencoded, got: '{ct}'"
1005                ),
1006            });
1007        }
1008        let limit = max_body_size(req.extensions());
1009        let body = req.into_body().collect_bytes(limit).await?;
1010        let body_str = std::str::from_utf8(&body).map_err(|_| Error::Rejection {
1011            status: StatusCode::BAD_REQUEST,
1012            message: "Form body is not valid UTF-8".to_string(),
1013        })?;
1014        let iter = QueryIter { input: body_str };
1015        let map_de = serde::de::value::MapDeserializer::new(iter);
1016        T::deserialize(map_de)
1017            .map(Form)
1018            .map_err(|e: serde::de::value::Error| Error::Rejection {
1019                status: StatusCode::UNPROCESSABLE_ENTITY,
1020                message: format!("Failed to deserialize form payload: {e}"),
1021            })
1022    }
1023}
1024
1025/// Extractor for request-local extensions.
1026#[derive(Debug, Clone, Copy)]
1027pub struct Extension<T>(pub T);
1028
1029impl<S, T> FromRequestParts<S> for Extension<T>
1030where
1031    T: Clone + Send + Sync + 'static,
1032{
1033    type Rejection = Error;
1034
1035    fn from_request_parts(
1036        parts: &mut hyper::http::request::Parts,
1037        _state: &S,
1038    ) -> Result<Self, Self::Rejection> {
1039        parts
1040            .extensions
1041            .get::<T>()
1042            .cloned()
1043            .map(Extension)
1044            .ok_or_else(|| Error::Rejection {
1045                status: StatusCode::INTERNAL_SERVER_ERROR,
1046                message: format!("Missing extension: {}", std::any::type_name::<T>()),
1047            })
1048    }
1049}
1050
1051impl<S, T> FromRequest<S> for Extension<T>
1052where
1053    S: Sync,
1054    T: Clone + Send + Sync + 'static,
1055{
1056    type Rejection = Error;
1057
1058    async fn from_request(req: hyper::Request<Body>, state: &S) -> Result<Self, Self::Rejection> {
1059        let (mut parts, _) = req.into_parts();
1060        Self::from_request_parts(&mut parts, state)
1061    }
1062}
1063
1064/// Extractor for reading and managing Cookies.
1065#[cfg(feature = "cookies")]
1066#[derive(Debug, Clone)]
1067pub struct Cookies {
1068    /// The internal cookie jar
1069    pub jar: CookieJar,
1070}
1071
1072#[cfg(feature = "cookies")]
1073impl Cookies {
1074    /// Create a new empty Cookies jar.
1075    #[must_use]
1076    pub fn new() -> Self {
1077        Self {
1078            jar: CookieJar::new(),
1079        }
1080    }
1081
1082    /// Get a cookie by name.
1083    #[must_use]
1084    pub fn get(&self, name: &str) -> Option<&Cookie<'static>> {
1085        self.jar.get(name)
1086    }
1087
1088    /// Adds `cookie` to the jar, returning `Self` for chaining — the request handler pattern is
1089    /// `async fn handler(jar: Cookies) -> (Cookies, T) { (jar.add(...), body) }`, matching
1090    /// `axum-extra`'s `CookieJar`. Returning the jar from a handler (anywhere in an
1091    /// [`IntoResponseParts`](crate::http::response::IntoResponseParts) tuple) is what actually
1092    /// applies it — only the cookies that changed (added or removed) are serialized into
1093    /// `Set-Cookie` headers, via [`cookie::CookieJar::delta`], not the whole jar.
1094    // Named to match `axum-extra`'s `CookieJar::add` exactly (the point of this method), not
1095    // `std::ops::Add` — the two aren't actually confusable in practice (different arity/purpose).
1096    #[allow(clippy::should_implement_trait)]
1097    #[must_use]
1098    pub fn add(mut self, cookie: Cookie<'static>) -> Self {
1099        self.jar.add(cookie);
1100        self
1101    }
1102
1103    /// Removes `cookie` from the jar (queuing a `Set-Cookie` that expires it immediately once
1104    /// this jar is returned from a handler), returning `Self` for chaining — see [`add`](Self::add).
1105    #[must_use]
1106    pub fn remove(mut self, cookie: Cookie<'static>) -> Self {
1107        self.jar.remove(cookie);
1108        self
1109    }
1110}
1111
1112#[cfg(feature = "cookies")]
1113impl Default for Cookies {
1114    fn default() -> Self {
1115        Self::new()
1116    }
1117}
1118
1119#[cfg(feature = "cookies")]
1120impl<S> FromRequestParts<S> for Cookies {
1121    type Rejection = Infallible;
1122
1123    fn from_request_parts(
1124        parts: &mut hyper::http::request::Parts,
1125        _state: &S,
1126    ) -> Result<Self, Self::Rejection> {
1127        let mut jar = CookieJar::new();
1128        if let Some(cookie_header) = parts.headers.get(hyper::header::COOKIE)
1129            && let Ok(cookie_str) = cookie_header.to_str()
1130        {
1131            for c in Cookie::split_parse_encoded(cookie_str).flatten() {
1132                jar.add_original(c.into_owned());
1133            }
1134        }
1135        Ok(Self { jar })
1136    }
1137}
1138
1139impl<S: Sync> FromRequest<S> for hyper::Request<Bytes> {
1140    type Rejection = Error;
1141
1142    async fn from_request(req: hyper::Request<Body>, _state: &S) -> Result<Self, Self::Rejection> {
1143        let limit = max_body_size(req.extensions());
1144        let (parts, body) = req.into_parts();
1145        let bytes = body.collect_bytes(limit).await?;
1146        Ok(Self::from_parts(parts, bytes))
1147    }
1148}
1149
1150/// Extractor providing direct, un-buffered access to the request body as a
1151/// stream — for handlers that want to process large uploads incrementally
1152/// instead of buffering the whole body into memory first.
1153///
1154/// Unlike `Bytes`, `String`, `Json`, and `Form`, this never allocates a single
1155/// contiguous buffer for the body and is not subject to [`crate::server::Server::max_body_size`]
1156/// — callers reading from the stream are responsible for enforcing their own limits.
1157#[derive(Debug)]
1158pub struct BodyStream(pub Body);
1159
1160impl<S: Sync> FromRequest<S> for BodyStream {
1161    type Rejection = Infallible;
1162
1163    async fn from_request(req: hyper::Request<Body>, _state: &S) -> Result<Self, Self::Rejection> {
1164        Ok(Self(req.into_body()))
1165    }
1166}
1167
1168impl<S: Sync> FromRequest<S> for hyper::Request<Body> {
1169    type Rejection = Infallible;
1170
1171    async fn from_request(req: hyper::Request<Body>, _state: &S) -> Result<Self, Self::Rejection> {
1172        Ok(req)
1173    }
1174}
1175
1176/// Extractor for host header or authority.
1177#[derive(Debug, Clone)]
1178pub struct Host(pub String);
1179
1180impl<S> FromRequestParts<S> for Host {
1181    type Rejection = Error;
1182
1183    fn from_request_parts(
1184        parts: &mut hyper::http::request::Parts,
1185        _state: &S,
1186    ) -> Result<Self, Self::Rejection> {
1187        if let Some(host) = parts
1188            .headers
1189            .get(hyper::header::HOST)
1190            .and_then(|h| h.to_str().ok())
1191        {
1192            Ok(Self(host.to_string()))
1193        } else if let Some(host) = parts.uri.host() {
1194            Ok(Self(host.to_string()))
1195        } else {
1196            Err(Error::Rejection {
1197                status: StatusCode::BAD_REQUEST,
1198                message: "Missing Host header or authority in URI".to_string(),
1199            })
1200        }
1201    }
1202}
1203
1204/// Extractor for the original URI. Requires the `original-uri` feature.
1205#[cfg(feature = "original-uri")]
1206#[derive(Debug, Clone)]
1207pub struct OriginalUri(pub Uri);
1208
1209#[cfg(feature = "original-uri")]
1210impl<S> FromRequestParts<S> for OriginalUri {
1211    type Rejection = Infallible;
1212
1213    fn from_request_parts(
1214        parts: &mut hyper::http::request::Parts,
1215        _state: &S,
1216    ) -> Result<Self, Self::Rejection> {
1217        let uri = parts
1218            .extensions
1219            .get::<Self>()
1220            .map_or_else(|| parts.uri.clone(), |ou| ou.0.clone());
1221        Ok(Self(uri))
1222    }
1223}
1224
1225/// Extractor for the matched route pattern (e.g. `/users/{id}`), as registered
1226/// via `Router::route`, rather than the literal request path (`/users/1`).
1227///
1228/// Matches `axum::extract::MatchedPath` — commonly used to label metrics/traces
1229/// by route template instead of by concrete path (which would otherwise create
1230/// one time series per distinct resource ID). Only available for requests that
1231/// matched a registered route; unmatched requests (404s) have no `MatchedPath`.
1232/// Requires the `matched-path` feature.
1233#[cfg(feature = "matched-path")]
1234#[derive(Debug, Clone)]
1235pub struct MatchedPath(pub(crate) std::sync::Arc<str>);
1236
1237#[cfg(feature = "matched-path")]
1238impl MatchedPath {
1239    /// The matched route pattern, e.g. `/users/{id}`.
1240    #[must_use]
1241    pub fn as_str(&self) -> &str {
1242        &self.0
1243    }
1244}
1245
1246#[cfg(feature = "matched-path")]
1247impl<S> FromRequestParts<S> for MatchedPath {
1248    type Rejection = Error;
1249
1250    fn from_request_parts(
1251        parts: &mut hyper::http::request::Parts,
1252        _state: &S,
1253    ) -> Result<Self, Self::Rejection> {
1254        parts
1255            .extensions
1256            .get::<Self>()
1257            .cloned()
1258            .ok_or_else(|| Error::Rejection {
1259                status: StatusCode::INTERNAL_SERVER_ERROR,
1260                message: "No matched path found in request extensions".to_string(),
1261            })
1262    }
1263}
1264
1265/// Extractor for network connection info.
1266#[derive(Debug, Clone, Copy)]
1267pub struct ConnectInfo<T>(pub T);
1268
1269impl<S, T> FromRequestParts<S> for ConnectInfo<T>
1270where
1271    T: Clone + Send + Sync + 'static,
1272{
1273    type Rejection = Error;
1274
1275    fn from_request_parts(
1276        parts: &mut hyper::http::request::Parts,
1277        _state: &S,
1278    ) -> Result<Self, Self::Rejection> {
1279        parts
1280            .extensions
1281            .get::<Self>()
1282            .cloned()
1283            .ok_or_else(|| Error::Rejection {
1284                status: StatusCode::INTERNAL_SERVER_ERROR,
1285                message: format!(
1286                    "Missing ConnectInfo<{}> extension",
1287                    std::any::type_name::<T>()
1288                ),
1289            })
1290    }
1291}
1292
1293impl<S, T> FromRequest<S> for ConnectInfo<T>
1294where
1295    S: Sync,
1296    T: Clone + Send + Sync + 'static,
1297{
1298    type Rejection = Error;
1299
1300    async fn from_request(req: hyper::Request<Body>, state: &S) -> Result<Self, Self::Rejection> {
1301        let (mut parts, _) = req.into_parts();
1302        Self::from_request_parts(&mut parts, state)
1303    }
1304}
1305
1306macro_rules! impl_from_request_via_parts {
1307    ($ty:ty) => {
1308        impl<S: Sync> FromRequest<S> for $ty {
1309            type Rejection = <Self as FromRequestParts<S>>::Rejection;
1310
1311            async fn from_request(
1312                req: hyper::Request<Body>,
1313                state: &S,
1314            ) -> Result<Self, Self::Rejection> {
1315                let (mut parts, _) = req.into_parts();
1316                <Self as FromRequestParts<S>>::from_request_parts(&mut parts, state)
1317            }
1318        }
1319    };
1320}
1321
1322impl_from_request_via_parts!(RawQuery);
1323impl_from_request_via_parts!(HeaderMap);
1324impl_from_request_via_parts!(Method);
1325impl_from_request_via_parts!(Uri);
1326#[cfg(feature = "cookies")]
1327impl_from_request_via_parts!(Cookies);
1328impl_from_request_via_parts!(Host);
1329#[cfg(feature = "original-uri")]
1330impl_from_request_via_parts!(OriginalUri);
1331#[cfg(feature = "matched-path")]
1332impl_from_request_via_parts!(MatchedPath);
1333
1334#[cfg(test)]
1335mod tests {
1336    #![allow(clippy::unwrap_used)]
1337    use super::*;
1338    use hyper::http::Request;
1339    use serde::Deserialize;
1340
1341    #[derive(Deserialize, Debug)]
1342    #[allow(clippy::struct_excessive_bools)]
1343    struct BigCoerce {
1344        a: u16,
1345        b: u64,
1346        c: i8,
1347        d: i16,
1348        e: i32,
1349        f: i64,
1350        g: f32,
1351        h: f64,
1352        i: bool,
1353        j: bool,
1354        k: bool,
1355        l: bool,
1356    }
1357
1358    #[derive(Deserialize)]
1359    #[allow(dead_code)]
1360    struct BoolTest {
1361        val: bool,
1362    }
1363
1364    #[derive(Deserialize, PartialEq, Debug)]
1365    enum Color {
1366        Red,
1367        Blue,
1368    }
1369
1370    #[derive(Deserialize)]
1371    struct EnumTest {
1372        val: Color,
1373    }
1374
1375    #[cfg(any(feature = "query", feature = "form"))]
1376    #[test]
1377    fn test_coercing_cow_deserializer() {
1378        let query_str = "a=12&b=34&c=5&d=6&e=7&f=8&g=1.2&h=3.4&i=true&j=1&k=false&l=0";
1379        let iter = QueryIter { input: query_str };
1380        let map_de = serde::de::value::MapDeserializer::new(iter);
1381        let data = BigCoerce::deserialize(map_de).unwrap();
1382        assert_eq!(data.a, 12);
1383        assert_eq!(data.b, 34);
1384        assert_eq!(data.c, 5);
1385        assert_eq!(data.d, 6);
1386        assert_eq!(data.e, 7);
1387        assert_eq!(data.f, 8);
1388        assert!((data.g - 1.2).abs() < 0.001);
1389        assert!((data.h - 3.4).abs() < 0.001);
1390        assert!(data.i);
1391        assert!(data.j);
1392        assert!(!data.k);
1393        assert!(!data.l);
1394
1395        // Test bool parse error
1396        let iter = QueryIter {
1397            input: "val=not_bool",
1398        };
1399        let map_de = serde::de::value::MapDeserializer::new(iter);
1400        assert!(BoolTest::deserialize(map_de).is_err());
1401
1402        // Test enum deserialization
1403        let iter = QueryIter { input: "val=Red" };
1404        let map_de = serde::de::value::MapDeserializer::new(iter);
1405        let et = EnumTest::deserialize(map_de).unwrap();
1406        assert_eq!(et.val, Color::Red);
1407    }
1408
1409    #[cfg(any(feature = "query", feature = "form"))]
1410    #[test]
1411    fn test_query_iter_edge_cases() {
1412        // Empty pair and key without value
1413        let query_str = "&&foo&&bar=baz";
1414        let mut iter = QueryIter { input: query_str };
1415        let first = iter.next().unwrap();
1416        assert_eq!(first.0, "foo");
1417        assert_eq!(first.1.val, "");
1418        let second = iter.next().unwrap();
1419        assert_eq!(second.0, "bar");
1420        assert_eq!(second.1.val, "baz");
1421
1422        // Invalid percent decoding in query param
1423        let query_str2 = "foo=bar%xy&baz=%";
1424        let mut iter2 = QueryIter { input: query_str2 };
1425        let first2 = iter2.next().unwrap();
1426        assert_eq!(first2.0, "foo");
1427        assert_eq!(first2.1.val, "bar%xy");
1428        let second2 = iter2.next().unwrap();
1429        assert_eq!(second2.0, "baz");
1430        assert_eq!(second2.1.val, "%");
1431    }
1432
1433    #[tokio::test]
1434    async fn test_extractors_direct() {
1435        let req = Request::builder()
1436            .method("POST")
1437            .uri("/path?q=1")
1438            .header("x-test", "hello")
1439            .body(Body::full(Bytes::from("body_bytes")))
1440            .unwrap();
1441        let (mut parts, body) = req.into_parts();
1442
1443        // HeaderMap
1444        let headers = HeaderMap::from_request_parts(&mut parts, &()).unwrap();
1445        assert_eq!(headers.get("x-test").unwrap(), "hello");
1446
1447        // Method
1448        let method = Method::from_request_parts(&mut parts, &()).unwrap();
1449        assert_eq!(method, "POST");
1450
1451        // Uri
1452        let uri = Uri::from_request_parts(&mut parts, &()).unwrap();
1453        assert_eq!(uri.path(), "/path");
1454
1455        // Bytes
1456        let req_bytes = Request::from_parts(parts.clone(), Body::full(Bytes::from("body_bytes")));
1457        let bytes = Bytes::from_request(req_bytes, &()).await.unwrap();
1458        assert_eq!(bytes.as_ref(), b"body_bytes");
1459
1460        // Request<Bytes>
1461        let req_full = Request::from_parts(parts, body);
1462        let extracted_req = <Request<Bytes>>::from_request(req_full, &()).await.unwrap();
1463        assert_eq!(extracted_req.uri().path(), "/path");
1464    }
1465
1466    #[cfg(feature = "cookies")]
1467    #[test]
1468    fn test_cookies_remove() {
1469        use cookie::Cookie;
1470        let cookies = Cookies::new().add(Cookie::new("foo", "bar"));
1471        assert_eq!(cookies.get("foo").unwrap().value(), "bar");
1472        let cookies = cookies.remove(Cookie::new("foo", ""));
1473        assert!(cookies.get("foo").is_none());
1474    }
1475
1476    #[test]
1477    fn test_host_missing() {
1478        let mut parts = Request::builder().uri("/").body(()).unwrap().into_parts().0;
1479        let res = Host::from_request_parts(&mut parts, &());
1480        assert!(res.is_err());
1481    }
1482
1483    #[test]
1484    fn test_connect_info_missing() {
1485        let mut parts = Request::builder().uri("/").body(()).unwrap().into_parts().0;
1486        let res = ConnectInfo::<std::net::SocketAddr>::from_request_parts(&mut parts, &());
1487        assert!(res.is_err());
1488    }
1489
1490    #[cfg(feature = "form")]
1491    #[tokio::test]
1492    async fn test_form_errors() {
1493        #[derive(Deserialize, Debug)]
1494        #[allow(dead_code)]
1495        struct FormPayload {
1496            foo: String,
1497        }
1498
1499        // Invalid content type. Method must be POST (or any non-GET/HEAD) — otherwise
1500        // `Form::from_request` silently delegates to the query-string path instead of ever
1501        // reaching the Content-Type check below, which is exactly the bug this test used to
1502        // have (all three sub-cases here defaulted to GET and accidentally exercised the
1503        // wrong branch entirely; the `is_err()` assertions still passed, just for the wrong
1504        // reason — a missing required field via an empty query string).
1505        let req = Request::builder()
1506            .method("POST")
1507            .header(hyper::header::CONTENT_TYPE, "text/plain")
1508            .body(Body::full(Bytes::from("foo=bar")))
1509            .unwrap();
1510        let res = Form::<FormPayload>::from_request(req, &()).await;
1511        assert!(res.is_err());
1512
1513        // Invalid UTF-8 body.
1514        let utf8_req = Request::builder()
1515            .method("POST")
1516            .header(
1517                hyper::header::CONTENT_TYPE,
1518                "application/x-www-form-urlencoded",
1519            )
1520            .body(Body::full(Bytes::from(vec![0xff, 0xff])))
1521            .unwrap();
1522        let utf8_result = Form::<FormPayload>::from_request(utf8_req, &()).await;
1523        assert!(utf8_result.is_err());
1524
1525        // Invalid payload (missing required field).
1526        let payload_req = Request::builder()
1527            .method("POST")
1528            .header(
1529                hyper::header::CONTENT_TYPE,
1530                "application/x-www-form-urlencoded",
1531            )
1532            .body(Body::full(Bytes::from("not_valid")))
1533            .unwrap();
1534        let payload_result = Form::<FormPayload>::from_request(payload_req, &()).await;
1535        assert!(payload_result.is_err());
1536    }
1537
1538    #[cfg(feature = "form")]
1539    #[test]
1540    fn test_form_from_request_parts_deserialize_error() {
1541        #[derive(Deserialize, Debug)]
1542        #[allow(dead_code)]
1543        struct FormPayload {
1544            foo: String,
1545        }
1546
1547        // The GET/HEAD "read from the query string" path (`FromRequestParts`), exercised
1548        // directly rather than via the `FromRequest::from_request` GET delegation, so it's
1549        // clear which branch is under test.
1550        let mut parts = Request::builder()
1551            .uri("/search?bar=baz")
1552            .body(())
1553            .unwrap()
1554            .into_parts()
1555            .0;
1556        let result = Form::<FormPayload>::from_request_parts(&mut parts, &());
1557        assert!(result.is_err());
1558    }
1559
1560    #[cfg(feature = "form")]
1561    #[tokio::test]
1562    async fn test_form_get_request_reads_from_query_string() {
1563        #[derive(Deserialize, Debug, PartialEq)]
1564        struct FormPayload {
1565            foo: String,
1566        }
1567
1568        let req = Request::builder()
1569            .method("GET")
1570            .uri("/search?foo=bar")
1571            .body(Body::empty())
1572            .unwrap();
1573        let Form(payload) = Form::<FormPayload>::from_request(req, &()).await.unwrap();
1574        assert_eq!(
1575            payload,
1576            FormPayload {
1577                foo: "bar".to_string(),
1578            }
1579        );
1580    }
1581
1582    #[cfg(feature = "query")]
1583    #[test]
1584    fn test_query_deserialize_error() {
1585        #[derive(Deserialize, Debug)]
1586        #[allow(dead_code)]
1587        struct QueryPayload {
1588            foo: u32,
1589        }
1590
1591        let mut parts = Request::builder()
1592            .uri("/?foo=not_a_number")
1593            .body(())
1594            .unwrap()
1595            .into_parts()
1596            .0;
1597        let result = Query::<QueryPayload>::from_request_parts(&mut parts, &());
1598        assert!(result.is_err());
1599    }
1600
1601    #[test]
1602    fn test_raw_query_present_and_absent() {
1603        let mut with_query = Request::builder()
1604            .uri("/path?a=1&b=2")
1605            .body(())
1606            .unwrap()
1607            .into_parts()
1608            .0;
1609        let RawQuery(q) = RawQuery::from_request_parts(&mut with_query, &()).unwrap();
1610        assert_eq!(q.as_deref(), Some("a=1&b=2"));
1611
1612        let mut without_query = Request::builder()
1613            .uri("/path")
1614            .body(())
1615            .unwrap()
1616            .into_parts()
1617            .0;
1618        let RawQuery(q2) = RawQuery::from_request_parts(&mut without_query, &()).unwrap();
1619        assert!(q2.is_none());
1620    }
1621
1622    #[cfg(feature = "cookies")]
1623    #[test]
1624    fn test_cookies_default() {
1625        let cookies = Cookies::default();
1626        assert!(cookies.get("anything").is_none());
1627    }
1628
1629    #[tokio::test]
1630    async fn test_body_stream_from_request() {
1631        let req = Request::builder()
1632            .body(Body::full(Bytes::from("stream me")))
1633            .unwrap();
1634        let BodyStream(body) = BodyStream::from_request(req, &()).await.unwrap();
1635        let collected = body.collect_bytes(1024).await.unwrap();
1636        assert_eq!(collected.as_ref(), b"stream me");
1637    }
1638
1639    #[cfg(feature = "json")]
1640    #[test]
1641    fn test_is_json_content_type_without_a_slash_is_rejected() {
1642        assert!(!is_json_content_type("not-a-media-type"));
1643    }
1644
1645    // --- PathDeserializer coverage ---
1646
1647    fn make_path_parts(params: Vec<(&str, &str)>) -> hyper::http::request::Parts {
1648        let mut parts = Request::builder().body(()).unwrap().into_parts().0;
1649        let path_params = PathParams(
1650            params
1651                .into_iter()
1652                .map(|(k, v)| (std::sync::Arc::from(k), v.to_string()))
1653                .collect(),
1654        );
1655        parts.extensions.insert(path_params);
1656        parts
1657    }
1658
1659    #[test]
1660    fn test_path_tuple_success_and_length_mismatch() {
1661        let mut ok_parts = make_path_parts(vec![("id", "42"), ("name", "hello")]);
1662        let Path((id, name)) =
1663            Path::<(u32, String)>::from_request_parts(&mut ok_parts, &()).unwrap();
1664        assert_eq!(id, 42);
1665        assert_eq!(name, "hello");
1666
1667        // Too many params for a 2-tuple.
1668        let mut too_many = make_path_parts(vec![("a", "1"), ("b", "2"), ("c", "3")]);
1669        assert!(Path::<(u32, String)>::from_request_parts(&mut too_many, &()).is_err());
1670
1671        // Too few params for a 2-tuple.
1672        let mut too_few = make_path_parts(vec![("a", "1")]);
1673        assert!(Path::<(u32, String)>::from_request_parts(&mut too_few, &()).is_err());
1674    }
1675
1676    #[test]
1677    fn test_path_vec_seq_target() {
1678        // `Vec<T>` reaches `deserialize_seq` directly (not via tuple delegation),
1679        // and its `Deserialize` impl calls `SeqAccess::size_hint` to preallocate.
1680        let mut parts = make_path_parts(vec![("a", "x"), ("b", "y"), ("c", "z")]);
1681        let Path(values) = Path::<Vec<String>>::from_request_parts(&mut parts, &()).unwrap();
1682        assert_eq!(
1683            values,
1684            vec!["x".to_string(), "y".to_string(), "z".to_string()]
1685        );
1686    }
1687
1688    #[test]
1689    fn test_path_scalar_wrong_param_count() {
1690        // Zero params for a bare scalar target.
1691        let mut zero = make_path_parts(vec![]);
1692        assert!(Path::<u32>::from_request_parts(&mut zero, &()).is_err());
1693
1694        // More than one param for a bare scalar target.
1695        let mut two = make_path_parts(vec![("a", "1"), ("b", "2")]);
1696        assert!(Path::<u32>::from_request_parts(&mut two, &()).is_err());
1697
1698        // Exactly one param succeeds.
1699        let mut one = make_path_parts(vec![("id", "7")]);
1700        let Path(v) = Path::<u32>::from_request_parts(&mut one, &()).unwrap();
1701        assert_eq!(v, 7);
1702    }
1703
1704    #[test]
1705    fn test_path_option_top_level_target() {
1706        // `Path<Option<T>>` makes `Option<T>` the *whole* deserialization target, so
1707        // `T::deserialize` dispatches straight to `PathDeserializer::deserialize_option`
1708        // (as opposed to a struct field being `Option<T>`, which is handled entirely by
1709        // `MapDeserializer`/`CoercingCowDeserializer` without ever calling back into
1710        // `PathDeserializer::deserialize_option`).
1711        let mut parts = make_path_parts(vec![("id", "9")]);
1712        let Path(v) = Path::<Option<u32>>::from_request_parts(&mut parts, &()).unwrap();
1713        assert_eq!(v, Some(9));
1714    }
1715
1716    #[test]
1717    fn test_path_enum_target() {
1718        let mut parts = make_path_parts(vec![("color", "Red")]);
1719        let Path(c) = Path::<Color>::from_request_parts(&mut parts, &()).unwrap();
1720        assert_eq!(c, Color::Red);
1721    }
1722
1723    #[test]
1724    fn test_path_unit_and_unit_struct_targets() {
1725        #[derive(Deserialize, PartialEq, Debug)]
1726        struct UnitStruct;
1727
1728        // `()` as the whole target reaches `deserialize_unit` and ignores any params.
1729        let mut parts = make_path_parts(vec![("a", "1"), ("b", "2")]);
1730        let Path(unit_val) = Path::<()>::from_request_parts(&mut parts, &()).unwrap();
1731        assert_eq!(unit_val, ());
1732
1733        // A derived unit struct reaches `deserialize_unit_struct`.
1734        let mut empty_parts = make_path_parts(vec![]);
1735        let Path(u) = Path::<UnitStruct>::from_request_parts(&mut empty_parts, &()).unwrap();
1736        assert_eq!(u, UnitStruct);
1737    }
1738
1739    #[test]
1740    fn test_path_newtype_struct_target() {
1741        #[derive(Deserialize, PartialEq, Debug)]
1742        struct Wrapper(u32);
1743
1744        let mut parts = make_path_parts(vec![("id", "77")]);
1745        let Path(Wrapper(v)) = Path::<Wrapper>::from_request_parts(&mut parts, &()).unwrap();
1746        assert_eq!(v, 77);
1747    }
1748
1749    #[test]
1750    fn test_path_ignored_any_top_level_target() {
1751        // `serde::de::IgnoredAny` is a real, public serde type whose `Deserialize` impl
1752        // calls `deserialize_ignored_any` directly on the top-level deserializer, so this
1753        // exercises `PathDeserializer::deserialize_ignored_any` through the public `Path<T>`
1754        // API without any artificial scaffolding.
1755        let mut parts = make_path_parts(vec![("a", "1"), ("b", "2")]);
1756        let result = Path::<serde::de::IgnoredAny>::from_request_parts(&mut parts, &());
1757        assert!(result.is_ok());
1758    }
1759
1760    struct IdentifierVisitor;
1761
1762    impl serde::de::Visitor<'_> for IdentifierVisitor {
1763        type Value = String;
1764
1765        fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1766            formatter.write_str("a string identifier")
1767        }
1768
1769        fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
1770        where
1771            E: serde::de::Error,
1772        {
1773            Ok(v.to_string())
1774        }
1775    }
1776
1777    #[test]
1778    fn test_path_deserializer_identifier_direct() {
1779        // No public `Deserialize` target routes into `PathDeserializer::deserialize_identifier`
1780        // through `Path<T>`: struct/map field names are resolved by the key type inside
1781        // `MapDeserializer` (a `Cow<str>`/`StrDeserializer`), and enum variant names are
1782        // resolved via `val.into_deserializer()` in `deserialize_enum` above — neither ever
1783        // hands control back to `PathDeserializer` itself. So this calls the trait method
1784        // directly on the (module-private) `PathDeserializer` to exercise its forwarding
1785        // logic to `deserialize_str`.
1786        let params: Vec<(std::sync::Arc<str>, String)> =
1787            vec![(std::sync::Arc::from("k"), "myvalue".to_string())];
1788        let de = PathDeserializer { params: &params };
1789        let result =
1790            serde::de::Deserializer::deserialize_identifier(de, IdentifierVisitor).unwrap();
1791        assert_eq!(result, "myvalue");
1792    }
1793}