@@ -88,15 +88,15 @@ defmodule HTTPClient.Steps do
8888 end
8989
9090 @ doc """
91- Encodes the request body based on its shape .
91+ Encodes the request body.
9292
93- If body is of the following shape, it's encoded and its `content-type` set
94- accordingly. Otherwise it's unchanged.
93+ ## Request Options
94+
95+ * `:form` - if set, encodes the request body as form data (using `URI.encode_query/1`).
9596
96- | Shape | Encoder | Content-Type |
97- | --------------- | --------------------------- | ------------------------------------- |
98- | `{:form, data}` | `URI.encode_query/1` | `"application/x-www-form-urlencoded"` |
99- | `{:json, data}` | `Jason.encode_to_iodata!/1` | `"application/json"` |
97+ * `:json` - if set, encodes the request body as JSON (using `Jason.encode_to_iodata!/1`), sets
98+ the `accept` header to `application/json`, and the `content-type`
99+ header to `application/json`.
100100
101101 """
102102 def encode_body ( % { body: { :form , data } } = request ) do
@@ -109,6 +109,7 @@ defmodule HTTPClient.Steps do
109109 request
110110 |> Map . put ( :body , Jason . encode_to_iodata! ( data ) )
111111 |> put_new_header ( "content-type" , "application/json" )
112+ |> put_new_header ( "accept" , "application/json" )
112113 end
113114
114115 def encode_body ( request ) , do: request
@@ -339,11 +340,40 @@ defmodule HTTPClient.Steps do
339340 max_cap = get_options ( retry_options , :max_cap , :timer . minutes ( 20 ) )
340341 delays = cap ( exponential_backoff ( ) , max_cap )
341342 % { delay: Enum . at ( delays , retry_count ) , retry?: true , type: :exponent }
343+
344+ :x_rate_limit ->
345+ delay = check_x_rate_limit ( response_or_exception )
346+ % { delay: delay , retry?: true , type: :x_rate_limit }
347+ end
348+ end
349+
350+ defp check_x_rate_limit ( % Response { headers: headers } ) do
351+ case get_headers ( headers , [ "x-ratelimit-reset" , "x-ratelimit-remaining" ] ) do
352+ % { "x-ratelimit-remaining" => "0" , "x-ratelimit-reset" => timestamp } ->
353+ get_x_rate_limit_delay ( timestamp )
354+
355+ % { "x-ratelimit-reset" => timestamp } = headers when map_size ( headers ) == 1 ->
356+ get_x_rate_limit_delay ( timestamp )
357+
358+ _headers ->
359+ @ default_retry_delay
360+ end
361+ end
362+
363+ defp check_x_rate_limit ( _response_or_exception ) , do: @ default_retry_delay
364+
365+ defp get_x_rate_limit_delay ( timestamp ) do
366+ with { timestamp , "" } <- Integer . parse ( timestamp ) ,
367+ { :ok , datetime } <- DateTime . from_unix ( timestamp ) ,
368+ seconds when seconds > 0 <- DateTime . diff ( datetime , DateTime . utc_now ( ) ) do
369+ :timer . seconds ( seconds )
370+ else
371+ _ -> @ default_retry_delay
342372 end
343373 end
344374
345375 defp get_retry_delay ( options , % Response { status: 429 , headers: headers } ) do
346- case List . keyfind ( headers , "retry-after" , 0 ) do
376+ case get_header ( headers , "retry-after" , 0 ) do
347377 { _ , header_delay } ->
348378 { :retry_after , retry_delay_in_ms ( header_delay ) }
349379
@@ -384,17 +414,21 @@ defmodule HTTPClient.Steps do
384414
385415 defp log_retry ( response_or_exception , retry_count , retry_params ) do
386416 message =
387- cond do
388- retry_params . type == :retry_after ->
417+ case retry_params do
418+ % { type: :retry_after } ->
389419 "Will retry after #{ retry_params . delay } ms"
390420
391- retry_params . type == :exponent ->
421+ % { type: :exponent } ->
392422 "Will retry in #{ retry_params . delay } ms"
393423
394- retry_params . max_retries - retry_count == 1 ->
424+ % { type: :x_rate_limit } ->
425+ "Will retry after #{ retry_params . delay } ms"
426+
427+ % { max_retries: max_retries } when max_retries - retry_count == 1 ->
395428 "Will retry in #{ retry_params . delay } ms, 1 attempt left"
396429
397- attempts = retry_params . max_retries - retry_count ->
430+ _retry_params ->
431+ attempts = retry_params . max_retries - retry_count
398432 "Will retry in #{ retry_params . delay } ms, #{ attempts } attempts left"
399433 end
400434
@@ -448,8 +482,14 @@ defmodule HTTPClient.Steps do
448482 end
449483 end
450484
451- defp get_header ( headers , name ) do
452- Enum . find_value ( headers , nil , fn { key , value } ->
485+ defp get_headers ( headers , keys ) when is_list ( keys ) do
486+ headers
487+ |> Keyword . take ( keys )
488+ |> Map . new ( )
489+ end
490+
491+ defp get_header ( headers , name , default_value \\ nil ) do
492+ Enum . find_value ( headers , default_value , fn { key , value } ->
453493 if String . downcase ( key ) == name , do: value
454494 end )
455495 end
0 commit comments