parlov docs

parlov-probe

HTTP execution layer — the Probe trait, HttpProbe implementation, timing capture, and extension points for custom clients.

Implemented

Version: 0.2.1 | Files: 2 | Lines: 164 | Dependencies: bytes, http, parlov-core, reqwest, tokio

The HTTP execution layer. This crate owns the network boundary — it sends requests and returns structured response surfaces. Everything else in the workspace is pure computation.


Public API

The Probe Trait

The central abstraction for HTTP execution. Consumers depend on the trait, not the concrete implementation.

pub trait Probe: Send + Sync {
    fn execute(
        &self,
        def: &ProbeDefinition,
    ) -> impl Future<Output = Result<ProbeExchange, Error>> + Send;
}

Contract:

  • Takes a ProbeDefinition (URL, method, headers, optional body)
  • Returns a ProbeExchange (the request paired with its ResponseSurface)
  • Captures timing (timing_ns) for potential statistical analysis
  • Must be Send + Sync for concurrent execution

HttpProbe

The production implementation of Probe, backed by reqwest::Client.

pub struct HttpProbe { /* reqwest::Client */ }

impl HttpProbe {
    /// Creates a new probe with default reqwest settings.
    pub fn new() -> Self;

    /// Creates a probe wrapping a caller-supplied reqwest::Client.
    /// Use this when you need custom TLS, proxy, or timeout settings.
    pub fn with_client(client: reqwest::Client) -> Self;
}

Execution flow:

  1. build_method() maps http::Method to reqwest::Method
  2. apply_headers() copies HeaderMap entries to the reqwest request builder
  3. Request is sent, timing is recorded via Instant::now() before/after
  4. convert_status() maps reqwest::StatusCode back to http::StatusCode
  5. convert_headers() maps reqwest::header::HeaderMap back to http::HeaderMap
  6. Response body is read fully into bytes::Bytes
  7. Everything is bundled into ProbeExchange { request, response }

Internal Modules

http module (src/http.rs)

Contains HttpProbe and four private helper functions:

FunctionPurpose
build_method(&http::Method) -> Result<reqwest::Method, Error>Maps the http crate's method type to reqwest's
apply_headers(RequestBuilder, &HeaderMap) -> Result<RequestBuilder, Error>Copies headers from the http crate format to reqwest format
convert_status(reqwest::StatusCode) -> Result<http::StatusCode, Error>Maps back to the http crate's status code type
convert_headers(&reqwest::header::HeaderMap) -> HeaderMapMaps back to the http crate's header map type

All four are private and exist only because reqwest and the http crate use different concrete types for the same concepts.


Design Decisions

Why a trait? The Probe trait exists so the scheduler and test harness can swap in mock implementations without touching the network. This is critical for snapshot testing — the test suite replays recorded ProbeExchange values without HTTP round-trips.

Why reqwest? reqwest provides TLS, HTTP/2, connection pooling, and proxy support out of the box. The with_client() constructor lets operators configure these settings without forking the crate.

Timing granularity. timing_ns uses std::time::Instant for wall-clock measurement. This is sufficient for statistical timing analysis but does not account for TLS handshake vs. server processing time separately.


Extension Points

Custom HTTP clients: Implement Probe for your own type. The trait uses RPITIT, so it is not dyn-compatible (Box<dyn Probe> does not compile). To erase the type, wrap it behind async-trait or a boxed return type. Use cases: mTLS with client certificates, SOCKS proxies, HTTP/3.

Mock probes for testing: Implement Probe to return canned ProbeExchange values. The binary crate's demo server uses HttpProbe::new() against a local Axum server for integration testing.

On this page