apimock_routing/parsed_request.rs
1//! The parsed form of an incoming HTTP request that the matcher consumes.
2//!
3//! # Why the struct lives in `apimock-routing` but the constructor doesn't
4//!
5//! `ParsedRequest` is what [`RuleSet::find_matched`](crate::RuleSet::find_matched)
6//! takes as input — so the matcher crate owns the type. But populating
7//! one from a `hyper::Request<Incoming>` requires reading the request
8//! body (async), looking at headers, and normalising the URL path.
9//! That logic is an HTTP-layer activity, so the constructor lives in
10//! `apimock-server::parsed_request`. Tests and benches can hand-build a
11//! `ParsedRequest` without going through the server code.
12
13use hyper::http::request::Parts;
14use serde_json::Value;
15
16/// Request metadata and body, decoded once per request.
17///
18/// # Why we eagerly collect the body
19///
20/// Routing decisions depend on body contents (rule-set `body.json`
21/// conditions, middleware evaluation). Rather than re-collecting the
22/// body each time a matcher asks for it, the upstream constructor
23/// consumes the `Incoming` stream once and keeps the parsed JSON
24/// around for the lifetime of the request. This is appropriate for a
25/// mock server where payloads are small; a production proxy would want
26/// streaming instead.
27#[derive(Debug)]
28pub struct ParsedRequest {
29 pub url_path: String,
30 pub component_parts: Parts,
31 /// Parsed JSON body, if the request had one that parsed successfully.
32 /// `None` here means either "no body" or "body present but not JSON";
33 /// the two are indistinguishable at the matcher layer and we don't
34 /// currently need to distinguish them.
35 pub body_json: Option<Value>,
36}