Packages
ex_aws
1.0.0-rc.4
2.7.0
2.6.1
2.6.0
2.5.11
2.5.10
2.5.9
2.5.8
2.5.7
2.5.6
2.5.5
2.5.4
2.5.3
2.5.2
2.5.1
2.5.0
2.4.4
2.4.3
2.4.2
2.4.1
2.4.0
2.3.4
2.3.3
2.3.2
2.3.1
2.3.0
2.2.10
2.2.9
2.2.8
2.2.7
2.2.6
2.2.5
2.2.4
2.2.3
2.2.2
2.2.1
2.2.0
2.1.9
2.1.8
2.1.7
2.1.6
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.0.2
2.0.1
2.0.0
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.0
1.0.0-rc.4
1.0.0-rc.3
1.0.0-rc.1
1.0.0-beta3
1.0.0-beta2
1.0.0-beta1
1.0.0-beta0
0.5.0
0.4.19
0.4.18
0.4.17
0.4.15
0.4.14
0.4.13
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.1
0.3.0
0.2.0
0.1.2
0.1.1
0.1.0
0.0.5
0.0.4
0.0.3
AWS client for Elixir. Currently supports Dynamo, DynamoStreams, EC2, Firehose, Kinesis, KMS, Lambda, RRDS, Route53, S3, SES, SNS, SQS, STS and others.
Current section
Files
Jump to
Current section
Files
lib/ex_aws/request.ex
defmodule ExAws.Request do
require Logger
@moduledoc """
Makes requests to AWS.
"""
@type http_status :: pos_integer
@type success_content :: %{body: binary, headers: [{binary, binary}]}
@type success_t :: {:ok, success_content}
@type error_t :: {:error, {:http_error, http_status, binary}}
@type response_t :: success_t | error_t
def request(http_method, url, data, headers, config, service) do
body = case data do
[] -> "{}"
d when is_binary(d) -> d
_ -> config[:json_codec].encode!(data)
end
request_and_retry(http_method, url, service, config, headers, body, {:attempt, 1})
end
def request_and_retry(_method, _url, _service, _config, _headers, _req_body, {:error, reason}), do: {:error, reason}
def request_and_retry(method, url, service, config, headers, req_body, {:attempt, attempt}) do
full_headers = ExAws.Auth.headers(method, url, service, config, headers, req_body)
if config[:debug_requests] do
Logger.debug("Request URL: #{inspect url}")
Logger.debug("Request HEADERS: #{inspect full_headers}")
Logger.debug("Request BODY: #{inspect req_body}")
end
case config[:http_client].request(method, url, req_body, full_headers, Map.get(config, :http_opts, [])) do
{:ok, response = %{status_code: status}} when status in 200..299 ->
{:ok, response}
{:ok, %{status_code: status} = resp} when status in 400..499 ->
case client_error(resp, config[:json_codec]) do
{:retry, reason} ->
request_and_retry(method, url, service, config, headers, req_body, attempt_again?(attempt, reason, config))
{:error, reason} -> {:error, reason}
end
{:ok, %{status_code: status, body: body}} when status >= 500 ->
reason = {:http_error, status, body}
request_and_retry(method, url, service, config, headers, req_body, attempt_again?(attempt, reason, config))
{:error, %{reason: reason}} ->
Logger.warn("ExAws: HTTP ERROR: #{inspect reason}")
request_and_retry(method, url, service, config, headers, req_body, attempt_again?(attempt, reason, config))
end
end
def client_error(%{status_code: status, body: body} = error, json_codec) do
case json_codec.decode(body) do
{:ok, %{"__type" => error_type, "message" => message} = err} ->
error_type
|> String.split("#")
|> case do
[_, type] -> handle_aws_error(type, message)
_ -> {:error, {:http_error, status, err}}
end
_ -> {:error, {:http_error, status, error}}
end
end
def client_error(%{status_code: status} = error, _) do
{:error, {:http_error, status, error}}
end
def handle_aws_error("ProvisionedThroughputExceededException" = type, message) do
{:retry, {type, message}}
end
def handle_aws_error("ThrottlingException" = type, message) do
{:retry, {type, message}}
end
def handle_aws_error(type, message) do
{:error, {type, message}}
end
def attempt_again?(attempt, reason, config) do
if attempt >= config[:retries][:max_attempts] do
{:error, reason}
else
attempt |> backoff(config)
{:attempt, attempt + 1}
end
end
def backoff(attempt, config) do
(config[:retries][:base_backoff_in_ms] * :math.pow(2, attempt))
|> min(config[:retries][:max_backoff_in_ms])
|> trunc
|> :rand.uniform
|> :timer.sleep
end
end