parlov 0.8.0

HTTP oracle detection tool — systematic probing for RFC-compliant information leakage.
Documentation
use super::*;
use bytes::Bytes;
use http::{HeaderMap, HeaderValue, StatusCode};

fn rate_limited_class() -> ResponseClass {
    ResponseClass::classify(StatusCode::TOO_MANY_REQUESTS, &HeaderMap::new())
}

fn success_class() -> ResponseClass {
    ResponseClass::classify(StatusCode::OK, &HeaderMap::new())
}

#[test]
fn rate_limit_gate_returns_false_on_empty_exchanges() {
    let exchanges: Vec<(ResponseClass, HeaderMap, Bytes)> = vec![];
    assert!(!rate_limit_gate(&exchanges));
}

#[test]
fn rate_limit_gate_returns_false_on_success_exchanges() {
    let exchanges = vec![(success_class(), HeaderMap::new(), Bytes::new())];
    assert!(!rate_limit_gate(&exchanges));
}

#[test]
fn rate_limit_gate_returns_true_when_retry_after_present() {
    let mut headers = HeaderMap::new();
    headers.insert(http::header::RETRY_AFTER, HeaderValue::from_static("30"));
    let exchanges = vec![(rate_limited_class(), headers, Bytes::new())];
    assert!(rate_limit_gate(&exchanges));
}

#[test]
fn rate_limit_gate_returns_true_when_ratelimit_remaining_zero() {
    let mut headers = HeaderMap::new();
    headers.insert("ratelimit-remaining", HeaderValue::from_static("0"));
    let exchanges = vec![(rate_limited_class(), headers, Bytes::new())];
    assert!(rate_limit_gate(&exchanges));
}

#[test]
fn rate_limit_gate_returns_false_when_ratelimit_remaining_nonzero() {
    let mut headers = HeaderMap::new();
    headers.insert("ratelimit-remaining", HeaderValue::from_static("5"));
    let exchanges = vec![(rate_limited_class(), headers, Bytes::new())];
    assert!(!rate_limit_gate(&exchanges));
}

#[test]
fn rate_limit_gate_returns_true_when_x_ratelimit_remaining_zero() {
    let mut headers = HeaderMap::new();
    headers.insert("x-ratelimit-remaining", HeaderValue::from_static("0"));
    let exchanges = vec![(rate_limited_class(), headers, Bytes::new())];
    assert!(rate_limit_gate(&exchanges));
}

#[test]
fn parse_rate_limit_remaining_parses_standard_header() {
    let mut headers = HeaderMap::new();
    headers.insert("ratelimit-remaining", HeaderValue::from_static("42"));
    assert_eq!(parse_rate_limit_remaining(&headers), Some(42));
}

#[test]
fn parse_rate_limit_remaining_parses_legacy_x_header() {
    let mut headers = HeaderMap::new();
    headers.insert("x-ratelimit-remaining", HeaderValue::from_static("10"));
    assert_eq!(parse_rate_limit_remaining(&headers), Some(10));
}

#[test]
fn parse_rate_limit_remaining_returns_none_when_absent() {
    let headers = HeaderMap::new();
    assert_eq!(parse_rate_limit_remaining(&headers), None);
}

#[test]
fn parse_rate_limit_remaining_returns_none_on_malformed() {
    let mut headers = HeaderMap::new();
    headers.insert(
        "ratelimit-remaining",
        HeaderValue::from_static("not-a-number"),
    );
    assert_eq!(parse_rate_limit_remaining(&headers), None);
}