s3
stableUtility functions for building AWS S3 request components: endpoints, signed URLs, presigned URLs, XML parsing, and payload hashing.
use plugin s3::{build_endpoint, build_object_url, content_type, …} Functions (10)
- build_endpoint Build an S3 bucket endpoint URL
- build_object_url Build a full URL for an S3 object
- content_type Map a file extension to a MIME type
- sign_v4_header Compute an AWS Signature V4 Authorization header
- parse_object_key Split an S3 key into directory, name, and extension
- build_presigned_url Generate a presigned GET URL with expiry
- build_copy_source Build the x-amz-copy-source header value
- parse_list_xml Parse a ListBucketResult XML response
- build_delete_xml Build XML body for multi-object delete
- sha256 Compute the SHA-256 hex digest of a string
Overview
s3 is a dependency-free toolkit of pure helper functions for assembling the
pieces of an AWS S3 (or S3-compatible) HTTP request by hand — endpoint and
object URLs, AWS Signature Version 4 Authorization headers, presigned GET
URLs, request/response XML bodies, MIME types, and SHA-256 payload hashes. It is
stateless and holds no client or connection: every function takes plain strings,
numbers, and tables and returns a plain string or table, so you stay in full
control of which HTTP library actually sends the request. Reach for it when you
need to talk to S3 from a thin runtime, sign requests yourself, or generate
shareable links without pulling in a heavyweight SDK.
The mental model is to compose: build a URL or canonical request from the bucket,
key, and region, hash the payload with sha256, feed everything into
sign_v4_header to authorize it, then parse the XML that S3 sends back with
parse_list_xml.
Common patterns
Sign a PUT upload: hash the payload, then build the Authorization header.
use plugin s3::{build_object_url, content_type, sha256, sign_v4_header}
let bucket = "my-bucket"
let region = "us-east-1"
let key = "uploads/report.pdf"
let body = "...file bytes..."
let url = build_object_url(bucket, key, region)
let ct = content_type("pdf")
let payload_hash = sha256(body)
let auth = sign_v4_header(
"PUT",
"/{key}",
"",
#{
"host": "{bucket}.s3.{region}.amazonaws.com",
"x-amz-content-sha256": payload_hash,
"x-amz-date": "20240101T120000Z",
"content-type": ct
},
payload_hash,
"AKIAIOSFODNN7EXAMPLE",
"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
region,
"s3",
"20240101T120000Z"
)
print("PUT {url}")
print("Authorization: {auth}")
Hand out a temporary download link without exposing credentials.
use plugin s3::{build_presigned_url}
let link = build_presigned_url(
"my-bucket",
"private/invoice.pdf",
"us-east-1",
"AKIAIOSFODNN7EXAMPLE",
"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
"20240101T120000Z",
3600
)
print("share this for one hour: {link}")
List a bucket, then group the returned objects by file type.
use plugin s3::{parse_list_xml, parse_object_key}
let xml = "<ListBucketResult><Contents><Key>img/logo.png</Key><Size>2048</Size><LastModified>2024-01-01</LastModified></Contents></ListBucketResult>"
let objects = parse_list_xml(xml)
for obj in objects {
let parts = parse_object_key(obj["key"])
print("{parts["name"]} ({parts["ext"]}) — {obj["size"]} bytes")
}
Build an S3 bucket endpoint URL
Returns the standard AWS S3 endpoint URL for a bucket in the given region.
use plugin s3::{build_endpoint}
let url = build_endpoint("my-bucket", "us-east-1")
print(url) // https://my-bucket.s3.us-east-1.amazonaws.com
Build a full URL for an S3 object
Returns the full HTTPS URL for a specific object in an S3 bucket. Leading slashes in the key are stripped automatically.
use plugin s3::{build_object_url}
let url = build_object_url("my-bucket", "images/photo.jpg", "eu-west-1")
print(url) // https://my-bucket.s3.eu-west-1.amazonaws.com/images/photo.jpg
Map a file extension to a MIME type
Returns the MIME content type for a given file extension. Supports common web, image, audio, video, font, and archive types. Falls back to application/octet-stream for unknown extensions.
use plugin s3::{content_type}
print(content_type("png")) // image/png
print(content_type(".json")) // application/json
print(content_type("wasm")) // application/wasm
print(content_type("xyz")) // application/octet-stream
Derive the type from a key by combining it with parse_object_key, so an
upload sets the right Content-Type header automatically:
use plugin s3::{parse_object_key, content_type}
let parts = parse_object_key("assets/styles/main.css")
print(content_type(parts["ext"])) // text/css
Compute an AWS Signature V4 Authorization header
Computes a complete AWS Signature Version 4 Authorization header value. The datetime_str must be in ISO 8601 basic format (e.g. "20240101T120000Z"). The headers table must contain lowercase header names.
use plugin s3::{sign_v4_header, sha256}
let payload_hash = sha256("")
let auth = sign_v4_header(
"PUT",
"/my-bucket/file.txt",
"",
#{"host": "my-bucket.s3.us-east-1.amazonaws.com", "x-amz-date": "20240101T120000Z"},
payload_hash,
"AKIAIOSFODNN7EXAMPLE",
"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
"us-east-1",
"s3",
"20240101T120000Z"
)
print(auth)
Split an S3 key into directory, name, and extension
Splits an S3 object key into its dir, name, and ext parts. Useful for routing objects by type or grouping by prefix.
use plugin s3::{parse_object_key}
let parts = parse_object_key("uploads/2024/photo.jpg")
print(parts["dir"]) // uploads/2024/
print(parts["name"]) // photo.jpg
print(parts["ext"]) // jpg
Keys with no directory or no extension yield empty strings for those fields:
use plugin s3::{parse_object_key}
let parts = parse_object_key("README")
print("dir=[{parts["dir"]}] name=[{parts["name"]}] ext=[{parts["ext"]}]")
// dir=[] name=[README] ext=[]
Generate a presigned GET URL with expiry
Generates a presigned GET URL that grants temporary access to a private S3 object without requiring AWS credentials on the client. The URL expires after expires_seconds seconds.
use plugin s3::{build_presigned_url}
let url = build_presigned_url(
"my-bucket",
"reports/q4.pdf",
"us-east-1",
"AKIAIOSFODNN7EXAMPLE",
"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
"20240101T120000Z",
3600
)
print(url)
Build the x-amz-copy-source header value
Returns the value for the x-amz-copy-source header used in S3 CopyObject requests.
use plugin s3::{build_copy_source}
let source = build_copy_source("source-bucket", "original/file.txt")
print(source) // /source-bucket/original/file.txt
Pair it with build_object_url to describe a full copy operation:
use plugin s3::{build_copy_source, build_object_url}
let source = build_copy_source("backups", "db/2024.sql")
let dest = build_object_url("archive", "db/2024.sql", "us-east-1")
print("copy {source} -> {dest}")
Parse a ListBucketResult XML response
Parses an S3 ListBucketResult XML response and returns a table of entries. Each entry has key, size, and last_modified fields.
use plugin s3::{parse_list_xml}
let xml = "<ListBucketResult><Contents><Key>img.png</Key><Size>1024</Size><LastModified>2024-01-01</LastModified></Contents></ListBucketResult>"
let objects = parse_list_xml(xml)
print(objects[1]["key"]) // img.png
print(objects[1]["size"]) // 1024
The result is a 1-indexed table you can iterate to total up the bucket size:
use plugin s3::{parse_list_xml}
let xml = "<ListBucketResult><Contents><Key>a.txt</Key><Size>10</Size><LastModified>2024-01-01</LastModified></Contents><Contents><Key>b.txt</Key><Size>20</Size><LastModified>2024-01-02</LastModified></Contents></ListBucketResult>"
let objects = parse_list_xml(xml)
for obj in objects {
print("{obj["key"]} last modified {obj["last_modified"]}")
}
Build XML body for multi-object delete
Builds the XML request body for an S3 multi-object delete operation. The keys argument is a table of string keys. Special XML characters in keys are escaped automatically.
use plugin s3::{build_delete_xml}
let xml = build_delete_xml(["old/file1.txt", "old/file2.txt"])
print(xml)
// <?xml ...><Delete><Object><Key>old/file1.txt</Key></Object>...</Delete>
Compute the SHA-256 hex digest of a string
Returns the lowercase hex-encoded SHA-256 digest of the given string. Use this to compute the x-amz-content-sha256 payload hash required for V4 signing.
use plugin s3::{sha256}
let hash = sha256("hello world")
print(hash) // b94d27b9934d3e08a52e52d7da7dabfac484efe04294e576b9c5cdf9c37c5e67 (truncated)
let empty_hash = sha256("")
print(empty_hash) // e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855