使用Lambda@Edge与CloudFront配置域的重定向规则的相关代码

76 阅读2分钟

我是一个很酷的URL和不破坏互联网的大粉丝,所以多年来我积累了一些域名,我为这些域名实施了很多重定向规则。近年来,我使用mod_rewrite实现了这些规则,并在Apache和我的LinodeVPS上运行它们,例如 blog.floehopper.org 的重定向规则。

然而,在2019年9月,我开始在GitHub Pages上发布这个网站,在这期间我几乎删除了我在该VPS上运行的所有其他东西。因此,我开始考虑关闭VPS并停止支付它的费用,这将是一件很好的事情!上面提到的遗留重定向是唯一阻止我这样做的事情。

在过去的18个月里,我在工作中和个人项目中都大量使用了AWS CDK。不幸的是,我只发表了一篇关于它的文章(我希望在不远的将来能弥补这一点)。总之,我读了一些关于Lambda@Edge的文章,并注意到CloudFormation和AWS CDK确实支持它,所以我想我要试一试。

我的基本想法是创建一个CloudFront分布,将一个自定义域名与之关联,配置一个Edge Lambda函数来处理对该分布的所有请求,并在JavaScript中实现适当的重定向,然后将相关的DNS记录指向CloudFront分布。 这被证明是非常简单的,尽管我必须使用AWS证书管理器来为该域名创建一个SSL证书,以使一切正常工作。

你可以在GitHub上找到完整的项目源代码。恐怕我还没有来得及更新AWS CDK生成的默认版本的README。如果你愿意,这里有几个关键项目文件的略微删减版本,说明我是如何为blog.floehopper.org

# file: lib/edge-redirector-stack.ts      
import * as cdk from '@aws-cdk/core';    
import * as lambda from '@aws-cdk/aws-lambda';     
import * as cloudfront from '@aws-cdk/aws-cloudfront';     
import * as origins from '@aws-cdk/aws-cloudfront-origins';  
import * as acm from '@aws-cdk/aws-certificatemanager';     
export class EdgeRedirectorStack extends cdk.Stack {       
     constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) 
     {         
         super(scope, id, props);      
         this.createDistribution('blog.floehopper.org', 'blogFloehopperOrg', 'arn:aws:acm:us-east-1:687105911108:certificate/aa11ee5a-54db-4a04-8307-f77330f86cb5');    
     }      
     createDistribution(domain: string, handler: string, certificateArn: string)
    {       
        const certificate = acm.Certificate.fromCertificateArn(this,`${handler}Certificate`, certificateArn);        
       new cloudfront.Distribution(this, `${handler}Distribution`, 
       {          
           defaultBehavior: {origin: new origins.HttpOrigin('example.com'), edgeLambdas: [{               eventType: cloudfront.LambdaEdgeEventType.VIEWER_REQUEST,               functionVersion: this.redirectVersion(domain, handler) }]},          domainNames: [domain], certificate: certificate,           enableLogging: true});       
       }       
       redirectVersion(domain: string, handler: string) :lambda.IVersion {         
       const redirectFunction = new cloudfront.experimental.EdgeFunction(this, `${handler}Redirect`, {           runtime: lambda.Runtime.NODEJS_12_X,handler:`${handler}.handler`,  code: lambda.Code.fromAsset('./lambdaFunctions/redirect')});     
       return redirectFunction.currentVersion;       
  }     
}
# file: lambdaFunctions/redirect/blogFloehopperOrg.js   
'use strict';      
exports.handler = function(event, context, callback) {      
const request = event.Records[0].cf.request;   
const mapping = [//Legacy Typo-style articles ['^/articles/([0-9]{4})/([0-9]{2})/([0-9]{2})/(.+)$', (m) => `http://jamesmead.org/blog/${m[1]}-${m[2]}-${m[3]}-${m[4]}`],          // Redirect blog.floehopper.org -> jamesmead.org ['^/(.*)$', (m) => `http://jamesmead.org/${m[1]}`],      ['^$', (m) => `http://jamesmead.org`],     ];     
let redirectUrl;      
for (const [pattern, url] of mapping) {         
     const match = request.uri.match(new RegExp(pattern));   
     if (match) {           
         if (typeof(url) == 'function') {    
               redirectUrl = url(match);         
         } else {            
                      redirectUrl = url;   
                };    
         break;   
     };       
};       
let response;       
if (redirectUrl) {        
    response = {status: '301',statusDescription: 'Moved Permanently',headers: { location: [{ key: 'Location', value: redirectUrl}],} };       
} else {       
          response = {status: '404',statusDescription: 'Not Found' };      
};   
callback(null, response);    
};