Skip to content

Commit dd5b613

Browse files
authored
Proxy: Fix root route rewrites (#141)
* Use native RegExp parser * Adds fix for default index object
1 parent f932c98 commit dd5b613

File tree

7 files changed

+369
-19
lines changed

7 files changed

+369
-19
lines changed

main.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ resource "aws_cloudfront_cache_policy" "this" {
295295
locals {
296296
# CloudFront default root object
297297
################################
298-
cloudfront_default_root_object = "index"
298+
cloudfront_default_root_object = ""
299299

300300
# CloudFront Origins
301301
####################

packages/proxy/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@
1818
"dependencies": {
1919
"@vercel/routing-utils": "^1.9.1",
2020
"abort-controller": "^3.0.0",
21-
"node-fetch": "^2.6.1",
22-
"pcre-to-regexp": "^1.1.0"
21+
"node-fetch": "^2.6.1"
2322
},
2423
"devDependencies": {
2524
"@types/aws-lambda": "^8.10.76",

packages/proxy/src/__test__/handler.test.ts

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,4 +545,281 @@ describe('[proxy] Handler', () => {
545545
},
546546
TIMEOUT
547547
);
548+
549+
test(
550+
'i18n default locale rewrite',
551+
async () => {
552+
const proxyConfig: ProxyConfig = {
553+
lambdaRoutes: [],
554+
prerenders: {},
555+
staticRoutes: [],
556+
routes: [
557+
{
558+
src: '^/(?!(?:_next/.*|en|fr\\-FR|nl)(?:/.*|$))(.*)$',
559+
dest: '$wildcard/$1',
560+
continue: true,
561+
},
562+
{
563+
src: '/',
564+
locale: {
565+
redirect: {
566+
en: '/',
567+
'fr-FR': '/fr-FR',
568+
nl: '/nl',
569+
},
570+
cookie: 'NEXT_LOCALE',
571+
},
572+
continue: true,
573+
},
574+
{
575+
src: '^/$',
576+
dest: '/en',
577+
continue: true,
578+
},
579+
],
580+
};
581+
const requestPath = '/';
582+
583+
// Prepare configServer
584+
configServer.proxyConfig = proxyConfig;
585+
586+
// Origin Request
587+
// https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html#example-origin-request
588+
const event: CloudFrontRequestEvent = {
589+
Records: [
590+
{
591+
cf: {
592+
config: {
593+
distributionDomainName: 'd111111abcdef8.cloudfront.net',
594+
distributionId: 'EDFDVBD6EXAMPLE',
595+
eventType: 'origin-request',
596+
requestId:
597+
'4TyzHTaYWb1GX1qTfsHhEqV6HUDd_BzoBZnwfnvQc_1oF26ClkoUSEQ==',
598+
},
599+
request: {
600+
clientIp: '203.0.113.178',
601+
headers: {
602+
'x-forwarded-for': [
603+
{
604+
key: 'X-Forwarded-For',
605+
value: '203.0.113.178',
606+
},
607+
],
608+
'user-agent': [
609+
{
610+
key: 'User-Agent',
611+
value: 'Amazon CloudFront',
612+
},
613+
],
614+
via: [
615+
{
616+
key: 'Via',
617+
value:
618+
'2.0 2afae0d44e2540f472c0635ab62c232b.cloudfront.net (CloudFront)',
619+
},
620+
],
621+
host: [
622+
{
623+
key: 'Host',
624+
value: 'example.org',
625+
},
626+
],
627+
'cache-control': [
628+
{
629+
key: 'Cache-Control',
630+
value: 'no-cache, cf-no-cache',
631+
},
632+
],
633+
},
634+
method: 'GET',
635+
origin: {
636+
s3: {
637+
customHeaders: {
638+
'x-env-config-endpoint': [
639+
{
640+
key: 'x-env-config-endpoint',
641+
value: configEndpoint,
642+
},
643+
],
644+
'x-env-api-endpoint': [
645+
{
646+
key: 'x-env-api-endpoint',
647+
value: 'example.localhost',
648+
},
649+
],
650+
},
651+
region: 'us-east-1',
652+
authMethod: 'origin-access-identity',
653+
domainName: 's3.localhost',
654+
path: '',
655+
},
656+
},
657+
querystring: '',
658+
uri: requestPath,
659+
},
660+
},
661+
},
662+
],
663+
};
664+
665+
const result = (await handler(event)) as CloudFrontRequest;
666+
667+
expect(result.origin?.s3).toEqual(
668+
expect.objectContaining({
669+
domainName: 's3.localhost',
670+
path: '',
671+
})
672+
);
673+
expect(result.uri).toBe('/en');
674+
},
675+
TIMEOUT
676+
);
677+
678+
test(
679+
'Correctly request /index object from S3 when requesting /',
680+
async () => {
681+
const proxyConfig: ProxyConfig = {
682+
staticRoutes: ['/404', '/500', '/index'],
683+
lambdaRoutes: [],
684+
routes: [
685+
{
686+
src: '^(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))\\/$',
687+
headers: {
688+
Location: '/$1',
689+
},
690+
status: 308,
691+
continue: true,
692+
},
693+
{
694+
src: '/404',
695+
status: 404,
696+
continue: true,
697+
},
698+
{
699+
handle: 'filesystem',
700+
},
701+
{
702+
handle: 'resource',
703+
},
704+
{
705+
src: '/.*',
706+
status: 404,
707+
},
708+
{
709+
handle: 'miss',
710+
},
711+
{
712+
handle: 'rewrite',
713+
},
714+
{
715+
handle: 'hit',
716+
},
717+
{
718+
handle: 'error',
719+
},
720+
{
721+
src: '/.*',
722+
dest: '/404',
723+
status: 404,
724+
},
725+
],
726+
prerenders: {},
727+
};
728+
729+
const requestPath = '/';
730+
731+
// Prepare configServer
732+
configServer.proxyConfig = proxyConfig;
733+
734+
// Origin Request
735+
// https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html#example-origin-request
736+
const event: CloudFrontRequestEvent = {
737+
Records: [
738+
{
739+
cf: {
740+
config: {
741+
distributionDomainName: 'd111111abcdef8.cloudfront.net',
742+
distributionId: 'EDFDVBD6EXAMPLE',
743+
eventType: 'origin-request',
744+
requestId:
745+
'4TyzHTaYWb1GX1qTfsHhEqV6HUDd_BzoBZnwfnvQc_1oF26ClkoUSEQ==',
746+
},
747+
request: {
748+
clientIp: '203.0.113.178',
749+
headers: {
750+
'x-forwarded-for': [
751+
{
752+
key: 'X-Forwarded-For',
753+
value: '203.0.113.178',
754+
},
755+
],
756+
'user-agent': [
757+
{
758+
key: 'User-Agent',
759+
value: 'Amazon CloudFront',
760+
},
761+
],
762+
via: [
763+
{
764+
key: 'Via',
765+
value:
766+
'2.0 2afae0d44e2540f472c0635ab62c232b.cloudfront.net (CloudFront)',
767+
},
768+
],
769+
host: [
770+
{
771+
key: 'Host',
772+
value: 'example.org',
773+
},
774+
],
775+
'cache-control': [
776+
{
777+
key: 'Cache-Control',
778+
value: 'no-cache, cf-no-cache',
779+
},
780+
],
781+
},
782+
method: 'GET',
783+
origin: {
784+
s3: {
785+
customHeaders: {
786+
'x-env-config-endpoint': [
787+
{
788+
key: 'x-env-config-endpoint',
789+
value: configEndpoint,
790+
},
791+
],
792+
'x-env-api-endpoint': [
793+
{
794+
key: 'x-env-api-endpoint',
795+
value: 'example.localhost',
796+
},
797+
],
798+
},
799+
region: 'us-east-1',
800+
authMethod: 'origin-access-identity',
801+
domainName: 's3.localhost',
802+
path: '',
803+
},
804+
},
805+
querystring: '',
806+
uri: requestPath,
807+
},
808+
},
809+
},
810+
],
811+
};
812+
813+
const result = (await handler(event)) as CloudFrontRequest;
814+
815+
expect(result.origin?.s3).toEqual(
816+
expect.objectContaining({
817+
domainName: 's3.localhost',
818+
path: '',
819+
})
820+
);
821+
expect(result.uri).toBe('/index');
822+
},
823+
TIMEOUT
824+
);
548825
});

packages/proxy/src/__test__/proxy.unit.test.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,3 +331,74 @@ test('[proxy-unit] External rewrite', () => {
331331
target: 'url',
332332
});
333333
});
334+
335+
test('[proxy-unit] Rewrite with ^ and $', () => {
336+
const routesConfig = [
337+
{
338+
src: '^/$',
339+
dest: '/en',
340+
continue: true,
341+
},
342+
] as Route[];
343+
344+
const result = new Proxy(routesConfig, [], []).route('/');
345+
346+
expect(result).toEqual({
347+
found: true,
348+
dest: '/en',
349+
continue: true,
350+
status: undefined,
351+
headers: {},
352+
uri_args: new URLSearchParams(),
353+
matched_route: routesConfig[0],
354+
matched_route_idx: 0,
355+
userDest: true,
356+
isDestUrl: false,
357+
phase: undefined,
358+
target: undefined,
359+
});
360+
});
361+
362+
test('[proxy-unit] i18n default locale', () => {
363+
const routesConfig = [
364+
{
365+
src: '^/(?!(?:_next/.*|en|fr\\-FR|nl)(?:/.*|$))(.*)$',
366+
dest: '$wildcard/$1',
367+
continue: true,
368+
},
369+
{
370+
src: '/',
371+
locale: {
372+
redirect: {
373+
en: '/',
374+
'fr-FR': '/fr-FR',
375+
nl: '/nl',
376+
},
377+
cookie: 'NEXT_LOCALE',
378+
},
379+
continue: true,
380+
},
381+
{
382+
src: '^/$',
383+
dest: '/en',
384+
continue: true,
385+
},
386+
] as Route[];
387+
388+
const result = new Proxy(routesConfig, [], []).route('/');
389+
390+
expect(result).toEqual({
391+
found: true,
392+
dest: '/en',
393+
continue: true,
394+
status: undefined,
395+
headers: {},
396+
uri_args: new URLSearchParams(''),
397+
matched_route: routesConfig[2],
398+
matched_route_idx: 2,
399+
userDest: true,
400+
isDestUrl: false,
401+
phase: undefined,
402+
target: undefined,
403+
});
404+
});

0 commit comments

Comments
 (0)