Current section
Files
Jump to
Current section
Files
lib/regc/oci/transport.ex
defmodule Regc.Oci.Transport do
@moduledoc """
Behaviour for custom registry HTTP transports.
A transport performs one request and returns either a
`Regc.Oci.Transport.Response`, a response-shaped map, or an error. Regc
handles registry status codes and bearer authentication around the
transport.
Implementations should enforce `request.max_body_bytes` while reading the
response, use the configured timeouts, and apply equivalent redirect and
network-target protections if they follow redirects themselves.
"""
alias Regc.Oci.Options
alias Regc.Oci.Transport.{Request, Response}
@type result :: {:ok, Response.t() | map()} | {:error, term()}
@callback request(Request.t(), Options.t()) :: result()
@non_retryable_errors [
:invalid_status_line,
:invalid_response_header,
:invalid_redirect,
:response_headers_too_large,
:invalid_body_framing,
:unsupported_transfer_encoding,
:invalid_chunk_size,
:invalid_chunk_terminator,
:invalid_body_length,
:unsupported_protocol_switch,
:too_many_informational_responses
]
@non_retryable_error_tuples [
:bad_cert,
:invalid_url,
:invalid_redirect,
:invalid_option,
:invalid_resolver_result,
:options,
:too_many_redirects,
:redirect_loop,
:private_redirect,
:insecure_redirect
]
@non_retryable_tls_alerts [
:unknown_ca,
:bad_certificate,
:certificate_expired,
:certificate_revoked,
:certificate_unknown,
:handshake_failure,
:insufficient_security,
:no_application_protocol,
:protocol_version,
:unrecognized_name,
:unsupported_certificate
]
@spec user_agent() :: String.t()
def user_agent do
version = Application.spec(:regc, :vsn) || "unknown"
"regc/" <> to_string(version)
end
@spec retryable_error?(term()) :: boolean()
def retryable_error?(reason) when reason in @non_retryable_errors, do: false
def retryable_error?({:tls_alert, {alert, _description}})
when alert in @non_retryable_tls_alerts,
do: false
def retryable_error?(%{__exception__: true}), do: false
def retryable_error?({:dns_error, %{reasons: reasons}})
when is_list(reasons) and reasons != [] do
Enum.all?(reasons, &retryable_error?/1)
end
def retryable_error?(reason) when is_tuple(reason) and tuple_size(reason) > 0 do
elem(reason, 0) not in @non_retryable_error_tuples
end
def retryable_error?(_reason), do: true
end