a56625699325cda6f9bf2a65721dfe3a256f9a66
[akkoma] / lib / pleroma / gun / conn.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Gun.Conn do
6 alias Pleroma.Gun
7
8 require Logger
9
10 def open(%URI{} = uri, opts) do
11 pool_opts = Pleroma.Config.get([:connections_pool], [])
12
13 opts =
14 opts
15 |> Enum.into(%{})
16 |> Map.put_new(:connect_timeout, pool_opts[:connect_timeout] || 5_000)
17 |> Map.put_new(:supervise, false)
18 |> maybe_add_tls_opts(uri)
19
20 do_open(uri, opts)
21 end
22
23 defp maybe_add_tls_opts(opts, %URI{scheme: "http"}), do: opts
24
25 defp maybe_add_tls_opts(opts, %URI{scheme: "https"}) do
26 tls_opts = [
27 verify: :verify_peer,
28 cacertfile: CAStore.file_path(),
29 depth: 20,
30 reuse_sessions: false,
31 log_level: :warning,
32 customize_hostname_check: [match_fun: :public_key.pkix_verify_hostname_match_fun(:https)]
33 ]
34
35 tls_opts =
36 if Keyword.keyword?(opts[:tls_opts]) do
37 Keyword.merge(tls_opts, opts[:tls_opts])
38 else
39 tls_opts
40 end
41
42 Map.put(opts, :tls_opts, tls_opts)
43 end
44
45 defp do_open(uri, %{proxy: {proxy_host, proxy_port}} = opts) do
46 connect_opts =
47 uri
48 |> destination_opts()
49 |> add_http2_opts(uri.scheme, Map.get(opts, :tls_opts, []))
50
51 with open_opts <- Map.delete(opts, :tls_opts),
52 {:ok, conn} <- Gun.open(proxy_host, proxy_port, open_opts),
53 {:ok, protocol} <- Gun.await_up(conn, opts[:connect_timeout]),
54 stream <- Gun.connect(conn, connect_opts),
55 {:response, :fin, 200, _} <- Gun.await(conn, stream) do
56 {:ok, conn, protocol}
57 else
58 error ->
59 Logger.warn(
60 "Opening proxied connection to #{compose_uri_log(uri)} failed with error #{
61 inspect(error)
62 }"
63 )
64
65 error
66 end
67 end
68
69 defp do_open(uri, %{proxy: {proxy_type, proxy_host, proxy_port}} = opts) do
70 version =
71 proxy_type
72 |> to_string()
73 |> String.last()
74 |> case do
75 "4" -> 4
76 _ -> 5
77 end
78
79 socks_opts =
80 uri
81 |> destination_opts()
82 |> add_http2_opts(uri.scheme, Map.get(opts, :tls_opts, []))
83 |> Map.put(:version, version)
84
85 opts =
86 opts
87 |> Map.put(:protocols, [:socks])
88 |> Map.put(:socks_opts, socks_opts)
89
90 with {:ok, conn} <- Gun.open(proxy_host, proxy_port, opts),
91 {:ok, protocol} <- Gun.await_up(conn, opts[:connect_timeout]) do
92 {:ok, conn, protocol}
93 else
94 error ->
95 Logger.warn(
96 "Opening socks proxied connection to #{compose_uri_log(uri)} failed with error #{
97 inspect(error)
98 }"
99 )
100
101 error
102 end
103 end
104
105 defp do_open(%URI{host: host, port: port} = uri, opts) do
106 host = Pleroma.HTTP.AdapterHelper.parse_host(host)
107
108 with {:ok, conn} <- Gun.open(host, port, opts),
109 {:ok, protocol} <- Gun.await_up(conn, opts[:connect_timeout]) do
110 {:ok, conn, protocol}
111 else
112 error ->
113 Logger.warn(
114 "Opening connection to #{compose_uri_log(uri)} failed with error #{inspect(error)}"
115 )
116
117 error
118 end
119 end
120
121 defp destination_opts(%URI{host: host, port: port}) do
122 host = Pleroma.HTTP.AdapterHelper.parse_host(host)
123 %{host: host, port: port}
124 end
125
126 defp add_http2_opts(opts, "https", tls_opts) do
127 Map.merge(opts, %{protocols: [:http2], transport: :tls, tls_opts: tls_opts})
128 end
129
130 defp add_http2_opts(opts, _, _), do: opts
131
132 def compose_uri_log(%URI{scheme: scheme, host: host, path: path}) do
133 "#{scheme}://#{host}#{path}"
134 end
135 end