ConnectionPool.Worker: Open gun conn in continue instead of init
[akkoma] / lib / pleroma / gun / conn.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 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(:await_up_timeout, pool_opts[:await_up_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", host: host}) do
26 tls_opts = [
27 verify: :verify_peer,
28 cacertfile: CAStore.file_path(),
29 depth: 20,
30 reuse_sessions: false,
31 verify_fun:
32 {&:ssl_verify_hostname.verify_fun/3,
33 [check_hostname: Pleroma.HTTP.AdapterHelper.format_host(host)]}
34 ]
35
36 tls_opts =
37 if Keyword.keyword?(opts[:tls_opts]) do
38 Keyword.merge(tls_opts, opts[:tls_opts])
39 else
40 tls_opts
41 end
42
43 Map.put(opts, :tls_opts, tls_opts)
44 end
45
46 defp do_open(uri, %{proxy: {proxy_host, proxy_port}} = opts) do
47 connect_opts =
48 uri
49 |> destination_opts()
50 |> add_http2_opts(uri.scheme, Map.get(opts, :tls_opts, []))
51
52 with open_opts <- Map.delete(opts, :tls_opts),
53 {:ok, conn} <- Gun.open(proxy_host, proxy_port, open_opts),
54 {:ok, _} <- Gun.await_up(conn, opts[:await_up_timeout]),
55 stream <- Gun.connect(conn, connect_opts),
56 {:response, :fin, 200, _} <- Gun.await(conn, stream) do
57 {:ok, conn}
58 else
59 error ->
60 Logger.warn(
61 "Opening proxied connection to #{compose_uri_log(uri)} failed with error #{
62 inspect(error)
63 }"
64 )
65
66 error
67 end
68 end
69
70 defp do_open(uri, %{proxy: {proxy_type, proxy_host, proxy_port}} = opts) do
71 version =
72 proxy_type
73 |> to_string()
74 |> String.last()
75 |> case do
76 "4" -> 4
77 _ -> 5
78 end
79
80 socks_opts =
81 uri
82 |> destination_opts()
83 |> add_http2_opts(uri.scheme, Map.get(opts, :tls_opts, []))
84 |> Map.put(:version, version)
85
86 opts =
87 opts
88 |> Map.put(:protocols, [:socks])
89 |> Map.put(:socks_opts, socks_opts)
90
91 with {:ok, conn} <- Gun.open(proxy_host, proxy_port, opts),
92 {:ok, _} <- Gun.await_up(conn, opts[:await_up_timeout]) do
93 {:ok, conn}
94 else
95 error ->
96 Logger.warn(
97 "Opening socks proxied connection to #{compose_uri_log(uri)} failed with error #{
98 inspect(error)
99 }"
100 )
101
102 error
103 end
104 end
105
106 defp do_open(%URI{host: host, port: port} = uri, opts) do
107 host = Pleroma.HTTP.AdapterHelper.parse_host(host)
108
109 with {:ok, conn} <- Gun.open(host, port, opts),
110 {:ok, _} <- Gun.await_up(conn, opts[:await_up_timeout]) do
111 {:ok, conn}
112 else
113 error ->
114 Logger.warn(
115 "Opening connection to #{compose_uri_log(uri)} failed with error #{inspect(error)}"
116 )
117
118 error
119 end
120 end
121
122 defp destination_opts(%URI{host: host, port: port}) do
123 host = Pleroma.HTTP.AdapterHelper.parse_host(host)
124 %{host: host, port: port}
125 end
126
127 defp add_http2_opts(opts, "https", tls_opts) do
128 Map.merge(opts, %{protocols: [:http2], transport: :tls, tls_opts: tls_opts})
129 end
130
131 defp add_http2_opts(opts, _, _), do: opts
132
133 def compose_uri_log(%URI{scheme: scheme, host: host, path: path}) do
134 "#{scheme}://#{host}#{path}"
135 end
136 end