Skip to content

Commit 6076ed9

Browse files
committed
Improve handling of s3 origins
* Handle s3 website urls properly * Allow multiple origins from same s3 bucket * Allow settings of original protocol polic
1 parent 6f15e07 commit 6076ed9

6 files changed

+251
-13
lines changed

__tests__/__snapshots__/cache-behavior-options.test.js.snap

+5-5
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ Object {
5757
"MinTTL": 0,
5858
"PathPattern": "/sample/path",
5959
"SmoothStreaming": false,
60-
"TargetOriginId": "mycustomorigin.com",
60+
"TargetOriginId": "mycustomorigin.com/path",
6161
"TrustedSigners": Object {
6262
"Enabled": false,
6363
"Quantity": 0,
@@ -108,7 +108,7 @@ Object {
108108
"MaxTTL": 31536000,
109109
"MinTTL": 0,
110110
"SmoothStreaming": false,
111-
"TargetOriginId": "mycustomorigin.com",
111+
"TargetOriginId": "mycustomorigin.com/path",
112112
"TrustedSigners": Object {
113113
"Enabled": false,
114114
"Items": Array [],
@@ -129,7 +129,7 @@ Object {
129129
"HTTPPort": 80,
130130
"HTTPSPort": 443,
131131
"OriginKeepaliveTimeout": 5,
132-
"OriginProtocolPolicy": "https-only",
132+
"OriginProtocolPolicy": "http-only",
133133
"OriginReadTimeout": 30,
134134
"OriginSslProtocols": Object {
135135
"Items": Array [
@@ -139,8 +139,8 @@ Object {
139139
},
140140
},
141141
"DomainName": "mycustomorigin.com",
142-
"Id": "mycustomorigin.com",
143-
"OriginPath": "",
142+
"Id": "mycustomorigin.com/path",
143+
"OriginPath": "/path",
144144
},
145145
],
146146
"Quantity": 1,

__tests__/__snapshots__/s3-origin.test.js.snap

+178
Original file line numberDiff line numberDiff line change
@@ -330,3 +330,181 @@ Object {
330330
"IfMatch": "etag",
331331
}
332332
`;
333+
334+
exports[`S3 origins When origin is an S3 bucket URL with path creates distribution 1`] = `
335+
Object {
336+
"DistributionConfig": Object {
337+
"Aliases": Object {
338+
"Items": Array [],
339+
"Quantity": 0,
340+
},
341+
"CacheBehaviors": Object {
342+
"Items": Array [],
343+
"Quantity": 0,
344+
},
345+
"CallerReference": "1566599541192",
346+
"Comment": "",
347+
"DefaultCacheBehavior": Object {
348+
"AllowedMethods": Object {
349+
"CachedMethods": Object {
350+
"Items": Array [
351+
"HEAD",
352+
"GET",
353+
],
354+
"Quantity": 2,
355+
},
356+
"Items": Array [
357+
"HEAD",
358+
"GET",
359+
],
360+
"Quantity": 2,
361+
},
362+
"Compress": false,
363+
"DefaultTTL": 86400,
364+
"FieldLevelEncryptionId": "",
365+
"ForwardedValues": Object {
366+
"Cookies": Object {
367+
"Forward": "none",
368+
},
369+
"Headers": Object {
370+
"Items": Array [],
371+
"Quantity": 0,
372+
},
373+
"QueryString": false,
374+
"QueryStringCacheKeys": Object {
375+
"Items": Array [],
376+
"Quantity": 0,
377+
},
378+
},
379+
"LambdaFunctionAssociations": Object {
380+
"Items": Array [],
381+
"Quantity": 0,
382+
},
383+
"MaxTTL": 31536000,
384+
"MinTTL": 0,
385+
"SmoothStreaming": false,
386+
"TargetOriginId": "mybucket/static",
387+
"TrustedSigners": Object {
388+
"Enabled": false,
389+
"Items": Array [],
390+
"Quantity": 0,
391+
},
392+
"ViewerProtocolPolicy": "redirect-to-https",
393+
},
394+
"Enabled": true,
395+
"HttpVersion": "http2",
396+
"Origins": Object {
397+
"Items": Array [
398+
Object {
399+
"CustomHeaders": Object {
400+
"Items": Array [],
401+
"Quantity": 0,
402+
},
403+
"DomainName": "mybucket.s3.amazonaws.com",
404+
"Id": "mybucket/static",
405+
"OriginPath": "/static",
406+
"S3OriginConfig": Object {
407+
"OriginAccessIdentity": "",
408+
},
409+
},
410+
],
411+
"Quantity": 1,
412+
},
413+
"PriceClass": "PriceClass_All",
414+
},
415+
}
416+
`;
417+
418+
exports[`S3 origins When origin is an S3 website URL creates custom origin not s3 origin distribution 1`] = `
419+
Object {
420+
"DistributionConfig": Object {
421+
"Aliases": Object {
422+
"Items": Array [],
423+
"Quantity": 0,
424+
},
425+
"CacheBehaviors": Object {
426+
"Items": Array [],
427+
"Quantity": 0,
428+
},
429+
"CallerReference": "1566599541192",
430+
"Comment": "",
431+
"DefaultCacheBehavior": Object {
432+
"AllowedMethods": Object {
433+
"CachedMethods": Object {
434+
"Items": Array [
435+
"HEAD",
436+
"GET",
437+
],
438+
"Quantity": 2,
439+
},
440+
"Items": Array [
441+
"HEAD",
442+
"GET",
443+
],
444+
"Quantity": 2,
445+
},
446+
"Compress": false,
447+
"DefaultTTL": 86400,
448+
"FieldLevelEncryptionId": "",
449+
"ForwardedValues": Object {
450+
"Cookies": Object {
451+
"Forward": "none",
452+
},
453+
"Headers": Object {
454+
"Items": Array [],
455+
"Quantity": 0,
456+
},
457+
"QueryString": false,
458+
"QueryStringCacheKeys": Object {
459+
"Items": Array [],
460+
"Quantity": 0,
461+
},
462+
},
463+
"LambdaFunctionAssociations": Object {
464+
"Items": Array [],
465+
"Quantity": 0,
466+
},
467+
"MaxTTL": 31536000,
468+
"MinTTL": 0,
469+
"SmoothStreaming": false,
470+
"TargetOriginId": "mybucket.s3-website.amazonaws.com",
471+
"TrustedSigners": Object {
472+
"Enabled": false,
473+
"Items": Array [],
474+
"Quantity": 0,
475+
},
476+
"ViewerProtocolPolicy": "redirect-to-https",
477+
},
478+
"Enabled": true,
479+
"HttpVersion": "http2",
480+
"Origins": Object {
481+
"Items": Array [
482+
Object {
483+
"CustomHeaders": Object {
484+
"Items": Array [],
485+
"Quantity": 0,
486+
},
487+
"CustomOriginConfig": Object {
488+
"HTTPPort": 80,
489+
"HTTPSPort": 443,
490+
"OriginKeepaliveTimeout": 5,
491+
"OriginProtocolPolicy": "https-only",
492+
"OriginReadTimeout": 30,
493+
"OriginSslProtocols": Object {
494+
"Items": Array [
495+
"TLSv1.2",
496+
],
497+
"Quantity": 1,
498+
},
499+
},
500+
"DomainName": "mybucket.s3-website.amazonaws.com",
501+
"Id": "mybucket.s3-website.amazonaws.com",
502+
"OriginPath": "",
503+
},
504+
],
505+
"Quantity": 1,
506+
},
507+
"PriceClass": "PriceClass_All",
508+
},
509+
}
510+
`;

__tests__/cache-behavior-options.test.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ describe('Input origin as a custom url', () => {
4444
},
4545
origins: [
4646
{
47-
url: 'https://mycustomorigin.com',
47+
url: 'https://mycustomorigin.com/path',
48+
protocolPolicy: 'http-only',
4849
pathPatterns: {
4950
'/sample/path': {
5051
ttl: 0,

__tests__/s3-origin.test.js

+54
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,60 @@ describe('S3 origins', () => {
7777
})
7878
})
7979

80+
describe('When origin is an S3 bucket URL with path', () => {
81+
it('creates distribution', async () => {
82+
await component.default({
83+
origins: ['https://mybucket.s3.amazonaws.com/static']
84+
})
85+
86+
assertHasOrigin(mockCreateDistribution, {
87+
Id: 'mybucket/static',
88+
DomainName: 'mybucket.s3.amazonaws.com',
89+
S3OriginConfig: {
90+
OriginAccessIdentity: ''
91+
},
92+
CustomHeaders: {
93+
Quantity: 0,
94+
Items: []
95+
},
96+
OriginPath: '/static'
97+
})
98+
99+
expect(mockCreateDistribution.mock.calls[0][0]).toMatchSnapshot()
100+
})
101+
})
102+
103+
describe('When origin is an S3 website URL', () => {
104+
it('creates custom origin not s3 origin distribution', async () => {
105+
await component.default({
106+
origins: ['https://mybucket.s3-website.amazonaws.com']
107+
})
108+
109+
assertHasOrigin(mockCreateDistribution, {
110+
Id: 'mybucket.s3-website.amazonaws.com',
111+
DomainName: 'mybucket.s3-website.amazonaws.com',
112+
CustomHeaders: {
113+
Quantity: 0,
114+
Items: []
115+
},
116+
CustomOriginConfig: {
117+
HTTPPort: 80,
118+
HTTPSPort: 443,
119+
OriginProtocolPolicy: 'https-only',
120+
OriginSslProtocols: {
121+
Quantity: 1,
122+
Items: ['TLSv1.2']
123+
},
124+
OriginReadTimeout: 30,
125+
OriginKeepaliveTimeout: 5
126+
},
127+
OriginPath: ''
128+
})
129+
130+
expect(mockCreateDistribution.mock.calls[0][0]).toMatchSnapshot()
131+
})
132+
})
133+
80134
describe('When origin is an S3 URL only accessible via CloudFront', () => {
81135
it('creates distribution', async () => {
82136
mockCreateCloudFrontOriginAccessIdentityPromise.mockResolvedValueOnce({

lib/getOriginConfig.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ module.exports = (origin, { originAccessIdentityId = '' }) => {
66
const { hostname, pathname } = url.parse(originUrl)
77

88
const originConfig = {
9-
Id: hostname,
9+
Id: `${hostname}${pathname}`.replace(/\/$/, ''),
1010
DomainName: hostname,
1111
CustomHeaders: {
1212
Quantity: 0,
@@ -15,9 +15,10 @@ module.exports = (origin, { originAccessIdentityId = '' }) => {
1515
OriginPath: pathname === '/' ? '' : pathname
1616
}
1717

18-
if (originUrl.includes('s3')) {
18+
if (originUrl.includes('s3') && !originUrl.includes('s3-website')) {
19+
// attach s3 origin for buckets, but don't do this for buckets configured as website
1920
const bucketName = hostname.split('.')[0]
20-
originConfig.Id = bucketName
21+
originConfig.Id = pathname === '/' ? bucketName : `${bucketName}${pathname}`
2122
originConfig.DomainName = `${bucketName}.s3.amazonaws.com`
2223
originConfig.S3OriginConfig = {
2324
OriginAccessIdentity: originAccessIdentityId
@@ -28,7 +29,7 @@ module.exports = (origin, { originAccessIdentityId = '' }) => {
2829
originConfig.CustomOriginConfig = {
2930
HTTPPort: 80,
3031
HTTPSPort: 443,
31-
OriginProtocolPolicy: 'https-only',
32+
OriginProtocolPolicy: origin.protocolPolicy || 'https-only',
3233
OriginSslProtocols: {
3334
Quantity: 1,
3435
Items: ['TLSv1.2']

lib/index.js

+7-3
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,15 @@ const servePrivateContentEnabled = (inputs) =>
88
return origin && origin.private === true
99
})
1010

11+
const unique = (value, index, self) => self.indexOf(value) === index
1112
const updateBucketsPolicies = async (s3, origins, s3CanonicalUserId) => {
1213
// update bucket policies with cloudfront access
13-
const bucketNames = origins.Items.filter((origin) => origin.S3OriginConfig).map(
14-
(origin) => origin.Id
15-
)
14+
const bucketNames = origins.Items.filter((origin) => origin.S3OriginConfig)
15+
.map((origin) => {
16+
// remove path from bucketname if origin had pathname
17+
return origin.Id.split('/')[0]
18+
})
19+
.filter(unique)
1620

1721
return Promise.all(
1822
bucketNames.map((bucketName) => grantCloudFrontBucketAccess(s3, bucketName, s3CanonicalUserId))

0 commit comments

Comments
 (0)