-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", 157).
-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", 96).
?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", 128).
?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", 143).
?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", 163).
?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", 257).
?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", 184).
?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", 189).
-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", 197).
?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", 213).
?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", 220).
?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", 234).
?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", 246).
?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", 902).
-spec body_preview(bitstring()) -> binary().
body_preview(Body) ->
Excerpt = case erlang:byte_size(Body) > 512 of
true ->
gleam_stdlib:bit_array_slice(Body, 0, 512);
false ->
{ok, Body}
end,
case Excerpt of
{ok, Bytes} ->
case gleam@bit_array:to_string(Bytes) of
{ok, Text} ->
Text;
{error, _} ->
<<"<non-utf8 body>"/utf8>>
end;
{error, _} ->
<<"<body>"/utf8>>
end.
-file("src/aws/internal/client/runtime.gleam", 775).
?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) ->
aws@internal@error_code:from_headers_and_body(Headers, Body).
-file("src/aws/internal/client/runtime.gleam", 712).
-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", 889).
-spec describe_http_error(aws@internal@http_send:http_error()) -> binary().
describe_http_error(Err) ->
case Err of
{connect_failed, R} ->
<<"connect failed: "/utf8, R/binary>>;
timeout ->
<<"timeout"/utf8>>;
{invalid_body, R@1} ->
<<"invalid body: "/utf8, R@1/binary>>;
{other, R@2} ->
R@2
end.
-file("src/aws/internal/client/runtime.gleam", 863).
?DOC(
" Case-insensitive header lookup over the built request headers (whose key\n"
" casing is decided by the per-protocol codegen). Only reached on the\n"
" debug path, so the fold cost is paid only when debug is on.\n"
).
-spec header_ci(gleam@dict:dict(binary(), binary()), binary()) -> {ok, binary()} |
{error, nil}.
header_ci(Headers, Lower_name) ->
gleam@dict:fold(Headers, {error, nil}, fun(Acc, Key, Value) -> case Acc of
{ok, _} ->
Acc;
{error, _} ->
case string:lowercase(Key) =:= Lower_name of
true ->
{ok, Value};
false ->
{error, nil}
end
end end).
-file("src/aws/internal/client/runtime.gleam", 847).
?DOC(
" One-line request summary for the `debug` request log: service, method,\n"
" resolved host + URI, and — for the JSON-RPC protocols — the `X-Amz-Target`\n"
" operation. Built lazily (only when debug is on).\n"
).
-spec request_summary(
client_config(),
{binary(), binary(), gleam@dict:dict(binary(), binary()), bitstring()},
gleam@http@request:request(bitstring())
) -> binary().
request_summary(Config, Built, Req) ->
{Method, Uri, Headers, _} = Built,
Operation = case header_ci(Headers, <<"x-amz-target"/utf8>>) of
{ok, Target} ->
<<" "/utf8, Target/binary>>;
{error, _} ->
<<""/utf8>>
end,
<<<<<<<<<<<<(erlang:element(5, Config))/binary, " "/utf8>>/binary,
Method/binary>>/binary,
" "/utf8>>/binary,
(erlang:element(6, Req))/binary>>/binary,
Uri/binary>>/binary,
Operation/binary>>.
-file("src/aws/internal/client/runtime.gleam", 879).
-spec describe_client_error(client_error()) -> binary().
describe_client_error(Err) ->
case Err of
{credentials_error, _} ->
<<"credentials error"/utf8>>;
{transport_error, E} ->
<<"transport: "/utf8, (describe_http_error(E))/binary>>;
{decode_error, R} ->
<<"decode: "/utf8, R/binary>>;
{service_error, S, T, _} ->
<<<<<<"service "/utf8, (erlang:integer_to_binary(S))/binary>>/binary,
" "/utf8>>/binary,
T/binary>>
end.
-file("src/aws/internal/client/runtime.gleam", 831).
?DOC(
" Run a side-effecting debug log on the `Error` branch, passing the error\n"
" through unchanged. Used to narrate request-preparation failures\n"
" (credential resolution, endpoint rule-set resolution) at `debug`.\n"
).
-spec tap_client_error({ok, LLO} | {error, client_error()}, client_config()) -> {ok,
LLO} |
{error, client_error()}.
tap_client_error(Result, Config) ->
_pipe = Result,
gleam@result:map_error(
_pipe,
fun(Err) ->
aws@internal@log:debug(
fun() ->
<<<<<<"aws ✗ "/utf8, (erlang:element(5, Config))/binary>>/binary,
" "/utf8>>/binary,
(describe_client_error(Err))/binary>>
end
),
Err
end
).
-file("src/aws/internal/client/runtime.gleam", 697).
-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", 568).
-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", 591).
-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", 663).
-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", 685).
?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", 651).
-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", 647).
-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", 626).
?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", 492).
-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>>,
gleam@result:'try'(
begin
_pipe@2 = gleam@http@request:to(Full_url),
gleam@result:replace_error(
_pipe@2,
{decode_error,
<<"invalid endpoint url: "/utf8,
Full_url/binary>>}
)
end,
fun(Base) ->
Http_req = begin
_pipe@3 = Base,
_pipe@4 = gleam@http@request:set_method(
_pipe@3,
parse_method(Method)
),
gleam@http@request:set_body(_pipe@4, 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
)
end
).
-file("src/aws/internal/client/runtime.gleam", 307).
?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,
LKF} |
{error, binary()})
) -> {ok, LKF} | {error, client_error()}.
invoke_with_endpoint_params_and_host_prefix(
Config,
Op_params,
Host_prefix,
Built,
Parse
) ->
gleam@result:'try'(
begin
_pipe = prepare_signed_request(
Config,
Op_params,
Host_prefix,
Built
),
tap_client_error(_pipe, Config)
end,
fun(Http_req) ->
aws@internal@log:debug(
fun() ->
<<"aws → "/utf8,
(request_summary(Config, Built, Http_req))/binary>>
end
),
Send = aws@retry:with_retry(
erlang:element(7, Config),
erlang:element(10, Config)
),
gleam@result:'try'(
begin
_pipe@1 = Send(Http_req),
gleam@result:map_error(
_pipe@1,
fun(Err) ->
aws@internal@log:debug(
fun() ->
<<<<<<"aws ✗ "/utf8,
(erlang:element(5, Config))/binary>>/binary,
" transport: "/utf8>>/binary,
(describe_http_error(Err))/binary>>
end
),
{transport_error, Err}
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 ->
aws@internal@log:debug(
fun() ->
<<<<<<"aws ← "/utf8,
(erlang:integer_to_binary(
erlang:element(2, Resp)
))/binary>>/binary,
" "/utf8>>/binary,
(erlang:element(5, Config))/binary>>
end
),
_pipe@2 = Parse(
erlang:element(2, Resp),
Resp_headers,
erlang:element(4, Resp)
),
gleam@result:map_error(
_pipe@2,
fun(Reason) ->
aws@internal@log:debug(
fun() ->
<<<<<<"aws ✗ "/utf8,
(erlang:element(
5,
Config
))/binary>>/binary,
" decode: "/utf8>>/binary,
Reason/binary>>
end
),
{decode_error, Reason}
end
);
false ->
Error_type = extract_error_type(
Resp_headers,
erlang:element(4, Resp)
),
aws@internal@log:debug(
fun() ->
<<<<<<<<<<<<<<"aws ✗ "/utf8,
(erlang:element(
5,
Config
))/binary>>/binary,
" "/utf8>>/binary,
(erlang:integer_to_binary(
erlang:element(
2,
Resp
)
))/binary>>/binary,
" "/utf8>>/binary,
Error_type/binary>>/binary,
": "/utf8>>/binary,
(body_preview(erlang:element(4, Resp)))/binary>>
end
),
{error,
{service_error,
erlang:element(2, Resp),
Error_type,
erlang:element(4, Resp)}}
end
end
)
end
).
-file("src/aws/internal/client/runtime.gleam", 286).
?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,
LJV} |
{error, binary()})
) -> {ok, LJV} | {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", 273).
?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,
LJM} |
{error, binary()})
) -> {ok, LJM} | {error, client_error()}.
invoke(Config, Built, Parse) ->
invoke_with_endpoint_params(Config, maps:new(), Built, Parse).
-file("src/aws/internal/client/runtime.gleam", 461).
-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", 404).
?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'(
begin
_pipe = prepare_signed_request(Config, Op_params, none, Built),
tap_client_error(_pipe, Config)
end,
fun(Http_req) ->
aws@internal@log:debug(
fun() ->
<<<<"aws → "/utf8,
(request_summary(Config, Built, Http_req))/binary>>/binary,
" (streaming)"/utf8>>
end
),
gleam@result:'try'(
begin
_pipe@1 = (erlang:element(8, Config))(Http_req),
gleam@result:map_error(
_pipe@1,
fun(Err) ->
aws@internal@log:debug(
fun() ->
<<<<<<"aws ✗ "/utf8,
(erlang:element(5, Config))/binary>>/binary,
" transport: "/utf8>>/binary,
(describe_http_error(Err))/binary>>
end
),
{transport_error, Err}
end
)
end,
fun(Resp) ->
case (erlang:element(2, Resp) >= 200) andalso (erlang:element(
2,
Resp
)
< 300) of
true ->
aws@internal@log:debug(
fun() ->
<<<<<<<<"aws ← "/utf8,
(erlang:integer_to_binary(
erlang:element(2, Resp)
))/binary>>/binary,
" "/utf8>>/binary,
(erlang:element(5, Config))/binary>>/binary,
" (streaming)"/utf8>>
end
),
{ok,
{response,
erlang:element(2, Resp),
erlang:element(3, Resp),
erlang:element(4, Resp)}};
false ->
Err@1 = streaming_error(Resp),
aws@internal@log:debug(
fun() ->
<<<<<<"aws ✗ "/utf8,
(erlang:element(5, Config))/binary>>/binary,
" "/utf8>>/binary,
(describe_client_error(Err@1))/binary>>
end
),
{error, Err@1}
end
end
)
end
).
-file("src/aws/internal/client/runtime.gleam", 395).
?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", 724).
?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", 740).
?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, LLE} | {error, nil})}),
fun((binary()) -> LLE),
fun((binary(), integer(), binary()) -> LLE)
) -> LLE.
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", 795).
?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.