diff --git a/lib/asciinema_web.ex b/lib/asciinema_web.ex
index eaa801ec7..d07181c08 100644
--- a/lib/asciinema_web.ex
+++ b/lib/asciinema_web.ex
@@ -52,6 +52,7 @@ defmodule AsciinemaWeb do
import AsciinemaWeb.Auth, only: [require_current_user: 2]
import AsciinemaWeb.Plug.ReturnTo
import AsciinemaWeb.Plug.Authz
+ import AsciinemaWeb.Caching
unquote(verified_routes())
diff --git a/lib/asciinema_web/caching.ex b/lib/asciinema_web/caching.ex
new file mode 100644
index 000000000..978b188d3
--- /dev/null
+++ b/lib/asciinema_web/caching.ex
@@ -0,0 +1,17 @@
+defmodule AsciinemaWeb.Caching do
+ import Plug.Conn
+
+ def put_etag(conn, content) do
+ etag = Crypto.md5(to_string(content))
+
+ conn
+ |> put_resp_header("etag", etag)
+ |> register_before_send(fn conn ->
+ if etag in get_req_header(conn, "if-none-match") do
+ send_resp(conn, 304, "")
+ else
+ conn
+ end
+ end)
+ end
+end
diff --git a/lib/asciinema_web/controllers/recording/thumbnail.html.heex b/lib/asciinema_web/controllers/recording/thumbnail.html.heex
index 5ae18794c..42a29b02b 100644
--- a/lib/asciinema_web/controllers/recording/thumbnail.html.heex
+++ b/lib/asciinema_web/controllers/recording/thumbnail.html.heex
@@ -2,5 +2,5 @@
class="thumbnail"
style={"background-color: #{theme(@asciicast).bg}; border-right: 1ch solid #{theme(@asciicast).bg}"}
>
- ".svg?v=t"} />
+ ".svg?f=t&v=#{svg_cache_key(@asciicast)}"} />
diff --git a/lib/asciinema_web/controllers/recording_controller.ex b/lib/asciinema_web/controllers/recording_controller.ex
index 0de4e1339..3ca7f0b0b 100644
--- a/lib/asciinema_web/controllers/recording_controller.ex
+++ b/lib/asciinema_web/controllers/recording_controller.ex
@@ -3,6 +3,7 @@ defmodule AsciinemaWeb.RecordingController do
alias Asciinema.{Recordings, PngGenerator}
alias Asciinema.Recordings.Asciicast
alias AsciinemaWeb.PlayerOpts
+ alias AsciinemaWeb.RecordingHTML
plug :clear_main_class
plug :load_asciicast when action in [:show, :edit, :update, :delete, :iframe]
@@ -102,6 +103,9 @@ defmodule AsciinemaWeb.RecordingController do
end
end
+ # 1 hour
+ @svg_max_age 3600
+
def do_show(conn, "svg", asciicast) do
if asciicast.archived_at do
path = Application.app_dir(:asciinema, "priv/static/images/archived.png")
@@ -112,12 +116,15 @@ defmodule AsciinemaWeb.RecordingController do
|> send_file(200, path)
else
variant =
- case conn.params["v"] do
+ case conn.params["f"] do
"t" -> :thumbnail
_ -> :show
end
- render(conn, variant, asciicast: asciicast)
+ conn
+ |> put_resp_header("cache-control", "public, max-age=#{@svg_max_age}, must-revalidate")
+ |> put_etag(RecordingHTML.svg_cache_key(asciicast))
+ |> render(variant, asciicast: asciicast)
end
end
diff --git a/lib/asciinema_web/controllers/recording_html.ex b/lib/asciinema_web/controllers/recording_html.ex
index ec9f8b724..a81d67cc5 100644
--- a/lib/asciinema_web/controllers/recording_html.ex
+++ b/lib/asciinema_web/controllers/recording_html.ex
@@ -221,6 +221,9 @@ defmodule AsciinemaWeb.RecordingHTML do
asciicast.views_count
end
+ def svg_cache_key(asciicast),
+ do: Timex.to_unix(asciicast.updated_at) - Timex.to_unix(asciicast.inserted_at)
+
def head("show.html", assigns), do: head_for_show(assigns)
def head(_, _), do: nil
end