@@ -3,118 +3,97 @@ defmodule HTTPClient.Adapters.Finch do
33 Implementation of `HTTPClient.Adapter` behaviour using Finch HTTP client.
44 """
55
6- alias HTTPClient . { Error , Response }
6+ alias HTTPClient . { Request , Response }
77
88 @ type method ( ) :: Finch.Request . method ( )
99 @ type url ( ) :: Finch.Request . url ( )
1010 @ type headers ( ) :: Finch.Request . headers ( )
1111 @ type body ( ) :: Finch.Request . body ( )
1212 @ type options ( ) :: keyword ( )
1313
14- @ behaviour HTTPClient.Adapter
14+ @ doc """
15+ Performs the request using `Finch`.
16+ """
17+ def perform_request ( request ) do
18+ options = prepare_options ( request . options )
1519
16- @ delay 1000
20+ request . method
21+ |> Finch . build ( request . url , request . headers , request . body )
22+ |> Finch . request ( request . private . finch_name , options )
23+ |> case do
24+ { :ok , % { status: status , body: body , headers: headers } } ->
25+ { request ,
26+ Response . new ( status: status , body: body , headers: headers , request_url: request . url ) }
1727
18- @ impl true
19- def request ( method , url , body , headers , options ) do
20- perform_request ( method , url , headers , body , options )
28+ { :error , exception } ->
29+ { request , exception }
30+ end
2131 end
2232
23- @ impl true
24- def get ( url , headers , options ) do
25- perform_request ( :get , url , headers , nil , options )
33+ @ doc false
34+ def proxy ( request ) do
35+ Request . put_private ( request , :finch_name , get_client ( ) )
2636 end
2737
28- @ impl true
29- def post ( url , body , headers , options ) do
30- perform_request ( :post , url , headers , body , options )
38+ defp prepare_options ( options ) do
39+ Enum . map ( options , & normalize_option / 1 )
3140 end
3241
33- @ impl true
34- def put ( url , body , headers , options ) do
35- perform_request ( :put , url , headers , body , options )
36- end
42+ defp normalize_option ( { :timeout , value } ) , do: { :pool_timeout , value }
43+ defp normalize_option ( { :recv_timeout , value } ) , do: { :receive_timeout , value }
44+ defp normalize_option ( { key , value } ) , do: { key , value }
3745
38- @ impl true
39- def patch ( url , body , headers , options ) do
40- perform_request ( :patch , url , headers , body , options )
46+ defp get_client ( ) do
47+ :http_client
48+ |> Application . get_env ( :proxy )
49+ |> get_client_name ( )
4150 end
4251
43- @ impl true
44- def delete ( url , headers , options ) do
45- perform_request ( :delete , url , headers , nil , options )
52+ defp get_client_name ( nil ) , do: HTTPClient.Finch
53+
54+ defp get_client_name ( proxies ) when is_list ( proxies ) do
55+ proxies
56+ |> Enum . random ( )
57+ |> get_client_name ( )
4658 end
4759
48- defp perform_request ( method , url , headers , body , options , attempt \\ 0 ) do
49- { params , options } = Keyword . pop ( options , :params )
50- { basic_auth , options } = Keyword . pop ( options , :basic_auth )
60+ defp get_client_name ( proxy ) when is_map ( proxy ) do
61+ name = custom_pool_name ( proxy )
5162
52- url = build_request_url ( url , params )
53- headers = add_basic_auth_header ( headers , basic_auth )
54- options = prepare_options ( options )
63+ pools = % {
64+ default: [
65+ conn_opts: [ proxy: compose_proxy ( proxy ) , proxy_headers: compose_proxy_headers ( proxy ) ]
66+ ]
67+ }
5568
56- method
57- |> Finch . build ( url , headers , body )
58- |> Finch . request ( get_client ( ) , options )
59- |> case do
60- { :ok , % { status: status , body: body , headers: headers } } ->
61- { :ok , % Response { status: status , body: body , headers: headers , request_url: url } }
62-
63- { :error ,
64- % Mint.HTTPError {
65- reason: { :proxy , _ }
66- } } ->
67- case attempt < 5 do
68- true ->
69- Process . sleep ( attempt * @ delay )
70- perform_request ( method , url , headers , body , options , attempt + 1 )
71-
72- false ->
73- { :error , % Error { reason: :proxy_error } }
74- end
75-
76- { :error , error } ->
77- { :error , % Error { reason: error . reason } }
78- end
79- end
80-
81- defp build_request_url ( url , nil ) , do: url
69+ child_spec = { Finch , name: name , pools: pools }
8270
83- defp build_request_url ( url , params ) do
84- cond do
85- Enum . count ( params ) === 0 -> url
86- URI . parse ( url ) . query -> url <> "&" <> URI . encode_query ( params )
87- true -> url <> "?" <> URI . encode_query ( params )
71+ case DynamicSupervisor . start_child ( HTTPClient.FinchSupervisor , child_spec ) do
72+ { :ok , _ } -> name
73+ { :error , { :already_started , _ } } -> name
8874 end
8975 end
9076
91- defp add_basic_auth_header ( headers , { username , password } ) do
92- credentials = Base . encode64 ( "#{ username } :#{ password } " )
93- [ { "Authorization" , "Basic " <> credentials } | headers || [ ] ]
77+ defp compose_proxy_headers ( % { opts: opts } ) do
78+ Keyword . get ( opts , :proxy_headers , [ ] )
9479 end
9580
96- defp add_basic_auth_header ( headers , _basic_auth ) , do: headers
81+ defp compose_proxy_headers ( _ ) , do: [ ]
9782
98- defp prepare_options ( options ) do
99- Enum . map ( options , & normalize_option / 1 )
83+ defp compose_proxy ( proxy ) do
84+ { proxy . scheme , proxy . address , to_integer ( proxy . port ) , proxy . opts }
10085 end
10186
102- defp normalize_option ( { :timeout , value } ) , do: { :pool_timeout , value }
103- defp normalize_option ( { :recv_timeout , value } ) , do: { :receive_timeout , value }
104- defp normalize_option ( { key , value } ) , do: { key , value }
105-
106- defp get_client do
107- case Application . get_env ( :http_client , :proxy , nil ) do
108- nil -> FinchHTTPClient
109- proxies -> get_client_with_proxy ( proxies )
110- end
111- end
87+ defp to_integer ( term ) when is_integer ( term ) , do: term
88+ defp to_integer ( term ) when is_binary ( term ) , do: String . to_integer ( term )
11289
113- defp get_client_with_proxy ( proxy ) when is_map ( proxy ) do
114- FinchHTTPClientWithProxy_0
115- end
90+ defp custom_pool_name ( opts ) do
91+ name =
92+ opts
93+ |> :erlang . term_to_binary ( )
94+ |> :erlang . md5 ( )
95+ |> Base . url_encode64 ( padding: false )
11696
117- defp get_client_with_proxy ( proxies ) when is_list ( proxies ) do
118- :"FinchHTTPClientWithProxy_#{ Enum . random ( 0 .. ( length ( proxies ) - 1 ) ) } "
97+ Module . concat ( HTTPClient.FinchSupervisor , "Pool_#{ name } " )
11998 end
12099end
0 commit comments