# respdiff
Trait-based differential response analysis and probe learning for HTTP scanning. It compares HTTP responses to find meaningful differences and automatically discovers injection gates from probe histories.
```rust
use std::time::Duration;
use respdiff::{ResponseSnapshot, compare_responses};
let baseline = ResponseSnapshot::new(
200,
vec![("Server", "nginx")],
"hello world"
).with_elapsed(Duration::from_millis(50));
let current = ResponseSnapshot::new(
500,
vec![("Server", "nginx"), ("X-Error", "1")],
"internal error"
).with_elapsed(Duration::from_millis(60));
let diff = compare_responses(&baseline, ¤t);
if diff.has_differences() {
println!("Status changed: {}", diff.status_changed);
println!("New headers: {:?}", diff.new_headers);
println!("Body similarity: {:.2}", diff.body_similarity);
}
```
## Why this exists
Scanners need to know if a payload actually changed the server's behavior. A raw text diff of two HTTP responses is useless because dynamic tokens, timestamps, and randomized content change on every request. `respdiff` understands HTTP semantics. It diffs headers intelligently, calculates body similarity using Jaccard index, and evaluates timing changes against configurable thresholds.
## Differential policies
Configure the thresholds for what constitutes a meaningful difference.
```rust
use respdiff::{DiffPolicy, is_differential_match_with_policy};
let policy = DiffPolicy {
timing_threshold_ms: 200,
similarity_threshold: 0.85,
};
let is_match = is_differential_match_with_policy(&diff, &policy);
```
## Probe learning
The `DifferentialLearner` analyzes a history of observations to identify which input properties act as gates and which ones are injectable. It generates new variants based on successful shapes.
```rust
use std::time::Duration;
use respdiff::{DifferentialLearner, ProbeObservation};
let mut learner = DifferentialLearner::new().with_analyze_every(50);
learner.record(
[("action", "run"), ("payload", "test")],
ProbeObservation::matched(Duration::from_millis(5), ["code"])
);
let variants = learner.generate_variants(&["alert(1)"]);
for variant in variants {
println!("Try properties {:?} because {}", variant.properties, variant.reason);
}
```
## Contributing
Pull requests are welcome. There is no such thing as a perfect crate. If you find a bug, a better API, or just a rough edge, open a PR. We review quickly.
## License
MIT. Copyright 2026 CORUM COLLECTIVE LLC.
[](https://crates.io/crates/respdiff)
[](https://docs.rs/respdiff)