unsafe_proxy.ex (5966B)
1 defmodule Mint.UnsafeProxy do 2 @moduledoc false 3 4 alias Mint.{Types, UnsafeProxy} 5 6 @behaviour Mint.Core.Conn 7 8 defstruct [ 9 :hostname, 10 :port, 11 :scheme, 12 :module, 13 :proxy_headers, 14 :state 15 ] 16 17 @opaque t() :: %UnsafeProxy{} 18 19 @type host_triple() :: {Types.scheme(), address :: Types.address(), :inet.port_number()} 20 21 @spec connect(host_triple(), host_triple(), opts :: keyword()) :: 22 {:ok, t()} | {:error, Types.error()} 23 def connect(proxy, host, opts \\ []) do 24 {proxy_scheme, proxy_address, proxy_port} = proxy 25 {scheme, address, port} = host 26 hostname = Mint.Core.Util.hostname(opts, address) 27 28 with {:ok, state} <- Mint.HTTP1.connect(proxy_scheme, proxy_address, proxy_port, opts) do 29 conn = %UnsafeProxy{ 30 scheme: scheme, 31 hostname: hostname, 32 port: port, 33 module: Mint.HTTP1, 34 proxy_headers: Keyword.get(opts, :proxy_headers, []), 35 state: state 36 } 37 38 {:ok, conn} 39 end 40 end 41 42 @impl true 43 @spec initiate( 44 module(), 45 Mint.Types.socket(), 46 String.t(), 47 :inet.port_number(), 48 keyword() 49 ) :: no_return() 50 def initiate(_transport, _transport_state, _hostname, _port, _opts) do 51 raise "initiate/5 does not apply for #{inspect(__MODULE__)}" 52 end 53 54 @impl true 55 @spec close(t()) :: {:ok, t()} 56 def close(%UnsafeProxy{module: module, state: state} = _conn) do 57 module.close(state) 58 end 59 60 @impl true 61 @spec open?(t(), :read | :write | :read_write) :: boolean() 62 def open?(%UnsafeProxy{module: module, state: state}, type \\ :read_write) do 63 module.open?(state, type) 64 end 65 66 @impl true 67 @spec request( 68 t(), 69 method :: String.t(), 70 path :: String.t(), 71 Types.headers(), 72 body :: iodata() | nil | :stream 73 ) :: 74 {:ok, t(), Types.request_ref()} 75 | {:error, t(), Types.error()} 76 def request( 77 %UnsafeProxy{module: module, state: state} = conn, 78 method, 79 path, 80 headers, 81 body \\ nil 82 ) do 83 path = request_line(conn, path) 84 headers = headers ++ conn.proxy_headers 85 86 case module.request(state, method, path, headers, body) do 87 {:ok, state, request} -> {:ok, %{conn | state: state}, request} 88 {:error, state, reason} -> {:error, %{conn | state: state}, reason} 89 end 90 end 91 92 @impl true 93 @spec stream_request_body( 94 t(), 95 Types.request_ref(), 96 iodata() | :eof | {:eof, trailing_headers :: Types.headers()} 97 ) :: 98 {:ok, t()} | {:error, t(), Types.error()} 99 def stream_request_body(%UnsafeProxy{module: module, state: state} = conn, ref, body) do 100 case module.stream_request_body(state, ref, body) do 101 {:ok, state} -> {:ok, %{conn | state: state}} 102 {:error, state, reason} -> {:error, %{conn | state: state}, reason} 103 end 104 end 105 106 @impl true 107 @spec stream(t(), term()) :: 108 {:ok, t(), [Types.response()]} 109 | {:error, t(), Types.error(), [Types.response()]} 110 | :unknown 111 def stream(%UnsafeProxy{module: module, state: state} = conn, message) do 112 case module.stream(state, message) do 113 {:ok, state, responses} -> {:ok, %{conn | state: state}, responses} 114 {:error, state, reason, responses} -> {:error, %{conn | state: state}, reason, responses} 115 :unknown -> :unknown 116 end 117 end 118 119 @impl true 120 @spec open_request_count(t()) :: non_neg_integer() 121 def open_request_count(%UnsafeProxy{module: module, state: state} = _conn) do 122 module.open_request_count(state) 123 end 124 125 @impl true 126 @spec recv(t(), non_neg_integer(), timeout()) :: 127 {:ok, t(), [Types.response()]} 128 | {:error, t(), Types.error(), [Types.response()]} 129 def recv(%UnsafeProxy{module: module, state: state} = conn, byte_count, timeout) do 130 case module.recv(state, byte_count, timeout) do 131 {:ok, state, responses} -> {:ok, %{conn | state: state}, responses} 132 {:error, state, reason, responses} -> {:error, %{conn | state: state}, reason, responses} 133 end 134 end 135 136 @impl true 137 @spec set_mode(t(), :active | :passive) :: {:ok, t()} | {:error, Types.error()} 138 def set_mode(%UnsafeProxy{module: module, state: state} = conn, mode) do 139 with {:ok, state} <- module.set_mode(state, mode) do 140 {:ok, %{conn | state: state}} 141 end 142 end 143 144 @impl true 145 @spec controlling_process(t(), pid()) :: {:ok, t()} | {:error, Types.error()} 146 def controlling_process(%UnsafeProxy{module: module, state: state} = conn, new_pid) do 147 with {:ok, _} <- module.controlling_process(state, new_pid) do 148 {:ok, conn} 149 end 150 end 151 152 @impl true 153 @spec put_private(t(), atom(), term()) :: t() 154 def put_private(%UnsafeProxy{module: module, state: state} = conn, key, value) do 155 state = module.put_private(state, key, value) 156 %{conn | state: state} 157 end 158 159 @impl true 160 @spec get_private(t(), atom(), term()) :: term() 161 def get_private(%UnsafeProxy{module: module, state: state}, key, default \\ nil) do 162 module.get_private(state, key, default) 163 end 164 165 @impl true 166 @spec delete_private(t(), atom()) :: t() 167 def delete_private(%UnsafeProxy{module: module, state: state} = conn, key) do 168 state = module.delete_private(state, key) 169 %{conn | state: state} 170 end 171 172 defp request_line(%UnsafeProxy{scheme: scheme, hostname: hostname, port: port}, path) do 173 %URI{scheme: Atom.to_string(scheme), host: hostname, port: port, path: path} 174 |> URI.to_string() 175 end 176 177 @impl true 178 @spec get_socket(t()) :: Mint.Types.socket() 179 def get_socket(%UnsafeProxy{module: module, state: state}) do 180 module.get_socket(state) 181 end 182 183 # The `%__MODULE__{proxy_headers: value}` here is the request headers, 184 # not the proxy response ones. Unsafe proxy mixes its headers (if any) 185 # with the regular response headers, so you can get them there. 186 @impl true 187 @spec get_proxy_headers(t()) :: Mint.Types.headers() 188 def get_proxy_headers(%__MODULE__{}), do: [] 189 end