total rebase
[anni] / lib / pleroma / reverse_proxy / client / hackney.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.ReverseProxy.Client.Hackney do
6   @behaviour Pleroma.ReverseProxy.Client
7
8   # redirect handler from Pleb, slightly modified to work with Hackney
9   # https://declin.eu/objects/d4f38e62-5429-4614-86d1-e8fc16e6bf33
10   # seven years without upstream fix!
11   # https://github.com/benoitc/hackney/issues/273
12   @redirect_statuses [301, 302, 303, 307, 308]
13   defp get_location(headers) do
14     location =
15       Enum.find(headers, fn {header, _location} ->
16         String.downcase(header) == "location"
17       end)
18
19     elem(location, 1)
20   end
21
22   @impl true
23   def request(method, url, headers, body, opts \\ []) do
24     if opts[:follow_redirect] != false do
25       {_state, req_opts} = Access.get_and_update(opts, :follow_redirect, fn a -> {a, false} end)
26       res = :hackney.request(method, url, headers, body, req_opts)
27
28       case res do
29         {:ok, code, reply_headers, _client} when code in @redirect_statuses ->
30           :hackney.request(method, get_location(reply_headers), headers, body, req_opts)
31
32         {:ok, code, reply_headers} when code in @redirect_statuses ->
33           :hackney.request(method, get_location(reply_headers), headers, body, req_opts)
34
35         _ ->
36           res
37       end
38     else
39       :hackney.request(method, url, headers, body, opts)
40     end
41   end
42
43   @impl true
44   def stream_body(ref) do
45     case :hackney.stream_body(ref) do
46       :done -> :done
47       {:ok, data} -> {:ok, data, ref}
48       {:error, error} -> {:error, error}
49     end
50   end
51
52   @impl true
53   def close(ref), do: :hackney.close(ref)
54 end