http-cache

    Deterministic RFC 9111 HTTP cache decision engine for MoonBit

    http-cache
    rfc9111
    cache-control
    http-semantics
    Download zip
    Author
    Version
    0.1.0
    License
    Apache-2.0
    Last updated
    1 hour ago
    Downloads
    2

    Dependencies

    #MoonHttpCache

    MoonHttpCache is a deterministic, framework-neutral HTTP cache decision engine implemented in MoonBit. It turns request and response metadata into explainable RFC 9111 storage, reuse, validation, and invalidation decisions.

    It does not open sockets or store response bodies. HTTP clients, servers, reverse proxies, service workers, and test tools can therefore reuse the same semantics without adopting a particular transport or cache database.

    #Highlights

    • Loss-tolerant Cache-Control parsing that preserves unknown extensions.
    • Private and shared cache modes, including Authorization, private, public, s-maxage, must-revalidate, and proxy-revalidate behavior.
    • Overflow-safe Age and freshness calculations with caller-injected time.
    • URI, method, and Vary matching for cache-key decisions.
    • Fresh reuse, validation, stale reuse, origin fetch, and only-if-cached paths.
    • ETag/Last-Modified validation preparation and safe 304 metadata merging.
    • Unsafe-method invalidation target reporting.
    • Optional RFC 5861 stale-while-revalidate and stale-if-error package.
    • Stable reason codes, human output, and machine-readable JSON output.

    #Install

    After the package is published to mooncakes.io:

    moon add oyjh0381/http-cache

    Before publication, clone the repository and run commands from its root.

    #Library example

    ///|
    let stored = @cache.stored_response(
    @cache.request("GET", "https://example.test/catalog"),
    @cache.response(
    200,
    headers=@cache.Headers::empty()
    .add("cache-control", "public, max-age=120")
    .add("etag", "\"catalog-v1\""),
    ),
    1000L,
    1002L,
    )

    ///|
    let decision = @cache.evaluate_reuse(
    Some(stored),
    @cache.request("GET", "https://example.test/catalog"),
    @cache.SharedCache,
    1060L,
    )

    Run the complete portable example:

    moon run examples/library-demo

    #Scenario CLI

    The native CLI reads a bounded, schema-versioned JSON file and performs no network access:

    moon run --target native cmd/moon-http-cache -- \ analyze examples/scenarios/fresh-hit.json moon run --target native cmd/moon-http-cache -- \ analyze examples/scenarios/stale-revalidate.json --format json

    The JSON report includes a terminal decision, computed age and lifetime, ordered trace steps, RFC section identifiers, and structured diagnostics.

    #Public decisions

    evaluate_reuse returns one of:

    • ReuseFresh
    • Revalidate
    • ServeStale
    • FetchFromOrigin
    • OnlyIfCachedMiss
    • NotReusable

    Every terminal path includes a stable reason code. Parsing faults do not panic; they are reported and evaluated conservatively.

    #Supported targets

    The core library, scenario codec, examples, and RFC 5861 extension are portable across native, JavaScript, WebAssembly, and WebAssembly GC. The filesystem CLI is native-only.

    #Verification

    moon check --target all --deny-warn moon test --target all --deny-warn moon run examples/library-demo moon run --target native cmd/moon-http-cache -- \ analyze examples/scenarios/fresh-hit.json moon fmt --check moon info

    #Standards and originality

    MoonHttpCache is an original implementation guided by RFC 9110, RFC 9111, and the optional RFC 5861. It is not a port of an existing cache library. Project-authored tests derive from normative behavior, not copied third-party test suites.

    #Non-goals

    • Networking, DNS, TLS, HTTP message parsing, or proxy operation.
    • Response-body persistence, cache eviction, or request collapsing.
    • Range storage and partial-response combination.
    • Claiming that a metadata decision alone makes an integration secure.

    #License

    Apache-2.0. See LICENSE and THIRD_PARTY_NOTICES.md.

    Timestamp

    type Timestamp = Int64

    Unix timestamp in whole seconds. Callers provide timestamps explicitly so all cache decisions remain deterministic.

    AgeBreakdown

    pub(all) struct AgeBreakdown {
    date_value : Int64?
    age_value : Int64
    apparent_age : Int64
    response_delay : Int64
    corrected_age_value : Int64
    corrected_initial_age : Int64
    resident_time : Int64
    current_age : Int64
    } derive(Eq,
    Debug
    )

    Intermediate values from RFC 9111 section 4.2.3 age calculation.

    AgeResult

    pub(all) struct AgeResult {
    breakdown : AgeBreakdown
    trace : Array[TraceStep]
    diagnostics : Array[Diagnostic]
    } derive(Eq,
    Debug
    )

    CacheControl

    pub(all) struct CacheControl {
    directives : Array[CacheDirective]
    diagnostics : Array[Diagnostic]
    } derive(Eq,
    Debug
    )

    Loss-tolerant parse result for one or more Cache-Control field lines.

    CacheControl::contains

    fn CacheControl::contains(self : CacheControl, name : String) -> Bool

    CacheControl::delta

    fn CacheControl::delta(self : CacheControl, name : String) -> DeltaDirective

    Read a single delta-seconds directive with RFC 9111 overflow saturation.

    CacheControl::field_names

    fn CacheControl::field_names(self : CacheControl, name : String) -> Array[String]

    Return field-name arguments from directives such as private="set-cookie".

    CacheControl::occurrences

    fn CacheControl::occurrences(self : CacheControl, name : String) -> Array[CacheDirective]

    CacheDirective

    pub(all) struct CacheDirective {
    name : String
    value : String?
    quoted : Bool
    raw : String
    } derive(Eq,
    Debug
    )

    One normalized Cache-Control directive. Unknown extensions are represented exactly like registered directives so callers can inspect them.

    CacheKeyResult

    pub(all) struct CacheKeyResult {
    matches : Bool
    vary_fields : Array[String]
    trace : Array[TraceStep]
    diagnostics : Array[Diagnostic]
    } derive(Eq,
    Debug
    )

    Result of comparing the primary cache key and Vary-selected request fields.

    CacheMode

    pub(all) enum CacheMode {
    PrivateCache
    SharedCache
    } derive(Eq,
    Debug
    )

    Whether a decision is made for a single-user or shared HTTP cache.

    CachePolicy

    pub(all) struct CachePolicy {
    allow_heuristic_freshness : Bool
    heuristic_fraction_percent : Int
    heuristic_max_seconds : Int64
    allow_disconnected_stale : Bool
    } derive(Eq,
    Debug
    )

    Caller-controlled choices for behavior that RFC 9111 leaves discretionary.

    DeltaDirective

    pub(all) enum DeltaDirective {
    DeltaMissing
    DeltaValid(Int64)
    DeltaInvalid
    DeltaRepeated
    } derive(Eq,
    Debug
    )

    Result of reading a numeric delta-seconds directive.

    Diagnostic

    pub(all) struct Diagnostic {
    level : DiagnosticLevel
    code : String
    message : String
    field_name : String?
    } derive(Eq,
    Debug
    )

    A non-fatal parser or evaluation observation.

    DiagnosticLevel

    pub(all) enum DiagnosticLevel {
    Info
    Warning
    Error
    } derive(Eq,
    Debug
    )

    Machine-stable diagnostic severity.

    FreshnessResult

    pub(all) struct FreshnessResult {
    source : FreshnessSource
    freshness_lifetime : Int64
    current_age : Int64
    remaining_freshness : Int64
    fresh : Bool
    trace : Array[TraceStep]
    diagnostics : Array[Diagnostic]
    } derive(Eq,
    Debug
    )

    FreshnessSource

    pub(all) enum FreshnessSource {
    SharedMaxAge
    MaxAge
    ExpiresDate
    LastModifiedHeuristic
    NoFreshnessInformation
    } derive(Eq,
    Debug
    )

    Source selected for a response's freshness lifetime.
    pub(all) struct Header {
    name : String
    value : String
    } derive(Eq,
    Debug
    )

    One HTTP field line. Field names are normalized to lowercase ASCII at construction; field values are retained verbatim.

    Headers

    pub(all) struct Headers {
    entries : Array[Header]
    } derive(Eq,
    Debug
    )

    Ordered, duplicate-preserving collection of HTTP fields.

    Headers::add

    fn Headers::add(self : Headers, name : String, value : String) -> Headers

    Return a new collection with one field appended.

    Headers::combined

    fn Headers::combined(self : Headers, name : String) -> String?

    Combine repeated list-valued field lines using the RFC 9110 comma form.

    Headers::contains

    fn Headers::contains(self : Headers, name : String) -> Bool

    Headers::diagnostics

    fn Headers::diagnostics(self : Headers) -> Array[Diagnostic]

    Report invalid field names without rejecting the complete message.

    Headers::empty

    fn Headers::empty() -> Headers

    Headers::first

    fn Headers::first(self : Headers, name : String) -> String?

    Return the first matching field line.

    Headers::from_array

    fn Headers::from_array(entries : Array[Header]) -> Headers

    Headers::replace

    fn Headers::replace(self : Headers, name : String, value : String) -> Headers

    Return a copy where all old values are replaced by one field line.

    Headers::values

    fn Headers::values(self : Headers, name : String) -> Array[String]

    Return all field-line values for a case-insensitive field name.

    Headers::without

    fn Headers::without(self : Headers, name : String) -> Headers

    Return a copy with every field of this name removed.

    InvalidationDecision

    pub(all) struct InvalidationDecision {
    invalidate : Bool
    target_uris : Array[String]
    trace : Array[TraceStep]
    diagnostics : Array[Diagnostic]
    } derive(Eq,
    Debug
    )

    Cache keys that a caller should invalidate after an unsafe request.

    OriginState

    pub(all) enum OriginState {
    OriginAvailable
    OriginUnavailable
    } derive(Eq,
    Debug
    )

    Whether contacting the origin is currently possible.

    RequestMetadata

    pub(all) struct RequestMetadata {
    http_method : String
    target_uri : String
    headers : Headers
    } derive(Eq,
    Debug
    )

    Request metadata required by RFC 9111 decisions.

    ResponseMetadata

    pub(all) struct ResponseMetadata {
    status : Int
    headers : Headers
    } derive(Eq,
    Debug
    )

    Response metadata required by RFC 9111 decisions. Bodies deliberately stay outside this library.

    ReuseDecision

    pub(all) struct ReuseDecision {
    verdict : ReuseVerdict
    current_age : Int64
    freshness_lifetime : Int64
    trace : Array[TraceStep]
    diagnostics : Array[Diagnostic]
    } derive(Eq,
    Debug
    )

    ReuseVerdict

    pub(all) enum ReuseVerdict {
    ReuseFresh
    Revalidate
    ServeStale
    FetchFromOrigin
    OnlyIfCachedMiss
    NotReusable
    } derive(Eq,
    Debug
    )

    Stable terminal reuse decisions.

    StorageDecision

    pub(all) struct StorageDecision {
    verdict : StorageVerdict
    fields_to_remove : Array[String]
    fields_requiring_validation : Array[String]
    trace : Array[TraceStep]
    diagnostics : Array[Diagnostic]
    } derive(Eq,
    Debug
    )

    Storage decision plus field handling required before persistence.

    StorageDecision::is_storable

    fn StorageDecision::is_storable(self : StorageDecision) -> Bool

    StorageVerdict

    pub(all) enum StorageVerdict {
    Storable
    NotStorable
    } derive(Eq,
    Debug
    )

    Terminal result of RFC 9111 storage eligibility evaluation.

    StoredResponse

    pub(all) struct StoredResponse {
    request : RequestMetadata
    response : ResponseMetadata
    request_time : Int64
    response_time : Int64
    } derive(Eq,
    Debug
    )

    Metadata retained by the caller for a stored response.

    TraceStep

    pub(all) struct TraceStep {
    code : String
    message : String
    rfc_section : String
    } derive(Eq,
    Debug
    )

    One machine-stable explanation step.

    ValidationMergeResult

    pub(all) struct ValidationMergeResult {
    merged : StoredResponse?
    trace : Array[TraceStep]
    diagnostics : Array[Diagnostic]
    } derive(Eq,
    Debug
    )

    Result of attempting to apply a 304 response to stored metadata.

    ValidationRequest

    pub(all) struct ValidationRequest {
    headers : Headers
    kind : ValidatorKind
    trace : Array[TraceStep]
    } derive(Eq,
    Debug
    )

    ValidatorKind

    pub(all) enum ValidatorKind {
    EntityTagValidator
    LastModifiedValidator
    NoValidator
    } derive(Eq,
    Debug
    )

    Validator selected for a conditional origin request.

    calculate_current_age

    fn calculate_current_age(stored : StoredResponse, now : Int64) -> AgeResult

    Calculate current response age with saturating arithmetic. Reversed clocks are clamped to zero-duration intervals and reported diagnostically.

    calculate_freshness

    fn calculate_freshness(stored : StoredResponse, mode : CacheMode, now : Int64, policy? : CachePolicy) -> FreshnessResult

    Calculate response freshness lifetime and compare it with current age.

    default_policy

    fn default_policy() -> CachePolicy

    Conservative defaults suitable for a standards-focused implementation.

    evaluate_cache_key

    fn evaluate_cache_key(stored : StoredResponse, presented : RequestMetadata) -> CacheKeyResult

    Compare a presented request with the request that selected a stored response.

    evaluate_invalidation

    fn evaluate_invalidation(request : RequestMetadata, response : ResponseMetadata) -> InvalidationDecision

    Evaluate RFC 9111 section 4.4 invalidation after a state-changing request. The caller remains responsible for locating and deleting stored variants.

    evaluate_reuse

    fn evaluate_reuse(stored : StoredResponse?, presented : RequestMetadata, mode : CacheMode, now : Int64, origin? : OriginState, policy? : CachePolicy) -> ReuseDecision

    Decide whether one candidate stored response can satisfy a request.

    evaluate_storage

    fn evaluate_storage(request : RequestMetadata, response : ResponseMetadata, mode : CacheMode, now : Int64) -> StorageDecision

    Evaluate whether a response may be stored by the selected cache mode. This implementation deliberately supports representation storage for GET; HEAD is handled later as metadata refresh and unsafe methods are rejected.

    format_http_date

    fn format_http_date(value : Int64) -> String?

    Format a timestamp using IMF-fixdate, the preferred HTTP wire format.
    fn header(name : String, value : String) -> Header

    Construct a field line. Invalid names are retained so later parsing can emit a structured diagnostic rather than panic.

    header_date

    fn header_date(headers : Headers, name : String, now : Int64) -> Int64?

    Read a date-valued header. Invalid or repeated values are treated as absent.

    merge_not_modified

    fn merge_not_modified(stored : StoredResponse, not_modified : ResponseMetadata, request_time : Int64, response_time : Int64) -> ValidationMergeResult

    Apply metadata from a valid 304 response without changing the stored status or body ownership. A conflicting ETag fails closed.

    parse_cache_control

    fn parse_cache_control(headers : Headers) -> CacheControl

    Parse every Cache-Control field line. Syntax faults are captured as diagnostics and valid neighboring directives remain available.

    parse_http_date

    fn parse_http_date(value : String, now : Int64) -> Int64?

    Parse any RFC 9110 HTTP-date form relative to the supplied current time.

    prepare_validation

    fn prepare_validation(stored : StoredResponse, presented : RequestMetadata) -> ValidationRequest

    Prepare conditional request fields from the preferred stored validator.

    request

    fn request(http_method : String, target_uri : String, headers? : Headers) -> RequestMetadata

    Construct validated request metadata.

    response

    fn response(status : Int, headers? : Headers) -> ResponseMetadata

    Construct response metadata without a body.

    stored_response

    fn stored_response(request : RequestMetadata, response : ResponseMetadata, request_time : Int64, response_time : Int64) -> StoredResponse

    Construct stored response metadata with explicit request/response times.

    Powered by MoonBit

    Site sourceReport issuePackagesBuild queueSkillsStatistics

    © 2026 mooncakes.io