fix(cloudfront): serve DefaultRootObject at the distribution root - #2560
Conversation
`DefaultRootObject` was parsed onto the model and never read, so a viewer request for the distribution root reached the origin as `/`. Against an S3 origin that returns the bucket's `ListBucketResult` XML instead of the SPA shell. Resolve it alongside the cache behavior and fetch it in place of the root path, preserving the query string. AWS applies the default root object to the distribution root ONLY, so a subdirectory request is never rewritten to `<dir>/<object>`; a blank config value is ignored and a stray leading slash is normalized rather than proxied as `//index.html`.
- Append DefaultRootObject after / exactly as configured, as AWS does (a leading slash yields //index.html rather than being normalized), and treat only an empty value as unset; percent-encode it for the origin URL so #, ? and spaces address the key rather than a fragment or query. - Prefix every origin request with the origin's OriginPath, for custom, S3 REST and S3-website origins alike, including the default root object and custom error pages. - Unit tests for root-object resolution, encoding and OriginPath; e2e for query-string roots, UpdateDistribution, and an OriginPath origin.
…Path tests After merging main (which now routes S3 REST origins to this process), the OriginPath unit tests' expected upstream base is the local endpoint.
|
Thanks a lot for this, @Sorttech. Wiring DefaultRootObject into the data plane is what makes a CDK SPA distribution serve its shell at /, and the root-only scoping was right. I pushed a few follow-ups before merging: the configured value is now appended after / exactly as given, matching AWS (a leading slash yields //index.html rather than being normalized, and only an empty value means unset), and it is percent-encoded for the origin URL so characters like # and ? address the key instead of becoming a fragment or query. I also implemented OriginPath, which was stored but never applied: every origin request (custom, S3 REST and S3 website, including the default root object and custom error pages) is now prefixed with the origin's OriginPath. Added unit tests plus e2e coverage for query-string roots, UpdateDistribution, and an OriginPath origin. Merged. Really appreciate the contribution. |
DefaultRootObjectwas parsed onto the model and never read, so a viewerrequest for the distribution root reached the origin as
/. Against an S3origin that returns the bucket's
ListBucketResultXML instead of the SPAshell.
Resolve it alongside the cache behavior and fetch it in place of the root
path, preserving the query string. AWS applies the default root object to
the distribution root ONLY, so a subdirectory request is never rewritten to
<dir>/<object>; a blank config value is ignored and a stray leading slashis normalized rather than proxied as
//index.html.Test plan
Regression test:
serves_default_root_object_at_the_distribution_rootincrates/fakecloud-e2e/tests/cloudfront_dataplane.rsVerification
On this exact head, rebased onto current
main:cargo fmt --all --check- clean.cargo clippy --workspace --all-targets -- -D warnings- clean.cargo test --workspaceexcludingfakecloud-e2e,fakecloud-conformance,fakecloud-tfaccandfakecloud-parity(the same set CI'stestjob runs),plus
cargo test -p fakecloud-conformance --lib- 9838 passed, 0 failed(
--no-fail-fast,--test-threads=4). An earlier run of the same commandfailed once in
fakecloud-ssm(
service::tests::rearm_in_flight_commands_settles_restored_pending) andpassed on rerun. That test reads the last durable snapshot right after the
in-memory status flips to
Successwhile the save is asynchronous; it fails4 of 11 runs on untouched
mainon this machine. This branch does not touchSSM.
cargo test -p fakecloud-e2e --test cloudfront_dataplane- the regressiontest above passes against a freshly built
target/debug/fakecloud.Found by deploying a CDK CloudFront + S3 SPA against fakecloud.
Summary by cubic
Fixes CloudFront
DefaultRootObjectandOriginPathhandling so viewer requests are served the configured objects instead of the bucket root. Previously both were stored on the model but never read: S3-backed SPAs at the root returned the bucket'sListBucketResultXML, and prefixed origins fetched from the root instead of the configured prefix.//index.html) and percent-encoded so#and?address the key rather than a fragment or query.OriginPathprefixes every origin request — viewer paths, the default root object, and custom error pages — for custom, S3 REST, and S3-website origins.main.Written for commit 8d123ee. Summary will update on new commits.