http-fresh 0.1.0

Check HTTP response freshness for conditional GET (If-None-Match / If-Modified-Since vs ETag / Last-Modified, honoring Cache-Control: no-cache). A faithful port of the `fresh` npm package. Zero dependencies, no_std, no alloc.
Documentation
  • Coverage
  • 100%
    16 out of 16 items documented3 out of 11 items with examples
  • Size
  • Source code size: 42.25 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 295.58 kB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 2s Average build duration of successful builds.
  • all releases: 2s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • Homepage
  • trananhtung/http-fresh
    0 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • trananhtung

http-fresh

All Contributors

crates.io docs.rs CI license

HTTP response freshness (conditional GET) checking for Rust.

Decide whether a cached response is still fresh for a request — i.e. whether your server may answer 304 Not Modified instead of resending the body. http-fresh evaluates the If-None-Match / If-Modified-Since request headers against the response's ETag / Last-Modified, honoring Cache-Control: no-cache.

It is a faithful Rust port of the widely-used fresh npm package (the logic behind Express's req.fresh), which has no Rust equivalent.

  • Zero dependencies
  • #![no_std] — works anywhere core does
  • Zero heap allocation — no alloc required
  • Differential-tested against the reference fresh implementation

Install

[dependencies]
http-fresh = "0.1"

Usage

use http_fresh::{fresh, Request, Response};

// ETag-based conditional GET.
let req = Request::new().if_none_match("\"686897696a7c876b7e\"");
let res = Response::new().etag("\"686897696a7c876b7e\"");
assert!(fresh(&req, &res)); // matching ETag → fresh → send 304

let res = Response::new().etag("\"a-new-etag\"");
assert!(!fresh(&req, &res)); // changed → stale → send the body

// Date-based conditional GET.
let req = Request::new().if_modified_since("Wed, 21 Oct 2015 07:28:00 GMT");
let res = Response::new().last_modified("Mon, 19 Oct 2015 07:28:00 GMT");
assert!(fresh(&req, &res)); // not modified since → fresh

// Cache-Control: no-cache always forces a revalidation.
let req = req.cache_control("no-cache");
assert!(!fresh(&req, &Response::new().last_modified("Mon, 19 Oct 2015 07:28:00 GMT")));

You can also build the headers as plain struct literals (all fields are Option<&str> and default to None):

use http_fresh::{fresh, Request, Response};

let req = Request { if_none_match: Some("*"), ..Default::default() };
let res = Response { etag: Some("\"v1\""), ..Default::default() };
assert!(fresh(&req, &res));

Semantics

fresh returns true (the response is fresh — send 304) only when:

  1. The request is conditional (has If-None-Match and/or If-Modified-Since); an unconditional request is never fresh.
  2. Cache-Control: no-cache is not present (it always forces stale).
  3. If If-None-Match is present and not *, the response ETag matches one of its entries, using weak/strong (W/) comparison.
  4. If If-Modified-Since is present, the response Last-Modified is no newer than it.

Empty-string headers are treated as absent.

Differences from the npm package

The npm package parses dates with JavaScript's Date.parse, which is lenient and — for timezone-less formats such as asctime — interprets them in the host machine's local timezone, so its result is not reproducible across machines. http-fresh instead parses exactly the three date formats mandated by RFC 9110 §5.6.7IMF-fixdate, obsolete RFC 850, and asctime — always in GMT. Any string outside those formats is treated as unparseable, which makes the response stale (a safe, revalidating default). For real-world HTTP traffic, which uses IMF-fixdate, the behavior is identical.

Contributors ✨

This project follows the all-contributors specification. Contributions of any kind are welcome — code, docs, bug reports, ideas, reviews! See the emoji key for how each contribution is recognized, and open a PR or issue to get involved.

Thanks goes to these wonderful people:

License

Licensed under either of MIT or Apache-2.0 at your option.