Skip to content

Commit 5ac005b

Browse files
committed
proxy: Adds language detection
1 parent 378b943 commit 5ac005b

File tree

7 files changed

+243
-69
lines changed

7 files changed

+243
-69
lines changed

main.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ resource "aws_cloudfront_cache_policy" "this" {
278278
dynamic "headers" {
279279
for_each = length(var.cloudfront_cache_key_headers) == 0 ? [] : [true]
280280
content {
281-
items = var.cloudfront_cache_key_headers
281+
items = sort(var.cloudfront_cache_key_headers)
282282
}
283283
}
284284
}

packages/proxy/src/__test__/detect-locale.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,11 @@ describe('detectLocale', () => {
4848
},
4949
};
5050
const preferredLocaleFromHeader = [
51-
['fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5', 'fr-FR'],
52-
['*', 'en'],
53-
['de', ''],
54-
['en-US, en', 'en'],
55-
['af;q=0.9, ar;q=0.8, de;q=0.7, *;q=0.5', 'en'],
51+
['fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5', 'fr-ch'],
52+
['*', '*'],
53+
['de', 'de'],
54+
['en-US, en', 'en-us'],
55+
['af;q=0.9, ar;q=0.8, de;q=0.7, *;q=0.5', 'af'],
5656
];
5757
test.each(preferredLocaleFromHeader)(
5858
'ExpectedLocale from "%s" should be "%s"',

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

Lines changed: 149 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { createServer, Server } from 'http';
2-
import { CloudFrontRequestEvent, CloudFrontRequest } from 'aws-lambda';
2+
import {
3+
CloudFrontRequestEvent,
4+
CloudFrontRequest,
5+
CloudFrontHeaders,
6+
} from 'aws-lambda';
37
import getPort from 'get-port';
48

59
import { ProxyConfig } from '../types';
@@ -546,26 +550,157 @@ describe('[proxy] Handler', () => {
546550
TIMEOUT
547551
);
548552

549-
test(
550-
'i18n default locale rewrite',
551-
async () => {
553+
type i18nRedirectTest = [
554+
string,
555+
CloudFrontHeaders,
556+
string | undefined,
557+
CloudFrontHeaders
558+
];
559+
560+
const i18nRedirectTests: i18nRedirectTest[] = [
561+
[
562+
'default locale rewrite',
563+
{
564+
'accept-language': [
565+
{
566+
key: 'Accept-Language',
567+
value: '*',
568+
},
569+
],
570+
},
571+
'/en',
572+
{
573+
'accept-language': [
574+
{
575+
key: 'Accept-Language',
576+
value: '*',
577+
},
578+
],
579+
},
580+
],
581+
582+
[
583+
'language redirect nl',
584+
{
585+
'accept-language': [
586+
{
587+
key: 'Accept-Language',
588+
value: 'nl',
589+
},
590+
],
591+
},
592+
undefined,
593+
{
594+
location: [
595+
{
596+
key: 'Location',
597+
value: '/nl',
598+
},
599+
],
600+
},
601+
],
602+
603+
[
604+
'language redirect fr-FR',
605+
{
606+
'accept-language': [
607+
{
608+
key: 'Accept-Language',
609+
value: 'fr-FR, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5',
610+
},
611+
],
612+
},
613+
undefined,
614+
{
615+
location: [
616+
{
617+
key: 'Location',
618+
value: '/fr-fr',
619+
},
620+
],
621+
},
622+
],
623+
624+
[
625+
'Language override by cookie',
626+
{
627+
'accept-language': [
628+
{
629+
key: 'Accept-Language',
630+
value: 'Accept-Language: en',
631+
},
632+
],
633+
cookie: [
634+
{
635+
key: 'Cookie',
636+
value: 'NEXT_LOCALE=nl',
637+
},
638+
],
639+
},
640+
undefined,
641+
{
642+
location: [
643+
{
644+
key: 'Location',
645+
value: '/nl',
646+
},
647+
],
648+
},
649+
],
650+
651+
[
652+
'Redirect to i18n domain',
653+
{
654+
'accept-language': [
655+
{
656+
key: 'Accept-Language',
657+
value: 'de',
658+
},
659+
],
660+
},
661+
undefined,
662+
{
663+
location: [
664+
{
665+
key: 'Location',
666+
value: 'https://milli.is/',
667+
},
668+
],
669+
},
670+
],
671+
];
672+
673+
test.each(i18nRedirectTests)(
674+
'i18n: %s',
675+
async (_, requestHeaders, destUrl, responseHeaders) => {
552676
const proxyConfig: ProxyConfig = {
553677
lambdaRoutes: [],
554678
prerenders: {},
555679
staticRoutes: [],
556680
routes: [
557681
{
558-
src: '^/(?!(?:_next/.*|en|fr\\-FR|nl)(?:/.*|$))(.*)$',
682+
src: '^/(?!(?:_next/.*|en|fr\\-FR|nl|de)(?:/.*|$))(.*)$',
559683
dest: '$wildcard/$1',
560684
continue: true,
561685
},
686+
{
687+
src: '^//?(?:en|fr\\-FR|nl|de)?/?$',
688+
locale: {
689+
redirect: {
690+
de: 'https://milli.is/',
691+
},
692+
cookie: 'NEXT_LOCALE',
693+
},
694+
continue: true,
695+
},
562696
{
563697
src: '/',
564698
locale: {
565699
redirect: {
566700
en: '/',
567-
'fr-FR': '/fr-FR',
701+
'fr-fr': '/fr-fr',
568702
nl: '/nl',
703+
de: '/de',
569704
},
570705
cookie: 'NEXT_LOCALE',
571706
},
@@ -576,6 +711,11 @@ describe('[proxy] Handler', () => {
576711
dest: '/en',
577712
continue: true,
578713
},
714+
{
715+
src: '^/(?!(?:_next/.*|en|fr\\-FR|nl|de)(?:/.*|$))(.*)$',
716+
dest: '/en/$1',
717+
continue: true,
718+
},
579719
],
580720
};
581721
const requestPath = '/';
@@ -598,39 +738,7 @@ describe('[proxy] Handler', () => {
598738
},
599739
request: {
600740
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-
},
741+
headers: requestHeaders,
634742
method: 'GET',
635743
origin: {
636744
s3: {
@@ -664,13 +772,8 @@ describe('[proxy] Handler', () => {
664772

665773
const result = (await handler(event)) as CloudFrontRequest;
666774

667-
expect(result.origin?.s3).toEqual(
668-
expect.objectContaining({
669-
domainName: 's3.localhost',
670-
path: '',
671-
})
672-
);
673-
expect(result.uri).toBe('/en');
775+
// expect(result.uri).toBe(destUrl);
776+
expect(result.headers).toStrictEqual(responseHeaders);
674777
},
675778
TIMEOUT
676779
);

packages/proxy/src/handler.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ function isRedirect(
4141
routeResult.status <= 309
4242
) {
4343
if ('Location' in routeResult.headers) {
44-
let headers: CloudFrontHeaders = {};
44+
const headers: CloudFrontHeaders = {
45+
location: [{ key: 'Location', value: routeResult.headers.Location }],
46+
};
4547

4648
// If the redirect is permanent, add caching it
4749
if (routeResult.status === 301 || routeResult.status === 308) {
@@ -56,7 +58,7 @@ function isRedirect(
5658
return {
5759
status: routeResult.status.toString(),
5860
statusDescription: STATUS_CODES[routeResult.status],
59-
headers: convertToCloudFrontHeaders(headers, routeResult.headers),
61+
headers,
6062
};
6163
}
6264
}
@@ -65,7 +67,12 @@ function isRedirect(
6567
}
6668

6769
export async function handler(event: CloudFrontRequestEvent) {
68-
const { request } = event.Records[0].cf;
70+
const { request, config } = event.Records[0].cf;
71+
// Get the domain where the request came from
72+
const wildcard = request.headers.host
73+
? request.headers.host[0].value
74+
: config.distributionDomainName;
75+
6976
const configEndpoint = request.origin!.s3!.customHeaders[
7077
'x-env-config-endpoint'
7178
][0].value;
@@ -109,7 +116,10 @@ export async function handler(event: CloudFrontRequestEvent) {
109116
headers.host = apiEndpoint;
110117
} else {
111118
// Handle by proxy
112-
const proxyResult = proxy.route(requestPath);
119+
const proxyResult = proxy.route(requestPath, {
120+
reqHeaders: request.headers,
121+
wildcard,
122+
});
113123

114124
// Check for redirect
115125
const redirect = isRedirect(proxyResult);

0 commit comments

Comments
 (0)