aboutsummaryrefslogtreecommitdiff
path: root/lib/pleroma/reverse_proxy/client/hackney.ex
blob: ca4a0c29f00117ac7ccb35b267af77186571ce73 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.ReverseProxy.Client.Hackney do
  @behaviour Pleroma.ReverseProxy.Client

  # redirect handler from Pleb, slightly modified to work with Hackney
  # https://declin.eu/objects/d4f38e62-5429-4614-86d1-e8fc16e6bf33
  # seven years without upstream fix!
  # https://github.com/benoitc/hackney/issues/273
  @redirect_statuses [301, 302, 303, 307, 308]
  defp get_location(headers) do
    location =
      Enum.find(headers, fn {header, _location} ->
        String.downcase(header) == "location"
      end)

    elem(location, 1)
  end

  @impl true
  def request(method, url, headers, body, opts \\ []) do
    if opts[:follow_redirect] != false do
      {_state, req_opts} = Access.get_and_update(opts, :follow_redirect, fn a -> {a, false} end)
      res = :hackney.request(method, url, headers, body, req_opts)

      case res do
        {:ok, code, reply_headers, _client} when code in @redirect_statuses ->
          :hackney.request(method, get_location(reply_headers), headers, body, req_opts)

        {:ok, code, reply_headers} when code in @redirect_statuses ->
          :hackney.request(method, get_location(reply_headers), headers, body, req_opts)

        _ ->
          res
      end
    else
      :hackney.request(method, url, headers, body, opts)
    end
  end

  @impl true
  def stream_body(ref) do
    case :hackney.stream_body(ref) do
      :done -> :done
      {:ok, data} -> {:ok, data, ref}
      {:error, error} -> {:error, error}
    end
  end

  @impl true
  def close(ref), do: :hackney.close(ref)
end