livery_s3_uri (livery_s3 v0.1.0)

View Source

URL building and RFC 3986 encoding for S3 requests.

S3 SigV4 is unforgiving about encoding: the canonical request must use the same percent-encoding and query ordering as the bytes actually put on the wire. This module is the single source of truth for both. Operations build the absolute request URL here; the signer (livery_s3_sigv4) reads the path and query back out of that URL with url_parts/1, so the signed and sent forms cannot drift.

Two encoders are exposed: encode/1 (encode every reserved byte, used for buckets and query components) and encode_path/1 (the same but keeping / so object keys with slashes map to literal path separators).

Summary

Functions

Build the canonical query string from key/value pairs: each component is percent-encoded, then the pairs are sorted by encoded key (ties by value) and joined with &. This is both what we sign and what we put in the URL.

Percent-encode every byte outside the RFC 3986 unreserved set.

Like encode/1 but keep / (for object keys used as path segments).

Parse an endpoint URL into scheme/host/port. Accepts https://host, http://host:port, bare host[:port] (defaults to https), and bracketed IPv6 literals. A trailing path is ignored.

Build the absolute request URL and the matching host header value.

Split an absolute URL into authority, path, and (already canonical) query. The path and query are returned verbatim so the signer reuses the exact bytes that were placed in the URL.

Types

config()

-type config() ::
          #s3_config{scheme :: binary(),
                     host :: binary(),
                     port :: undefined | inet:port_number(),
                     region :: binary(),
                     credentials :: livery_s3_credentials:handle(),
                     addressing :: path | virtual}.

query_params()

-type query_params() :: [{binary(), binary()}].

Functions

canonical_query/1

-spec canonical_query(query_params()) -> binary().

Build the canonical query string from key/value pairs: each component is percent-encoded, then the pairs are sorted by encoded key (ties by value) and joined with &. This is both what we sign and what we put in the URL.

encode(Bin)

-spec encode(binary()) -> binary().

Percent-encode every byte outside the RFC 3986 unreserved set.

encode_path(Bin)

-spec encode_path(binary()) -> binary().

Like encode/1 but keep / (for object keys used as path segments).

parse_endpoint(Endpoint)

-spec parse_endpoint(binary()) ->
                        #{scheme := binary(), host := binary(), port := undefined | inet:port_number()}.

Parse an endpoint URL into scheme/host/port. Accepts https://host, http://host:port, bare host[:port] (defaults to https), and bracketed IPv6 literals. A trailing path is ignored.

request_target(Cfg, Bucket, Key, Query)

-spec request_target(config(), undefined | binary(), undefined | binary(), query_params()) ->
                        {binary(), binary()}.

Build the absolute request URL and the matching host header value.

Bucket and Key may be undefined (service- and bucket-level requests). The returned authority is the exact host[:port] we will sign, computed from the addressing style: path-style keeps the endpoint host and puts the bucket in the path; virtual-hosted prefixes the bucket onto the host.

url_parts(Url)

-spec url_parts(binary()) -> #{authority := binary(), path := binary(), query := binary()}.

Split an absolute URL into authority, path, and (already canonical) query. The path and query are returned verbatim so the signer reuses the exact bytes that were placed in the URL.