HTTP caching middleware for Tesla
def deps do
[
{:http_cache, "~> 0.3.1"}
{:tesla_http_cache, "~> 0.3.0"}
]
end
Responses have to be stored in a separate store backend (this library does not come with one), such as:
http_cache_store_memory
: responses are stored in memory (ETS)http_cache_store_disk
: responses are stored on disk. An application using thesendfile
system call (such asplug_http_cache
) may benefit from the kernel's memory caching automatically
Both are cluster-aware.
Options of this middleware are options of the http_cache library, By default, the following options are set:
:type
::private
:auto_accept_encoding
:true
:auto_compress
:true
The :store
option must be set when configuring the middleware.
Notice the age
response header after the first request.
iex> client = Tesla.client([{TeslaHTTPCache, %{store: :http_cache_store_process}}])
%Tesla.Client{
fun: nil,
pre: [{TeslaHTTPCache, :call, [%{store: :http_cache_store_process}]}],
post: [],
adapter: nil
}
iex> Tesla.get!(client, "http://perdu.com")
%Tesla.Env{
method: :get,
url: "http://perdu.com",
query: [],
headers: [
{"cache-control", "max-age=600"},
{"date", "Sat, 22 Apr 2023 14:15:11 GMT"},
{"etag", "W/\"cc-5344555136fe9-gzip\""},
{"server", "cloudflare"},
{"vary", "Accept-Encoding,User-Agent"},
{"content-type", "text/html"},
{"expires", "Sat, 22 Apr 2023 14:25:11 GMT"},
{"last-modified", "Thu, 02 Jun 2016 06:01:08 GMT"},
{"cf-cache-status", "DYNAMIC"},
{"report-to",
"{\"endpoints\":[{\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v3?s=OW%2BJhOzTmxq4FGquM7w7bvkDLoryGQY9elB6ajNGx6Wgw0%2BjJechCF9vurIyh1V8rJ%2F0O6KL%2B36xUILE8SICSy1o0O1%2FrR2lx0XHgsN0ZWhBXsWf81OnlHM6ITw%3D\"}],\"group\":\"cf-nel\",\"max_age\":604800}"},
{"nel",
"{\"success_fraction\":0,\"report_to\":\"cf-nel\",\"max_age\":604800}"},
{"cf-ray", "7bbe7a35ea419bc2-FRA"},
{"alt-svc", "h3=\":443\"; ma=86400, h3-29=\":443\"; ma=86400"},
{"content-length", "204"}
],
body: "<html><head><title>Vous Etes Perdu ?</title></head><body><h1>Perdu sur l'Internet ?</h1><h2>Pas de panique, on va vous aider</h2><strong><pre> * <----- vous êtes ici</pre></strong></body></html>\n",
status: 200,
opts: [],
__module__: Tesla,
__client__: %Tesla.Client{
fun: nil,
pre: [{TeslaHTTPCache, :call, [%{store: :http_cache_store_process}]}],
post: [],
adapter: nil
}
}
iex> Tesla.get!(client, "http://perdu.com")
%Tesla.Env{
method: :get,
url: "http://perdu.com",
query: [],
headers: [
{"cache-control", "max-age=600"},
{"date", "Sat, 22 Apr 2023 14:15:11 GMT"},
{"etag", "W/\"cc-5344555136fe9-gzip\""},
{"server", "cloudflare"},
{"vary", "Accept-Encoding,User-Agent"},
{"content-type", "text/html"},
{"expires", "Sat, 22 Apr 2023 14:25:11 GMT"},
{"last-modified", "Thu, 02 Jun 2016 06:01:08 GMT"},
{"cf-cache-status", "DYNAMIC"},
{"report-to",
"{\"endpoints\":[{\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v3?s=OW%2BJhOzTmxq4FGquM7w7bvkDLoryGQY9elB6ajNGx6Wgw0%2BjJechCF9vurIyh1V8rJ%2F0O6KL%2B36xUILE8SICSy1o0O1%2FrR2lx0XHgsN0ZWhBXsWf81OnlHM6ITw%3D\"}],\"group\":\"cf-nel\",\"max_age\":604800}"},
{"nel",
"{\"success_fraction\":0,\"report_to\":\"cf-nel\",\"max_age\":604800}"},
{"cf-ray", "7bbe7a35ea419bc2-FRA"},
{"alt-svc", "h3=\":443\"; ma=86400, h3-29=\":443\"; ma=86400"},
{"content-length", "204"},
{"age", "8"}
],
body: "<html><head><title>Vous Etes Perdu ?</title></head><body><h1>Perdu sur l'Internet ?</h1><h2>Pas de panique, on va vous aider</h2><strong><pre> * <----- vous êtes ici</pre></strong></body></html>\n",
status: 200,
opts: [],
__module__: Tesla,
__client__: %Tesla.Client{
fun: nil,
pre: [{TeslaHTTPCache, :call, [%{store: :http_cache_store_process}]}],
post: [],
adapter: nil
}
}
iex> Tesla.get!(client, "http://perdu.com", headers: [{"range", "bytes=12-43"}])
%Tesla.Env{
method: :get,
url: "http://perdu.com",
query: [],
headers: [
{"cache-control", "max-age=600"},
{"date", "Sat, 22 Apr 2023 14:15:11 GMT"},
{"etag", "W/\"cc-5344555136fe9-gzip\""},
{"server", "cloudflare"},
{"vary", "Accept-Encoding,User-Agent"},
{"content-type", "text/html"},
{"expires", "Sat, 22 Apr 2023 14:25:11 GMT"},
{"last-modified", "Thu, 02 Jun 2016 06:01:08 GMT"},
{"cf-cache-status", "DYNAMIC"},
{"report-to",
"{\"endpoints\":[{\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v3?s=OW%2BJhOzTmxq4FGquM7w7bvkDLoryGQY9elB6ajNGx6Wgw0%2BjJechCF9vurIyh1V8rJ%2F0O6KL%2B36xUILE8SICSy1o0O1%2FrR2lx0XHgsN0ZWhBXsWf81OnlHM6ITw%3D\"}],\"group\":\"cf-nel\",\"max_age\":604800}"},
{"nel",
"{\"success_fraction\":0,\"report_to\":\"cf-nel\",\"max_age\":604800}"},
{"cf-ray", "7bbe7a35ea419bc2-FRA"},
{"alt-svc", "h3=\":443\"; ma=86400, h3-29=\":443\"; ma=86400"},
{"content-range", "bytes 12-43/204"},
{"content-length", "32"},
{"age", "22"}
],
body: "<title>Vous Etes Perdu ?</title>",
status: 206,
opts: [],
__module__: Tesla,
__client__: %Tesla.Client{
fun: nil,
pre: [{TeslaHTTPCache, :call, [%{store: :http_cache_store_process}]}],
post: [],
adapter: nil
}
}
iex> Tesla.get!(client, "http://perdu.com", headers: [{"cache-control", "no-cache"}])
%Tesla.Env{
method: :get,
url: "http://perdu.com",
query: [],
headers: [
{"cache-control", "max-age=600"},
{"date", "Sat, 22 Apr 2023 14:15:46 GMT"},
{"etag", "W/\"cc-5344555136fe9-gzip\""},
{"server", "cloudflare"},
{"vary", "Accept-Encoding,User-Agent"},
{"content-type", "text/html"},
{"expires", "Sat, 22 Apr 2023 14:25:46 GMT"},
{"last-modified", "Thu, 02 Jun 2016 06:01:08 GMT"},
{"cf-cache-status", "DYNAMIC"},
{"report-to",
"{\"endpoints\":[{\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v3?s=3gbcT%2Bp7OxvokGPjTitRoTA9KyQOcbn6z1EG5jp2%2Frvg%2FqA%2Bi0CZgDK0O7VNSB6c5UIPsilr%2BMysTPCgi8ocxYYCsMhc82q4e7EP4nAI5zYYuJhmMGFXTeSjWMI%3D\"}],\"group\":\"cf-nel\",\"max_age\":604800}"},
{"nel",
"{\"success_fraction\":0,\"report_to\":\"cf-nel\",\"max_age\":604800}"},
{"cf-ray", "7bbe7b10f8619bc2-FRA"},
{"alt-svc", "h3=\":443\"; ma=86400, h3-29=\":443\"; ma=86400"},
{"content-length", "204"}
],
body: "<html><head><title>Vous Etes Perdu ?</title></head><body><h1>Perdu sur l'Internet ?</h1><h2>Pas de panique, on va vous aider</h2><strong><pre> * <----- vous êtes ici</pre></strong></body></html>\n",
status: 200,
opts: [],
__module__: Tesla,
__client__: %Tesla.Client{
fun: nil,
pre: [{TeslaHTTPCache, :call, [%{store: :http_cache_store_process}]}],
post: [],
adapter: nil
}
}
http_cache
only caches iodata bodies. As a consequence, beware of not decoding the body before
caching. For instance, do not:
defp client(token) do
Tesla.client([
Tesla.Middleware.Logger,
{Tesla.Middleware.BearerAuth, token: token},
{TeslaHTTPCache, %{store: :http_cache_store_memory}},
Tesla.Middleware.JSON # <- WRONG!!!
])
end
as the JSON will be decoded before caching, and the body wil be a map (not cacheable). Instead do:
defp client(token) do
Tesla.client([
Tesla.Middleware.Logger,
{Tesla.Middleware.BearerAuth, token: token},
Tesla.Middleware.JSON,
{TeslaHTTPCache, %{store: :http_cache_store_memory}}
])
end
Response will be cached first then decoded.
The following events are emitted:
[:tesla_http_cache, :hit]
when a response is returned from the cache- measurements: none
- metadata:
:freshness
: one of:fresh
: a fresh response was returned:stale
: a stale response was returned:revalidated
: the response was successfully revalidated and returned
[:tesla_http_cache, :miss]
in case of cache miss- measurements: none
- metadata: none