aws/internal/client/runtime
Shared runtime for awsJson1_0 + awsJson1_1 generated clients.
Generated per-service modules carry the per-operation build_* /
parse_* codec pair plus the service-level metadata (endpoint
prefix, signing name, region). They call into invoke here for
everything else: credential resolution, endpoint URL construction,
SigV4 signing, HTTP dispatch, response parsing.
This keeps the generated code small: one invoke call per
operation rather than ~30 lines of glue per op duplicated 57×
across DynamoDB.
Types
Configuration carried inside every generated Client. See module
docs for what’s threaded through where.
endpoint_rule_set and endpoint_params are the Smithy endpoint
resolution inputs. When endpoint_rule_set is Some, every invoke
call computes the request URL by walking the rule set against
endpoint_params merged with {Region: region} and any operation-
specific parameters threaded through invoke_with_endpoint_params.
When it’s None, endpoint_url is used verbatim — that’s the
pre-M3 behaviour the runtime keeps as a fallback.
pub type ClientConfig {
ClientConfig(
provider: credentials.Provider,
region: String,
endpoint_prefix: String,
signing_name: String,
endpoint_url: String,
http_send: fn(request.Request(BitArray)) -> Result(
response.Response(BitArray),
http_send.HttpError,
),
streaming_http_send: fn(request.Request(BitArray)) -> Result(
response.Response(streaming.StreamingBody),
http_send.HttpError,
),
timestamp: fn() -> String,
retry_strategy: retry.Strategy,
endpoint_rule_set: option.Option(endpoints.RuleSet),
endpoint_params: dict.Dict(String, endpoints.Value),
sigv4a_signer: option.Option(Sigv4aSigner),
)
}
Constructors
-
ClientConfig( provider: credentials.Provider, region: String, endpoint_prefix: String, signing_name: String, endpoint_url: String, http_send: fn(request.Request(BitArray)) -> Result( response.Response(BitArray), http_send.HttpError, ), streaming_http_send: fn(request.Request(BitArray)) -> Result( response.Response(streaming.StreamingBody), http_send.HttpError, ), timestamp: fn() -> String, retry_strategy: retry.Strategy, endpoint_rule_set: option.Option(endpoints.RuleSet), endpoint_params: dict.Dict(String, endpoints.Value), sigv4a_signer: option.Option(Sigv4aSigner), )Arguments
- streaming_http_send
-
Streaming HTTP sender used by
@streamingoperations. Buffered callers (the majority of AWS APIs) keep usinghttp_send; the codegen for streaming operations threads this one instead so large object GETs (S3) or long-lived subscription streams (Kinesis, Bedrock) can consume chunks incrementally without the runtime buffering the full response. - sigv4a_signer
-
SigV4a signing opt-in. When
Some,prepare_signed_requestderives an EC scalar from the provider’s IAM credentials and signs viasigv4a.sign_with_credentialsinstead of the defaultsigv4.sign. Required for S3 Multi-Region Access Points and any other endpoint that demands AWS4-ECDSA-P256.
Errors surfaced from a generated <op>(client, input) call.
pub type ClientError {
CredentialsError(credentials.ProviderError)
TransportError(http_send.HttpError)
DecodeError(reason: String)
ServiceError(status: Int, error_type: String, body: BitArray)
}
Constructors
-
CredentialsError(credentials.ProviderError) -
TransportError(http_send.HttpError) -
DecodeError(reason: String) -
ServiceError(status: Int, error_type: String, body: BitArray)
Per-Client SigV4a opt-in state. Carries the region set the
signature binds to (X-Amz-Region-Set); the IAM credentials
themselves still flow through the regular config.provider,
so credential rotation / refresh continues to work via the
existing credentials_cache path. normalize_path defaults to
True (the typical AWS service); S3 callers need False so
object keys with . / .. survive the canonical-request step.
pub type Sigv4aSigner {
Sigv4aSigner(region_set: List(String), normalize_path: Bool)
}
Constructors
-
Sigv4aSigner(region_set: List(String), normalize_path: Bool)
Values
pub fn check_error_type_matches(
headers: dict.Dict(String, String),
body: BitArray,
expected_local: String,
) -> Result(Nil, String)
Discriminator check for protocol-test error-shape dispatchers. Used
by the generated parse_<err>_response function: if the wire-side
discriminator (header or body) resolves to expected_local, the
response was routed to the right error shape and the dispatcher
reports Ok(Nil). The runner’s response-side assertion is binary
— Ok vs Error — so returning Nil is enough.
The runner hands fixture headers through with their literal-case
keys (X-Amzn-Errortype), but extract_error_type expects the
lowercased form that the real-request path produces via
headers_to_dict. We lowercase here so the helper matches HTTP’s
case-insensitive header semantics regardless of which call-site
invokes it.
pub fn default_config(
region: String,
endpoint_prefix: String,
signing_name: String,
) -> ClientConfig
Sensible default config given a region. Credentials default to
the standard chain (env → web-identity → SSO → profile → process →
ECS → IMDS); callers swap in a custom provider via
with_credentials_provider, matching the convention every other
AWS SDK follows.
pub fn default_endpoint(
endpoint_prefix: String,
region: String,
) -> String
pub fn error_type_matches(
error_type: String,
local: String,
) -> Bool
Match an AWS error_type wire value against a local Smithy
shape name. Used by the generated per-op translate_<op>_error
dispatchers. error_type already passes through
normalise_error_type (namespace + suffix stripped) at the
invoke layer, so a plain equality check suffices; we keep this
behind a helper to give the codegen one stable call-site.
pub fn extract_error_type(
headers: dict.Dict(String, String),
body: BitArray,
) -> String
Pull the wire-error-type local name out of a response. Looks at the
X-Amzn-Errortype header first (restJson1, awsJson*); falls back to
the body for __type / code / <Code> (covers JSON and XML
error shapes). The returned string is the local shape name —
namespace prefix, URI suffix, and Smithy [Charlie,foo,bar] suffix
are all stripped. Exposed for codegen-emitted error-shape protocol
test dispatchers; the in-process retry path uses it via the
ServiceError discriminator.
pub fn invoke(
config: ClientConfig,
built: #(String, String, dict.Dict(String, String), BitArray),
parse: fn(Int, dict.Dict(String, String), BitArray) -> Result(
output,
String,
),
) -> Result(output, ClientError)
Run one operation end-to-end. See module docs for the pipeline.
Operations that need to thread rule-set parameters known only to the
op itself (e.g. S3’s Bucket) should use invoke_with_endpoint_params
instead and pass those parameters through op_params.
pub fn invoke_streaming(
config: ClientConfig,
built: #(String, String, dict.Dict(String, String), BitArray),
) -> Result(streaming.Response, ClientError)
Streaming-response variant of invoke. Builds + signs the
request exactly like invoke, but dispatches through
streaming_http_send (chunked transport) and returns the raw
Response(StreamingBody) so callers can consume the body
incrementally — fold chunk-by-chunk via streaming.fold_chunks,
decode event-stream frames via event_stream.fold_events, or
stream-to-disk without buffering.
Used by generated codegen for operations whose output carries
@streaming — S3.GetObject for multi-GB downloads,
Transcribe.StartStreamTranscription and Kinesis.SubscribeToShard
for event-stream responses, etc.
Error responses (non-2xx) are materialised via
streaming.to_bit_array_max(body, 1 MiB) so error_type
extraction works on the JSON/XML error body the same way as
invoke. A response body that exceeds the 1 MiB cap on the
error path surfaces as DecodeError since we can’t safely
extract a typed error from an oversized error body.
Retry is intentionally NOT wrapped around streaming_http_send
at this layer — replaying a streaming request after a transient
failure is op-specific (idempotent vs. mutating). Callers that
want retry on a streaming op should layer it themselves or
drop down to the buffered invoke.
pub fn invoke_streaming_with_endpoint_params(
config: ClientConfig,
op_params: dict.Dict(String, endpoints.Value),
built: #(String, String, dict.Dict(String, String), BitArray),
) -> Result(streaming.Response, ClientError)
invoke_streaming with per-op endpoint-rule-set parameters (the
streaming-side counterpart to invoke_with_endpoint_params).
pub fn invoke_with_endpoint_params(
config: ClientConfig,
op_params: dict.Dict(String, endpoints.Value),
built: #(String, String, dict.Dict(String, String), BitArray),
parse: fn(Int, dict.Dict(String, String), BitArray) -> Result(
output,
String,
),
) -> Result(output, ClientError)
Same as invoke but with extra rule-set parameters merged in for this
operation only — used by generated S3 ops to supply Bucket/Key etc.
without leaking them onto the client config. If the client has no
endpoint_rule_set, op_params is ignored (the static endpoint_url
is used).
pub fn invoke_with_endpoint_params_and_host_prefix(
config: ClientConfig,
op_params: dict.Dict(String, endpoints.Value),
host_prefix: option.Option(String),
built: #(String, String, dict.Dict(String, String), BitArray),
parse: fn(Int, dict.Dict(String, String), BitArray) -> Result(
output,
String,
),
) -> Result(output, ClientError)
invoke_with_endpoint_params with an already-substituted host
prefix. Codegen passes Some(prefix) for ops carrying
@smithy.api#endpoint.hostPrefix — the template (e.g.
"{RequestRoute}.") is expanded against the input’s @hostLabel
members in the generated wrapper, mirroring the Rust SDK’s
read_before_execution interceptor.
pub fn translate_service_error(
err: ClientError,
decoders: List(#(String, fn(String) -> Result(t, Nil))),
on_transport: fn(String) -> t,
on_unknown: fn(String, Int, String) -> t,
) -> t
Generic translator from the runtime’s ClientError to a per-op
typed-error enum. Each generated translate_<op>_error is a
one-liner that supplies its operation’s decoder table plus
constructors for the always-present *Transport and *Unknown
variants. Saves ~15–25 LOC/op vs the previous open-coded nested
match — see Pass 3c in plan.md.
decoders is a list of (wire_error_type_local_name, decoder)
pairs. The first pair whose error_type matches gets to attempt the
decode; if its decoder returns Error(Nil), we fall back to
on_unknown with the textified body so the caller still sees
something useful instead of a panic.
pub fn with_credentials_provider(
config: ClientConfig,
provider: credentials.Provider,
) -> ClientConfig
Override the credentials provider — use for non-default profiles, in-process static creds, or any custom resolution chain.
pub fn with_endpoint_param(
config: ClientConfig,
name: String,
value: endpoints.Value,
) -> ClientConfig
Set a single client-level endpoint-rule-set parameter (e.g.
"UseFIPS" -> BoolVal(True)). Operation-specific params (S3
Bucket, Key) are threaded per-call via
invoke_with_endpoint_params.
pub fn with_endpoint_rule_set(
config: ClientConfig,
rule_set: endpoints.RuleSet,
) -> ClientConfig
Attach a Smithy endpoint rule set. When set, the runtime walks the rule
set per request to compute the endpoint URL — the value passed in via
with_endpoint_url (or default_endpoint) is then ignored except as a
fallback when the rule set is cleared. Use this from generated service
constructors that embed their service’s rule set.
pub fn with_endpoint_url(
config: ClientConfig,
url: String,
) -> ClientConfig
Override the request endpoint URL. Used for LocalStack, FIPS endpoints, custom DNS, and pre-signed-URL replay flows.
When a Smithy endpoint rule set is attached (the codegen wires
one on every generated service that declares one), the override
is threaded into the rule set as the standard Endpoint
parameter rather than replacing endpoint_url outright. AWS
rule sets honour this parameter via an early-branch rule like
case isSet(Endpoint) -> endpoint { url: "{Endpoint}" }, so the
override wins consistently across all services that declare an
Endpoint rule-set parameter.
The static endpoint_url is also updated so services without a
rule set continue to honour the override via the fallback path.
pub fn with_http2(config: ClientConfig) -> ClientConfig
Switch the streaming sender to the HTTP/2 variant. httpc adds
{http_version, "HTTP/2"} to its option list; servers that don’t
speak HTTP/2 negotiate down to HTTP/1.1 via ALPN, so calls keep
working even when the peer doesn’t support it. Buffered requests
(http_send) are unaffected — HTTP/2 is for high-throughput
streaming endpoints (S3 multipart, Bedrock streaming, Transcribe).
Pair with with_streaming_http_send for finer control (e.g. a
stubbed sender in tests that needs HTTP/2 wiring elsewhere).
pub fn with_http_send(
config: ClientConfig,
send: fn(request.Request(BitArray)) -> Result(
response.Response(BitArray),
http_send.HttpError,
),
) -> ClientConfig
pub fn with_max_attempts(
config: ClientConfig,
n: Int,
) -> ClientConfig
Tune just the retry attempt budget without replacing the whole
strategy. Equivalent to
with_retry_strategy(config, retry.with_max_attempts(config.retry_strategy, n)),
but reads as a single knob — the common case for production
tuning. Pass 1 to disable retries entirely (single attempt
per request), 5 for long-running batch workloads that tolerate
extra wait, etc.
pub fn with_retry_strategy(
config: ClientConfig,
strategy: retry.Strategy,
) -> ClientConfig
Override the retry strategy used to wrap http_send. Pass
retry.standard() for the AWS-standard 3-attempt backoff (the
default), or retry.adaptive(bucket) to add the token-bucket gate.
pub fn with_sigv4a_path_normalization(
config: ClientConfig,
normalize: Bool,
) -> ClientConfig
Override the SigV4a normalize_path knob. No-op when the client
hasn’t opted into SigV4a yet (with_sigv4a_region_set not
called). Pass False for S3 — its object-key paths can carry
. / .. that the RFC 3986 dot-segment removal would otherwise
strip.
pub fn with_sigv4a_region_set(
config: ClientConfig,
region_set: List(String),
) -> ClientConfig
Opt the Client into SigV4a (asymmetric ECDSA P-256) signing for
every request. region_set becomes the X-Amz-Region-Set header
— single-region callers pass ["us-east-1"], multi-region callers
pass the full list. Required for S3 Multi-Region Access Points.
normalize_path defaults to True; S3 callers should follow up
with with_sigv4a_path_normalization(client, False) so keys
containing . / .. survive the canonical-request step.
pub fn with_streaming_http_send(
config: ClientConfig,
send: fn(request.Request(BitArray)) -> Result(
response.Response(streaming.StreamingBody),
http_send.HttpError,
),
) -> ClientConfig
Override the streaming HTTP sender. Use for tests (stub the
transport), for forcing the buffered-then-streamed path via
http_send.lift_to_streaming(custom_buffered), or to inject a
future custom transport (proxy, gRPC tunnel, etc.).