Packages

Runtime core for the aws-gleam SDK: credentials, SigV4 + SigV4a signing, endpoint resolution, retry, HTTP transport, waiters, paginators, and Smithy protocol codecs.

Current section

Files

Jump to
aws_gleam_runtime src aws@internal@client@runtime.erl
Raw

src/aws@internal@client@runtime.erl

-module(aws@internal@client@runtime).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/aws/internal/client/runtime.gleam").
-export([default_endpoint/2, default_config/3, with_sigv4a_region_set/2, with_sigv4a_path_normalization/2, with_credentials_provider/2, with_endpoint_param/3, with_endpoint_url/2, with_http_send/2, with_streaming_http_send/2, with_http2/1, with_retry_strategy/2, with_max_attempts/2, with_endpoint_rule_set/2, extract_error_type/2, invoke_with_endpoint_params_and_host_prefix/5, invoke_with_endpoint_params/4, invoke/3, invoke_streaming_with_endpoint_params/3, invoke_streaming/2, error_type_matches/2, translate_service_error/4, check_error_type_matches/3]).
-export_type([client_config/0, sigv4a_signer/0, client_error/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Shared runtime for awsJson1_0 + awsJson1_1 generated clients.\n"
"\n"
" Generated per-service modules carry the per-operation `build_*` /\n"
" `parse_*` codec pair plus the service-level metadata (endpoint\n"
" prefix, signing name, region). They call into `invoke` here for\n"
" everything else: credential resolution, endpoint URL construction,\n"
" SigV4 signing, HTTP dispatch, response parsing.\n"
"\n"
" This keeps the generated code small: one `invoke` call per\n"
" operation rather than ~30 lines of glue per op duplicated 57×\n"
" across DynamoDB.\n"
).
-type client_config() :: {client_config,
aws@credentials:provider(),
binary(),
binary(),
binary(),
binary(),
fun((gleam@http@request:request(bitstring())) -> {ok,
gleam@http@response:response(bitstring())} |
{error, aws@internal@http_send:http_error()}),
fun((gleam@http@request:request(bitstring())) -> {ok,
gleam@http@response:response(aws@streaming:streaming_body())} |
{error, aws@internal@http_send:http_error()}),
fun(() -> binary()),
aws@retry:strategy(),
gleam@option:option(aws@endpoints:rule_set()),
gleam@dict:dict(binary(), aws@endpoints:value()),
gleam@option:option(sigv4a_signer())}.
-type sigv4a_signer() :: {sigv4a_signer, list(binary()), boolean()}.
-type client_error() :: {credentials_error, aws@credentials:provider_error()} |
{transport_error, aws@internal@http_send:http_error()} |
{decode_error, binary()} |
{service_error, integer(), binary(), bitstring()}.
-file("src/aws/internal/client/runtime.gleam", 156).
-spec default_endpoint(binary(), binary()) -> binary().
default_endpoint(Endpoint_prefix, Region) ->
<<<<<<<<"https://"/utf8, Endpoint_prefix/binary>>/binary, "."/utf8>>/binary,
Region/binary>>/binary,
".amazonaws.com"/utf8>>.
-file("src/aws/internal/client/runtime.gleam", 95).
?DOC(
" Sensible default config given a region. Credentials default to\n"
" the standard chain (env → web-identity → SSO → profile → process →\n"
" ECS → IMDS); callers swap in a custom provider via\n"
" `with_credentials_provider`, matching the convention every other\n"
" AWS SDK follows.\n"
).
-spec default_config(binary(), binary(), binary()) -> client_config().
default_config(Region, Endpoint_prefix, Signing_name) ->
{client_config,
aws@credentials:default_chain(
fun aws@internal@http_send:default_send/1,
<<"default"/utf8>>
),
Region,
Endpoint_prefix,
Signing_name,
default_endpoint(Endpoint_prefix, Region),
fun aws@internal@http_send:default_send/1,
fun aws@internal@http_streaming:default_send/1,
fun aws_ffi:aws_timestamp/0,
aws@retry:standard(),
none,
maps:new(),
none}.
-file("src/aws/internal/client/runtime.gleam", 127).
?DOC(
" Opt the Client into SigV4a (asymmetric ECDSA P-256) signing for\n"
" every request. `region_set` becomes the `X-Amz-Region-Set` header\n"
" — single-region callers pass `[\"us-east-1\"]`, multi-region callers\n"
" pass the full list. Required for S3 Multi-Region Access Points.\n"
"\n"
" `normalize_path` defaults to `True`; S3 callers should follow up\n"
" with `with_sigv4a_path_normalization(client, False)` so keys\n"
" containing `.` / `..` survive the canonical-request step.\n"
).
-spec with_sigv4a_region_set(client_config(), list(binary())) -> client_config().
with_sigv4a_region_set(Config, Region_set) ->
{client_config,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config),
erlang:element(6, Config),
erlang:element(7, Config),
erlang:element(8, Config),
erlang:element(9, Config),
erlang:element(10, Config),
erlang:element(11, Config),
erlang:element(12, Config),
{some, {sigv4a_signer, Region_set, true}}}.
-file("src/aws/internal/client/runtime.gleam", 142).
?DOC(
" Override the SigV4a `normalize_path` knob. No-op when the client\n"
" hasn't opted into SigV4a yet (`with_sigv4a_region_set` not\n"
" called). Pass `False` for S3 — its object-key paths can carry\n"
" `.` / `..` that the RFC 3986 dot-segment removal would otherwise\n"
" strip.\n"
).
-spec with_sigv4a_path_normalization(client_config(), boolean()) -> client_config().
with_sigv4a_path_normalization(Config, Normalize) ->
case erlang:element(13, Config) of
{some, S} ->
{client_config,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config),
erlang:element(6, Config),
erlang:element(7, Config),
erlang:element(8, Config),
erlang:element(9, Config),
erlang:element(10, Config),
erlang:element(11, Config),
erlang:element(12, Config),
{some, {sigv4a_signer, erlang:element(2, S), Normalize}}};
none ->
Config
end.
-file("src/aws/internal/client/runtime.gleam", 162).
?DOC(
" Override the credentials provider — use for non-default profiles,\n"
" in-process static creds, or any custom resolution chain.\n"
).
-spec with_credentials_provider(client_config(), aws@credentials:provider()) -> client_config().
with_credentials_provider(Config, Provider) ->
{client_config,
Provider,
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config),
erlang:element(6, Config),
erlang:element(7, Config),
erlang:element(8, Config),
erlang:element(9, Config),
erlang:element(10, Config),
erlang:element(11, Config),
erlang:element(12, Config),
erlang:element(13, Config)}.
-file("src/aws/internal/client/runtime.gleam", 256).
?DOC(
" Set a single client-level endpoint-rule-set parameter (e.g.\n"
" `\"UseFIPS\"` -> `BoolVal(True)`). Operation-specific params (S3\n"
" `Bucket`, `Key`) are threaded per-call via\n"
" `invoke_with_endpoint_params`.\n"
).
-spec with_endpoint_param(client_config(), binary(), aws@endpoints:value()) -> client_config().
with_endpoint_param(Config, Name, Value) ->
{client_config,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config),
erlang:element(6, Config),
erlang:element(7, Config),
erlang:element(8, Config),
erlang:element(9, Config),
erlang:element(10, Config),
erlang:element(11, Config),
gleam@dict:insert(erlang:element(12, Config), Name, Value),
erlang:element(13, Config)}.
-file("src/aws/internal/client/runtime.gleam", 183).
?DOC(
" Override the request endpoint URL. Used for LocalStack, FIPS\n"
" endpoints, custom DNS, and pre-signed-URL replay flows.\n"
"\n"
" When a Smithy endpoint rule set is attached (the codegen wires\n"
" one on every generated service that declares one), the override\n"
" is threaded into the rule set as the standard `Endpoint`\n"
" parameter rather than replacing `endpoint_url` outright. AWS\n"
" rule sets honour this parameter via an early-branch rule like\n"
" `case isSet(Endpoint) -> endpoint { url: \"{Endpoint}\" }`, so the\n"
" override wins consistently across all services that declare an\n"
" `Endpoint` rule-set parameter.\n"
"\n"
" The static `endpoint_url` is also updated so services without a\n"
" rule set continue to honour the override via the fallback path.\n"
).
-spec with_endpoint_url(client_config(), binary()) -> client_config().
with_endpoint_url(Config, Url) ->
_pipe = {client_config,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config),
Url,
erlang:element(7, Config),
erlang:element(8, Config),
erlang:element(9, Config),
erlang:element(10, Config),
erlang:element(11, Config),
erlang:element(12, Config),
erlang:element(13, Config)},
with_endpoint_param(_pipe, <<"Endpoint"/utf8>>, {string_val, Url}).
-file("src/aws/internal/client/runtime.gleam", 188).
-spec with_http_send(
client_config(),
fun((gleam@http@request:request(bitstring())) -> {ok,
gleam@http@response:response(bitstring())} |
{error, aws@internal@http_send:http_error()})
) -> client_config().
with_http_send(Config, Send) ->
{client_config,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config),
erlang:element(6, Config),
Send,
erlang:element(8, Config),
erlang:element(9, Config),
erlang:element(10, Config),
erlang:element(11, Config),
erlang:element(12, Config),
erlang:element(13, Config)}.
-file("src/aws/internal/client/runtime.gleam", 196).
?DOC(
" Override the streaming HTTP sender. Use for tests (stub the\n"
" transport), for forcing the buffered-then-streamed path via\n"
" `http_send.lift_to_streaming(custom_buffered)`, or to inject a\n"
" future custom transport (proxy, gRPC tunnel, etc.).\n"
).
-spec with_streaming_http_send(
client_config(),
fun((gleam@http@request:request(bitstring())) -> {ok,
gleam@http@response:response(aws@streaming:streaming_body())} |
{error, aws@internal@http_send:http_error()})
) -> client_config().
with_streaming_http_send(Config, Send) ->
{client_config,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config),
erlang:element(6, Config),
erlang:element(7, Config),
Send,
erlang:element(9, Config),
erlang:element(10, Config),
erlang:element(11, Config),
erlang:element(12, Config),
erlang:element(13, Config)}.
-file("src/aws/internal/client/runtime.gleam", 212).
?DOC(
" Switch the streaming sender to the HTTP/2 variant. httpc adds\n"
" `{http_version, \"HTTP/2\"}` to its option list; servers that don't\n"
" speak HTTP/2 negotiate down to HTTP/1.1 via ALPN, so calls keep\n"
" working even when the peer doesn't support it. Buffered requests\n"
" (`http_send`) are unaffected — HTTP/2 is for high-throughput\n"
" streaming endpoints (S3 multipart, Bedrock streaming, Transcribe).\n"
"\n"
" Pair with `with_streaming_http_send` for finer control (e.g. a\n"
" stubbed sender in tests that needs HTTP/2 wiring elsewhere).\n"
).
-spec with_http2(client_config()) -> client_config().
with_http2(Config) ->
{client_config,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config),
erlang:element(6, Config),
erlang:element(7, Config),
fun aws@internal@http_streaming:default_send_http2/1,
erlang:element(9, Config),
erlang:element(10, Config),
erlang:element(11, Config),
erlang:element(12, Config),
erlang:element(13, Config)}.
-file("src/aws/internal/client/runtime.gleam", 219).
?DOC(
" Override the retry strategy used to wrap `http_send`. Pass\n"
" `retry.standard()` for the AWS-standard 3-attempt backoff (the\n"
" default), or `retry.adaptive(bucket)` to add the token-bucket gate.\n"
).
-spec with_retry_strategy(client_config(), aws@retry:strategy()) -> client_config().
with_retry_strategy(Config, Strategy) ->
{client_config,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config),
erlang:element(6, Config),
erlang:element(7, Config),
erlang:element(8, Config),
erlang:element(9, Config),
Strategy,
erlang:element(11, Config),
erlang:element(12, Config),
erlang:element(13, Config)}.
-file("src/aws/internal/client/runtime.gleam", 233).
?DOC(
" Tune just the retry attempt budget without replacing the whole\n"
" strategy. Equivalent to\n"
" `with_retry_strategy(config, retry.with_max_attempts(config.retry_strategy, n))`,\n"
" but reads as a single knob — the common case for production\n"
" tuning. Pass `1` to disable retries entirely (single attempt\n"
" per request), `5` for long-running batch workloads that tolerate\n"
" extra wait, etc.\n"
).
-spec with_max_attempts(client_config(), integer()) -> client_config().
with_max_attempts(Config, N) ->
{client_config,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config),
erlang:element(6, Config),
erlang:element(7, Config),
erlang:element(8, Config),
erlang:element(9, Config),
aws@retry:with_max_attempts(erlang:element(10, Config), N),
erlang:element(11, Config),
erlang:element(12, Config),
erlang:element(13, Config)}.
-file("src/aws/internal/client/runtime.gleam", 245).
?DOC(
" Attach a Smithy endpoint rule set. When set, the runtime walks the rule\n"
" set per request to compute the endpoint URL — the value passed in via\n"
" `with_endpoint_url` (or `default_endpoint`) is then ignored except as a\n"
" fallback when the rule set is cleared. Use this from generated service\n"
" constructors that embed their service's rule set.\n"
).
-spec with_endpoint_rule_set(client_config(), aws@endpoints:rule_set()) -> client_config().
with_endpoint_rule_set(Config, Rule_set) ->
{client_config,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config),
erlang:element(6, Config),
erlang:element(7, Config),
erlang:element(8, Config),
erlang:element(9, Config),
erlang:element(10, Config),
{some, Rule_set},
erlang:element(12, Config),
erlang:element(13, Config)}.
-file("src/aws/internal/client/runtime.gleam", 765).
-spec normalise_error_type(binary()) -> binary().
normalise_error_type(Raw) ->
S = case gleam@string:split_once(Raw, <<":"/utf8>>) of
{ok, {Prefix, _}} ->
Prefix;
{error, _} ->
Raw
end,
S@1 = case gleam@string:split_once(S, <<","/utf8>>) of
{ok, {Prefix@1, _}} ->
Prefix@1;
{error, _} ->
S
end,
case gleam@string:split_once(S@1, <<"#"/utf8>>) of
{ok, {_, Local}} ->
Local;
{error, _} ->
S@1
end.
-file("src/aws/internal/client/runtime.gleam", 799).
?DOC(
" Pull the error code out of a restXml error body. Two shapes appear\n"
" in the wild — S3-style `<Error><Code>NoSuchBucket</Code>...</Error>`\n"
" and SQS/SNS-style `<ErrorResponse><Error><Code>X</Code>...</Error>...`.\n"
" In both cases the first `<Code>` element holds the error type, so a\n"
" single text search keyed on `<Code>` covers both shapes without\n"
" dragging in the full XML decoder for an error-only path. The trim\n"
" + empty-check rejects `<Code/>` and `<Code> </Code>` so the\n"
" fallback to \"Unknown\" still fires for malformed bodies.\n"
).
-spec extract_xml_error_code(binary()) -> {ok, binary()} | {error, nil}.
extract_xml_error_code(Body) ->
gleam@result:'try'(
aws@internal@text_scan:xml_tag_text(Body, <<"Code"/utf8>>),
fun(Raw) -> case gleam@string:trim(Raw) of
<<""/utf8>> ->
{error, nil};
Non_empty ->
{ok, Non_empty}
end end
).
-file("src/aws/internal/client/runtime.gleam", 780).
-spec error_type_from_body(binary()) -> binary().
error_type_from_body(Body) ->
Found = begin
_pipe = aws@internal@text_scan:json_string_after_key(
Body,
<<"__type"/utf8>>
),
_pipe@1 = gleam@result:lazy_or(
_pipe,
fun() ->
aws@internal@text_scan:json_string_after_key(
Body,
<<"code"/utf8>>
)
end
),
gleam@result:lazy_or(_pipe@1, fun() -> extract_xml_error_code(Body) end)
end,
case Found of
{ok, V} ->
normalise_error_type(V);
{error, _} ->
<<"Unknown"/utf8>>
end.
-file("src/aws/internal/client/runtime.gleam", 716).
?DOC(
" Pull the wire-error-type local name out of a response. Looks at the\n"
" `X-Amzn-Errortype` header first (restJson1, awsJson*); falls back to\n"
" the body for `__type` / `code` / `<Code>` (covers JSON and XML\n"
" error shapes). The returned string is the *local* shape name —\n"
" namespace prefix, URI suffix, and Smithy `[Charlie,foo,bar]` suffix\n"
" are all stripped. Exposed for codegen-emitted error-shape protocol\n"
" test dispatchers; the in-process retry path uses it via the\n"
" `ServiceError` discriminator.\n"
).
-spec extract_error_type(gleam@dict:dict(binary(), binary()), bitstring()) -> binary().
extract_error_type(Headers, Body) ->
case gleam_stdlib:map_get(Headers, <<"x-amzn-errortype"/utf8>>) of
{ok, V} ->
normalise_error_type(V);
{error, _} ->
case gleam@bit_array:to_string(Body) of
{error, _} ->
<<"Unknown"/utf8>>;
{ok, Text} ->
error_type_from_body(Text)
end
end.
-file("src/aws/internal/client/runtime.gleam", 653).
-spec headers_to_dict(list({binary(), binary()})) -> gleam@dict:dict(binary(), binary()).
headers_to_dict(Headers) ->
gleam@list:fold(
Headers,
maps:new(),
fun(Acc, P) ->
gleam@dict:insert(
Acc,
string:lowercase(erlang:element(1, P)),
erlang:element(2, P)
)
end
).
-file("src/aws/internal/client/runtime.gleam", 638).
-spec parse_method(binary()) -> gleam@http:method().
parse_method(Method) ->
case string:uppercase(Method) of
<<"GET"/utf8>> ->
get;
<<"POST"/utf8>> ->
post;
<<"PUT"/utf8>> ->
put;
<<"DELETE"/utf8>> ->
delete;
<<"HEAD"/utf8>> ->
head;
<<"PATCH"/utf8>> ->
patch;
<<"OPTIONS"/utf8>> ->
options;
<<"TRACE"/utf8>> ->
trace;
<<"CONNECT"/utf8>> ->
connect;
_ ->
post
end.
-file("src/aws/internal/client/runtime.gleam", 509).
-spec sign_sigv4(
aws@internal@http_request:http_request(),
aws@credentials:credentials(),
client_config()
) -> aws@internal@http_request:http_request().
sign_sigv4(Unsigned, Creds, Config) ->
Opts = {signing_options,
(erlang:element(9, Config))(),
erlang:element(3, Config),
erlang:element(5, Config),
true,
true,
false},
Signing_creds = {signing_credentials,
erlang:element(2, Creds),
erlang:element(3, Creds),
erlang:element(4, Creds)},
aws@internal@sigv4:sign(Unsigned, Signing_creds, Opts).
-file("src/aws/internal/client/runtime.gleam", 532).
-spec sign_sigv4a(
aws@internal@http_request:http_request(),
aws@credentials:credentials(),
client_config(),
sigv4a_signer()
) -> aws@internal@http_request:http_request().
sign_sigv4a(Unsigned, Creds, Config, Signer) ->
Private_key = aws@internal@sigv4a:derive_signing_key(
erlang:element(2, Creds),
erlang:element(3, Creds)
),
Sigv4a_creds = {sigv4a_credentials,
erlang:element(2, Creds),
Private_key,
erlang:element(4, Creds)},
Opts = {sigv4a_options,
(erlang:element(9, Config))(),
erlang:element(2, Signer),
erlang:element(5, Config),
true,
erlang:element(3, Signer),
false},
aws@internal@sigv4a:sign_with_credentials(Unsigned, Sigv4a_creds, Opts).
-file("src/aws/internal/client/runtime.gleam", 604).
-spec host_from_endpoint(binary()) -> binary().
host_from_endpoint(Url) ->
After = case gleam@string:split_once(Url, <<"://"/utf8>>) of
{ok, {_, Rest}} ->
Rest;
{error, _} ->
Url
end,
case gleam@string:split_once(After, <<"/"/utf8>>) of
{ok, {Host, _}} ->
Host;
{error, _} ->
After
end.
-file("src/aws/internal/client/runtime.gleam", 626).
?DOC(
" Prepend `prefix` to the authority portion of `url`. Scheme + path +\n"
" query are preserved. `prefix` is already-substituted (e.g. `\"foo.\"`\n"
" — no `{Label}` placeholders left). Mirrors the Rust SDK's\n"
" `apply_endpoint_to_request` (`format!(\"{scheme}://{prefix}{authority}{path_and_query}\")`).\n"
"\n"
" Examples:\n"
" `inject_host_prefix(\"https://s3.us-east-1.amazonaws.com/\", \"foo.\")`\n"
" → `\"https://foo.s3.us-east-1.amazonaws.com/\"`\n"
).
-spec inject_host_prefix(binary(), binary()) -> binary().
inject_host_prefix(Url, Prefix) ->
{Scheme, Rest} = case gleam@string:split_once(Url, <<"://"/utf8>>) of
{ok, {S, R}} ->
{<<S/binary, "://"/utf8>>, R};
{error, _} ->
{<<""/utf8>>, Url}
end,
{Authority, Path_and_query} = case gleam@string:split_once(
Rest,
<<"/"/utf8>>
) of
{ok, {A, P}} ->
{A, <<"/"/utf8, P/binary>>};
{error, _} ->
{Rest, <<""/utf8>>}
end,
<<<<<<Scheme/binary, Prefix/binary>>/binary, Authority/binary>>/binary,
Path_and_query/binary>>.
-file("src/aws/internal/client/runtime.gleam", 592).
-spec describe_endpoint_error(aws@endpoints:resolve_error()) -> binary().
describe_endpoint_error(Err) ->
case Err of
{rule_error, M} ->
<<"endpoint rule error: "/utf8, M/binary>>;
no_match ->
<<"endpoint rule set: no match"/utf8>>;
{invalid_rule_set, R} ->
<<"invalid endpoint rule set: "/utf8, R/binary>>;
{unsupported, R@1} ->
<<"endpoint unsupported: "/utf8, R@1/binary>>;
{missing_parameter, N} ->
<<"endpoint parameter missing: "/utf8, N/binary>>;
{required_parameter_missing, N@1} ->
<<"endpoint required parameter missing: "/utf8, N@1/binary>>
end.
-file("src/aws/internal/client/runtime.gleam", 588).
-spec merge_params(
gleam@dict:dict(binary(), aws@endpoints:value()),
gleam@dict:dict(binary(), aws@endpoints:value())
) -> gleam@dict:dict(binary(), aws@endpoints:value()).
merge_params(Base, Overlay) ->
gleam@dict:fold(
Overlay,
Base,
fun(Acc, K, V) -> gleam@dict:insert(Acc, K, V) end
).
-file("src/aws/internal/client/runtime.gleam", 567).
?DOC(
" Compute the request URL using the rule set if attached, otherwise fall\n"
" back to the static `endpoint_url`. Returns a runtime error if the rule\n"
" set can't be resolved — bubbles up as `DecodeError` for now so existing\n"
" callers don't need a new variant.\n"
).
-spec resolve_endpoint_url(
client_config(),
gleam@dict:dict(binary(), aws@endpoints:value())
) -> {ok, binary()} | {error, client_error()}.
resolve_endpoint_url(Config, Op_params) ->
case erlang:element(11, Config) of
none ->
{ok, erlang:element(6, Config)};
{some, Rs} ->
Params = gleam@dict:insert(
merge_params(erlang:element(12, Config), Op_params),
<<"Region"/utf8>>,
{string_val, erlang:element(3, Config)}
),
case aws@endpoints:resolve(Rs, Params) of
{ok, Endpoint} ->
{ok, erlang:element(2, Endpoint)};
{error, Err} ->
{error, {decode_error, describe_endpoint_error(Err)}}
end
end.
-file("src/aws/internal/client/runtime.gleam", 442).
-spec prepare_signed_request(
client_config(),
gleam@dict:dict(binary(), aws@endpoints:value()),
gleam@option:option(binary()),
{binary(), binary(), gleam@dict:dict(binary(), binary()), bitstring()}
) -> {ok, gleam@http@request:request(bitstring())} | {error, client_error()}.
prepare_signed_request(Config, Op_params, Host_prefix, Built) ->
{Method, Uri, Headers, Body} = Built,
gleam@result:'try'(
begin
_pipe = aws@credentials:fetch(erlang:element(2, Config)),
gleam@result:map_error(
_pipe,
fun(Field@0) -> {credentials_error, Field@0} end
)
end,
fun(Creds) ->
gleam@result:'try'(
resolve_endpoint_url(Config, Op_params),
fun(Endpoint_url) ->
{Endpoint_url@1, Host} = case Host_prefix of
none ->
{Endpoint_url, host_from_endpoint(Endpoint_url)};
{some, Prefix} ->
Prefixed_url = inject_host_prefix(
Endpoint_url,
Prefix
),
{Prefixed_url, host_from_endpoint(Prefixed_url)}
end,
Header_pairs = begin
_pipe@1 = [{<<"host"/utf8>>, Host} |
maps:to_list(Headers)],
gleam@list:map(
_pipe@1,
fun(P) ->
{header,
erlang:element(1, P),
erlang:element(2, P)}
end
)
end,
{Path_only, Query_str} = case gleam@string:split_once(
Uri,
<<"?"/utf8>>
) of
{ok, {P@1, Q}} ->
{P@1, Q};
{error, _} ->
{Uri, <<""/utf8>>}
end,
Unsigned = {http_request,
Method,
Path_only,
Query_str,
Header_pairs,
Body},
Signed = case erlang:element(13, Config) of
{some, Signer} ->
sign_sigv4a(Unsigned, Creds, Config, Signer);
none ->
sign_sigv4(Unsigned, Creds, Config)
end,
Full_url = <<Endpoint_url@1/binary, Uri/binary>>,
Base@1 = case gleam@http@request:to(Full_url) of
{ok, Base} -> Base;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"aws/internal/client/runtime"/utf8>>,
function => <<"prepare_signed_request"/utf8>>,
line => 497,
value => _assert_fail,
start => 19055,
'end' => 19097,
pattern_start => 19066,
pattern_end => 19074})
end,
Http_req = begin
_pipe@2 = Base@1,
_pipe@3 = gleam@http@request:set_method(
_pipe@2,
parse_method(Method)
),
gleam@http@request:set_body(_pipe@3, Body)
end,
Http_req@1 = gleam@list:fold(
erlang:element(5, Signed),
Http_req,
fun(R, H) ->
gleam@http@request:set_header(
R,
erlang:element(2, H),
erlang:element(3, H)
)
end
),
{ok, Http_req@1}
end
)
end
).
-file("src/aws/internal/client/runtime.gleam", 306).
?DOC(
" `invoke_with_endpoint_params` with an already-substituted host\n"
" prefix. Codegen passes `Some(prefix)` for ops carrying\n"
" `@smithy.api#endpoint.hostPrefix` — the template (e.g.\n"
" `\"{RequestRoute}.\"`) is expanded against the input's `@hostLabel`\n"
" members in the generated wrapper, mirroring the Rust SDK's\n"
" `read_before_execution` interceptor.\n"
).
-spec invoke_with_endpoint_params_and_host_prefix(
client_config(),
gleam@dict:dict(binary(), aws@endpoints:value()),
gleam@option:option(binary()),
{binary(), binary(), gleam@dict:dict(binary(), binary()), bitstring()},
fun((integer(), gleam@dict:dict(binary(), binary()), bitstring()) -> {ok,
LFE} |
{error, binary()})
) -> {ok, LFE} | {error, client_error()}.
invoke_with_endpoint_params_and_host_prefix(
Config,
Op_params,
Host_prefix,
Built,
Parse
) ->
gleam@result:'try'(
prepare_signed_request(Config, Op_params, Host_prefix, Built),
fun(Http_req) ->
Send = aws@retry:with_retry(
erlang:element(7, Config),
erlang:element(10, Config)
),
gleam@result:'try'(
begin
_pipe = Send(Http_req),
gleam@result:map_error(
_pipe,
fun(Field@0) -> {transport_error, Field@0} end
)
end,
fun(Resp) ->
Resp_headers = headers_to_dict(erlang:element(3, Resp)),
case (erlang:element(2, Resp) >= 200) andalso (erlang:element(
2,
Resp
)
< 300) of
true ->
_pipe@1 = Parse(
erlang:element(2, Resp),
Resp_headers,
erlang:element(4, Resp)
),
gleam@result:map_error(
_pipe@1,
fun(Reason) -> {decode_error, Reason} end
);
false ->
Error_type = extract_error_type(
Resp_headers,
erlang:element(4, Resp)
),
{error,
{service_error,
erlang:element(2, Resp),
Error_type,
erlang:element(4, Resp)}}
end
end
)
end
).
-file("src/aws/internal/client/runtime.gleam", 285).
?DOC(
" Same as `invoke` but with extra rule-set parameters merged in for this\n"
" operation only — used by generated S3 ops to supply `Bucket`/`Key` etc.\n"
" without leaking them onto the client config. If the client has no\n"
" `endpoint_rule_set`, `op_params` is ignored (the static `endpoint_url`\n"
" is used).\n"
).
-spec invoke_with_endpoint_params(
client_config(),
gleam@dict:dict(binary(), aws@endpoints:value()),
{binary(), binary(), gleam@dict:dict(binary(), binary()), bitstring()},
fun((integer(), gleam@dict:dict(binary(), binary()), bitstring()) -> {ok,
LEU} |
{error, binary()})
) -> {ok, LEU} | {error, client_error()}.
invoke_with_endpoint_params(Config, Op_params, Built, Parse) ->
invoke_with_endpoint_params_and_host_prefix(
Config,
Op_params,
none,
Built,
Parse
).
-file("src/aws/internal/client/runtime.gleam", 272).
?DOC(
" Run one operation end-to-end. See module docs for the pipeline.\n"
"\n"
" Operations that need to thread rule-set parameters known only to the\n"
" op itself (e.g. S3's `Bucket`) should use `invoke_with_endpoint_params`\n"
" instead and pass those parameters through `op_params`.\n"
).
-spec invoke(
client_config(),
{binary(), binary(), gleam@dict:dict(binary(), binary()), bitstring()},
fun((integer(), gleam@dict:dict(binary(), binary()), bitstring()) -> {ok,
LEL} |
{error, binary()})
) -> {ok, LEL} | {error, client_error()}.
invoke(Config, Built, Parse) ->
invoke_with_endpoint_params(Config, maps:new(), Built, Parse).
-file("src/aws/internal/client/runtime.gleam", 411).
-spec streaming_error(
gleam@http@response:response(aws@streaming:streaming_body())
) -> client_error().
streaming_error(Resp) ->
Resp_headers = headers_to_dict(erlang:element(3, Resp)),
case aws@streaming:to_bit_array_max(erlang:element(4, Resp), 1048576) of
{ok, Body} ->
{service_error,
erlang:element(2, Resp),
extract_error_type(Resp_headers, Body),
Body};
{error, _} ->
{decode_error,
<<<<"streaming error body exceeded "/utf8,
(erlang:integer_to_binary(1048576))/binary>>/binary,
" bytes — refusing to materialise for typed-error extraction"/utf8>>}
end.
-file("src/aws/internal/client/runtime.gleam", 377).
?DOC(
" `invoke_streaming` with per-op endpoint-rule-set parameters (the\n"
" streaming-side counterpart to `invoke_with_endpoint_params`).\n"
).
-spec invoke_streaming_with_endpoint_params(
client_config(),
gleam@dict:dict(binary(), aws@endpoints:value()),
{binary(), binary(), gleam@dict:dict(binary(), binary()), bitstring()}
) -> {ok, aws@streaming:response()} | {error, client_error()}.
invoke_streaming_with_endpoint_params(Config, Op_params, Built) ->
gleam@result:'try'(
prepare_signed_request(Config, Op_params, none, Built),
fun(Http_req) ->
gleam@result:'try'(
begin
_pipe = (erlang:element(8, Config))(Http_req),
gleam@result:map_error(
_pipe,
fun(Field@0) -> {transport_error, Field@0} end
)
end,
fun(Resp) ->
case (erlang:element(2, Resp) >= 200) andalso (erlang:element(
2,
Resp
)
< 300) of
true ->
{ok,
{response,
erlang:element(2, Resp),
erlang:element(3, Resp),
erlang:element(4, Resp)}};
false ->
{error, streaming_error(Resp)}
end
end
)
end
).
-file("src/aws/internal/client/runtime.gleam", 368).
?DOC(
" Streaming-response variant of `invoke`. Builds + signs the\n"
" request exactly like `invoke`, but dispatches through\n"
" `streaming_http_send` (chunked transport) and returns the raw\n"
" `Response(StreamingBody)` so callers can consume the body\n"
" incrementally — fold chunk-by-chunk via `streaming.fold_chunks`,\n"
" decode event-stream frames via `event_stream.fold_events`, or\n"
" stream-to-disk without buffering.\n"
"\n"
" Used by generated codegen for operations whose output carries\n"
" `@streaming` — `S3.GetObject` for multi-GB downloads,\n"
" `Transcribe.StartStreamTranscription` and `Kinesis.SubscribeToShard`\n"
" for event-stream responses, etc.\n"
"\n"
" Error responses (non-2xx) are materialised via\n"
" `streaming.to_bit_array_max(body, 1 MiB)` so `error_type`\n"
" extraction works on the JSON/XML error body the same way as\n"
" `invoke`. A response body that exceeds the 1 MiB cap on the\n"
" error path surfaces as `DecodeError` since we can't safely\n"
" extract a typed error from an oversized error body.\n"
"\n"
" Retry is intentionally NOT wrapped around `streaming_http_send`\n"
" at this layer — replaying a streaming request after a transient\n"
" failure is op-specific (idempotent vs. mutating). Callers that\n"
" want retry on a streaming op should layer it themselves or\n"
" drop down to the buffered `invoke`.\n"
).
-spec invoke_streaming(
client_config(),
{binary(), binary(), gleam@dict:dict(binary(), binary()), bitstring()}
) -> {ok, aws@streaming:response()} | {error, client_error()}.
invoke_streaming(Config, Built) ->
invoke_streaming_with_endpoint_params(Config, maps:new(), Built).
-file("src/aws/internal/client/runtime.gleam", 665).
?DOC(
" Match an AWS `error_type` wire value against a local Smithy\n"
" shape name. Used by the generated per-op `translate_<op>_error`\n"
" dispatchers. `error_type` already passes through\n"
" `normalise_error_type` (namespace + suffix stripped) at the\n"
" invoke layer, so a plain equality check suffices; we keep this\n"
" behind a helper to give the codegen one stable call-site.\n"
).
-spec error_type_matches(binary(), binary()) -> boolean().
error_type_matches(Error_type, Local) ->
Error_type =:= Local.
-file("src/aws/internal/client/runtime.gleam", 681).
?DOC(
" Generic translator from the runtime's `ClientError` to a per-op\n"
" typed-error enum. Each generated `translate_<op>_error` is a\n"
" one-liner that supplies its operation's decoder table plus\n"
" constructors for the always-present `*Transport` and `*Unknown`\n"
" variants. Saves ~15–25 LOC/op vs the previous open-coded nested\n"
" match.\n"
"\n"
" `decoders` is a list of `(wire_error_type_local_name, decoder)`\n"
" pairs. The first pair whose error_type matches gets to attempt the\n"
" decode; if its decoder returns `Error(Nil)`, we fall back to\n"
" `on_unknown` with the textified body so the caller still sees\n"
" something useful instead of a panic.\n"
).
-spec translate_service_error(
client_error(),
list({binary(), fun((binary()) -> {ok, LGD} | {error, nil})}),
fun((binary()) -> LGD),
fun((binary(), integer(), binary()) -> LGD)
) -> LGD.
translate_service_error(Err, Decoders, On_transport, On_unknown) ->
case Err of
{service_error, S, Et, B} ->
Text = case gleam@bit_array:to_string(B) of
{ok, T} ->
T;
{error, _} ->
<<""/utf8>>
end,
case gleam@list:find(
Decoders,
fun(D) -> error_type_matches(Et, erlang:element(1, D)) end
) of
{ok, {_, Decoder}} ->
case Decoder(Text) of
{ok, V} ->
V;
{error, _} ->
On_unknown(Et, S, Text)
end;
{error, _} ->
On_unknown(Et, S, Text)
end;
{transport_error, _} ->
On_transport(<<"transport error"/utf8>>);
{credentials_error, _} ->
On_transport(<<"credentials error"/utf8>>);
{decode_error, R} ->
On_transport(<<"decode: "/utf8, R/binary>>)
end.
-file("src/aws/internal/client/runtime.gleam", 743).
?DOC(
" Discriminator check for protocol-test error-shape dispatchers. Used\n"
" by the generated `parse_<err>_response` function: if the wire-side\n"
" discriminator (header or body) resolves to `expected_local`, the\n"
" response was routed to the right error shape and the dispatcher\n"
" reports `Ok(Nil)`. The runner's response-side assertion is binary\n"
" — `Ok` vs `Error` — so returning `Nil` is enough.\n"
"\n"
" The runner hands fixture headers through with their literal-case\n"
" keys (`X-Amzn-Errortype`), but `extract_error_type` expects the\n"
" lowercased form that the real-request path produces via\n"
" `headers_to_dict`. We lowercase here so the helper matches HTTP's\n"
" case-insensitive header semantics regardless of which call-site\n"
" invokes it.\n"
).
-spec check_error_type_matches(
gleam@dict:dict(binary(), binary()),
bitstring(),
binary()
) -> {ok, nil} | {error, binary()}.
check_error_type_matches(Headers, Body, Expected_local) ->
Lower = gleam@dict:fold(
Headers,
maps:new(),
fun(Acc, K, V) -> gleam@dict:insert(Acc, string:lowercase(K), V) end
),
Extracted = extract_error_type(Lower, Body),
case Extracted =:= Expected_local of
true ->
{ok, nil};
false ->
{error,
<<<<<<"error type mismatch: expected "/utf8,
Expected_local/binary>>/binary,
", got "/utf8>>/binary,
Extracted/binary>>}
end.