parlov-probe
HTTP execution layer — the Probe trait, HttpProbe implementation, timing capture, and extension points for custom clients.
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 itsResponseSurface) - Captures timing (
timing_ns) for potential statistical analysis - Must be
Send + Syncfor 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:
build_method()mapshttp::Methodtoreqwest::Methodapply_headers()copiesHeaderMapentries to the reqwest request builder- Request is sent, timing is recorded via
Instant::now()before/after convert_status()mapsreqwest::StatusCodeback tohttp::StatusCodeconvert_headers()mapsreqwest::header::HeaderMapback tohttp::HeaderMap- Response body is read fully into
bytes::Bytes - Everything is bundled into
ProbeExchange { request, response }
Internal Modules
http module (src/http.rs)
Contains HttpProbe and four private helper functions:
| Function | Purpose |
|---|---|
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) -> HeaderMap | Maps 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.
parlov-core
Shared vocabulary crate — request/response primitives, oracle domain types, technique metadata, signal types, scoring types, and the OracleResult.
parlov-analysis
Analysis engine — the Analyzer trait, ExistenceAnalyzer, three-layer scoring pipeline, signal extractors, and family-based deduplication.