Service + region (and optional bucket) decoded from a Host header.
Covers both the LocalStack hostname convention
(<service>.<region>.localhost.localstack.cloud[:port],
<bucket>.s3.<region>.localhost.localstack.cloud[:port]) and real AWS
service hostnames (<service>.<region>.amazonaws.com, S3 path-style
and virtual-hosted-style including the legacy no-region
s3.amazonaws.com / <bucket>.s3.amazonaws.com forms, the older
dash-separated s3-<region>.amazonaws.com form, the dualstack, FIPS and
static-website endpoints), under every AWS partition’s DNS suffix.
Detect a Smithy RPC v2 CBOR request: the smithy-protocol: rpc-v2-cbor
header plus a /service/{ServiceName}/operation/{Operation} path. The
service shape name is the same identifier awsJson services use as their
X-Amz-Target prefix, so it resolves through the same table.
Header-only service detection. Skips the form-encoded body sniff so
the dispatch path can decide whether to stream or buffer the body
without first reading it. Returns None when only a body sniff
would succeed; the caller must then fall back to detect_service
after buffering. Used to opt streaming routes (S3 PutObject /
UploadPart, ECR OCI v2 blob upload) out of the global body cap.
Whether detect_service_headers_only names the service
detect_service will route the request to, whatever its body: the
header detection rests on an X-Amz-Target, a query Action that names
a service, or a SigV4 Authorization scope, each of which detect_service
honors ahead of (or, for the scope, inside) its form-body sniff. A
detection resting on a presigned URL or the Host header alone is final
only without a form body (is_form_content_type): a form body naming an
Action routes the request to that action’s service instead.
Whether the request declares a form body
(application/x-www-form-urlencoded, parameters such as charset
allowed): the only body Query detection reads an Action from, before or
after the body is read.
Returns the REST protocol variant for a service, or None if not a REST service.
Whether service’s API is a REST protocol (restXml or restJson1), so a
REST request to its API host is one of its operations. ECR’s API is
awsJson: a REST request routed to ECR is its OCI registry.
Whether the request is addressed to an S3 Control endpoint
([<account-id>.]s3-control.<region>...). S3 Control is served by the
s3 handler, so the host is the only thing telling the two apart. The
s3-control label must lead the host (after the SDK’s account-ID
prefix), so a bucket whose name merely contains s3-control stays S3.
Normalize a Host header value for comparing against configured domain
names: drop the port (api.example.com:4566), any trailing root dot, and
lowercase it (DNS names are case-insensitive). A bracketed IPv6 literal
keeps its brackets.
Parse a Host header value for a LocalStack- or AWS-shaped hostname.
Returns None for anything that doesn’t match — callers fall through
to their existing detection path.
Whether the request is an S3 or API Gateway request on which a
query-string Action is only a parameter, never a Query call: its Host
is an S3 endpoint (path-style or a bucket’s virtual host) or an
execute-api host, its SigV4 scope (header or presign) is s3 or
execute-api, or it is an S3 SigV2 presign (AWSAccessKeyId,
Signature and Expires; a SigV2 Query signature carries
SignatureVersion and Timestamp instead). Dispatch adds the cases it
alone can tell: an unsigned plain-host path addressing an existing bucket,
and an API Gateway custom-domain host.
Decode one application/x-www-form-urlencoded component (a query-string
key or value, or a form-body field): + is a space and %XX escapes are
decoded on the raw bytes, then read as UTF-8 (lossily). For URI path
segments, where + is literal, use crate::path::percent_decode_segment.