Skip to main content

cratestack_axum/idempotency/
hash.rs

1//! Stable request fingerprint.
2
3use http::Method;
4use sha2::{Digest, Sha256};
5
6/// Stable fingerprint of a request: SHA-256 over method, path + query,
7/// content-type, and body bytes. Used to detect when a duplicate key is
8/// reused with a different payload (the conflict case the draft spec
9/// calls out). The `path` argument should include the query string so
10/// modifier-style flags (`?dry_run=true`, `?confirm=true`) don't collide
11/// — the middleware passes `Uri::path_and_query` for that reason.
12pub fn hash_request(
13    method: &Method,
14    path: &str,
15    content_type: Option<&str>,
16    body: &[u8],
17) -> [u8; 32] {
18    let mut hasher = Sha256::new();
19    hasher.update(method.as_str().as_bytes());
20    hasher.update(b"\0");
21    hasher.update(path.as_bytes());
22    hasher.update(b"\0");
23    hasher.update(content_type.unwrap_or("").as_bytes());
24    hasher.update(b"\0");
25    hasher.update(body);
26    hasher.finalize().into()
27}
28
29/// Returns true if the HTTP method is one we'd guard with idempotency. We
30/// apply only to mutating verbs — GETs are already safely repeatable.
31pub fn is_idempotent_target_method(method: &Method) -> bool {
32    matches!(
33        method,
34        &Method::POST | &Method::PATCH | &Method::PUT | &Method::DELETE
35    )
36}