http_cache

http_cache is a stateless Erlang HTTP caching library that implements the various HTTP RFCs related to caching.

When caching, it analyses the response along with the associated request to determine whether the response is cacheable, and caches it if so.

When looking up cached responses, it analyses the request and finds the most suitable response (that is, response that is the freshest and conforms to the vary header).

It supports invalidating cached responses:

It also supports:

Finally, many telemetry events are emitted. See the documentation of the main module.

Usage

1> Req = {<<"GET">>, <<"http://example.org">>, [], <<>>}.
{<<"GET">>,<<"http://example.org">>,[],<<>>}

2> Resp = {200, [{<<"content-type">>, <<"text/plain">>}], <<"Cache me">>}.
{200,[{<<"content-type">>,<<"text/plain">>}],<<"Cache me">>}

3> Opts = #{store => http_cache_store_process, type => shared}.
#{store => http_cache_store_process, type => shared}

4> http_cache:cache(Req, Resp, Opts).
{ok,{200,
     [{<<"content-type">>,<<"text/plain">>},
      {<<"content-length">>,<<"8">>}],
     <<"Cache me">>}}

5> http_cache:get(Req, Opts).
{fresh,{{<<21,255,141,93,218,86,217,58,55,246,85,151,223,
           133,134,248,212,121,102,151,176,244,210,11,46,
           ...>>,
         #{}},
        {200,
         [{<<"content-type">>,<<"text/plain">>},
          {<<"content-length">>,<<"8">>},
          {<<"age">>,<<"10">>}],
         <<"Cache me">>}}}

6> http_cache:get(Req, Opts).
{must_revalidate,{{<<21,255,141,93,218,86,217,58,55,246,
                     85,151,223,133,134,248,212,121,102,
                     151,176,244,210,11,46,...>>,
                   #{}},
                  {200,
                   [{<<"content-type">>,<<"text/plain">>},
                    {<<"content-length">>,<<"8">>},
                    {<<"age">>,<<"218">>}],
                   <<"Cache me">>}}}

7> http_cache:get(Req, Opts).
miss

Store backends

Responses have to be stored in a separate store backend (this library being stateless). A suitable backend store for production use is http_cache_store_native. It uses native BEAM features (ETSes…) and is cluster-aware.

Header normalisation

This library may store different responses for the same URL, following the directives of the "vary" header. For instance, if a response can be returned in English or in French, both versions can be cached as long as the "vary" header is correctly used.

This can unfortunately result in an explosion of stored responses if the headers are not normalized. For instance, in this scenario where a site handles both these languages, a response will be stored for any of these requests that include an accept-language header:

and so on, so potentially hundreds of stored responses for only 2 available responses (English or French versions).

In this case, you probably want to apply normalization before caching, that is modify the accept-language header to have only the en or fr value set before using this library.

See Best practices for using the Vary header for more guidance regarding this issue.

Support

OTP24+

RFC5861 (stale-if-error and stale-while-revalidate cache directives) is supported on latest development branch of cowlib (since this commit) or in other words from version 2.12 (not released yet as April, 2022). Manually override dependency if you need to use it (you can take a look at this project’s rebar.config file).

Conformance

RFC9111: HTTP Caching:

RFC5861: HTTP Cache-Control Extensions for Stale Content

(Only with the latest cowlib code, see comment above.)

RFC9110: HTTP Semantics

Contributing

Format with rebar3 format. Pay attention that some lines of the macros in src/http_cache.erl must be manually edited because of an issue in the format plugin.