Put rich media processing in a Task
[akkoma] / lib / pleroma / web / rich_media / parser.ex
index c70d2fdbade7c66a6f253ecbf2e0d3672523e059..1d4cad0100e2ebcb8092b96555f5b5dfa938fa70 100644 (file)
@@ -1,10 +1,12 @@
 # Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.Web.RichMedia.Parser do
   require Logger
 
+  @cachex Pleroma.Config.get([:cachex, :provider], Cachex)
+
   defp parsers do
     Pleroma.Config.get([:rich_media, :parsers])
   end
@@ -13,7 +15,7 @@ defmodule Pleroma.Web.RichMedia.Parser do
 
   if Pleroma.Config.get(:env) == :test do
     @spec parse(String.t()) :: {:ok, map()} | {:error, any()}
-    def parse(url), do: parse_url(url)
+    def parse(url), do: parse_with_timeout(url)
   else
     @spec parse(String.t()) :: {:ok, map()} | {:error, any()}
     def parse(url) do
@@ -24,8 +26,8 @@ defmodule Pleroma.Web.RichMedia.Parser do
     end
 
     defp get_cached_or_parse(url) do
-      case Cachex.fetch(:rich_media_cache, url, fn ->
-             case parse_url(url) do
+      case @cachex.fetch(:rich_media_cache, url, fn ->
+             case parse_with_timeout(url) do
                {:ok, _} = res ->
                  {:commit, res}
 
@@ -64,7 +66,7 @@ defmodule Pleroma.Web.RichMedia.Parser do
 
     defp set_error_ttl(url, _reason) do
       ttl = Pleroma.Config.get([:rich_media, :failure_backoff], 60_000)
-      Cachex.expire(:rich_media_cache, url, ttl)
+      @cachex.expire(:rich_media_cache, url, ttl)
       :ok
     end
 
@@ -106,7 +108,7 @@ defmodule Pleroma.Web.RichMedia.Parser do
       {:ok, ttl} when is_number(ttl) ->
         ttl = ttl * 1000
 
-        case Cachex.expire_at(:rich_media_cache, url, ttl) do
+        case @cachex.expire_at(:rich_media_cache, url, ttl) do
           {:ok, true} -> {:ok, ttl}
           {:ok, false} -> {:error, :no_key}
         end
@@ -139,6 +141,21 @@ defmodule Pleroma.Web.RichMedia.Parser do
     end
   end
 
+  def parse_with_timeout(url) do
+    try do
+      task =
+        Task.Supervisor.async_nolink(Pleroma.TaskSupervisor, fn ->
+          parse_url(url)
+        end)
+
+      Task.await(task, 5000)
+    catch
+      :exit, {:timeout, _} ->
+        Logger.warn("Timeout while fetching rich media for #{url}")
+        {:error, :timeout}
+    end
+  end
+
   defp maybe_parse(html) do
     Enum.reduce_while(parsers(), %{}, fn parser, acc ->
       case parser.parse(html, acc) do