AWS Cloudfront + lambda@edge 修改 html 内容(使所有链接都是绝对的 -> 相对的)
AWS Cloudfront + lambda@edge modify html content (making all links absolute -> relative)
我(可能错误地)假设 lambda@edge 可以修改 origin.responce 内容,
所以写了一个像这样的 lambda 函数:
/* this does not work. response.Body is not defined */
'use strict';
exports.handler = (event, context, callback) => {
var response = event.Records[0].cf.response;
var data = response.Body.replace(/OLDTEXT/g, 'NEWTEXT');
response.Body = data;
callback(null, response);
};
失败,因为您无法使用此语法引用原始响应正文。
我能否修改此脚本以使其按预期工作,或者我是否应该考虑在 AWS 上使用其他服务?
我的背景:
我们正在尝试设置一个 AWS Cloudfront 分布,它整合了对多个网站的访问,如下所示:
ttp://foo.com/ -> https:/newsite.com/foo/
ttp://bar.com/ -> https:/newsite.com/bar/
ttp://boo.com/ -> https:/newsite.com/boo/
这些网站目前由外部方管理。我们想禁止直接 public 访问 foo/bar/boo,并且只有 newsite.com 作为互联网上唯一可见的站点。
将原点映射到单个 c-f 分布相对简单。
但是这样做会破坏 html 指定文件的内容 url,
如果他们当前的域名已从网络中删除。
ttp://foo.com/images/1.jpg
-> (disable foo.com dns)
-> image not found
为了受益于云端缓存和其他优点,
我想 modify/rewrite html 文件中的所有绝对文件引用到相对 url -
所以
<img src="ttp://foo.com/images/1.jpg">
变成
<img src="/foo/images/1.jpg">
//(accessed as https:/newsite.com/foo/images/1.jpg from a user)
//(maybe I should make it an absolte url for SEO purpose)
(http改为ttp,由于使用被禁域名的限制foo.com)
(编辑)
我找到了这个 AWS 博客,这可能是一个很好的提示,但感觉有点太复杂了,超出了我的预期。 (设置一个 linux 容器,这样我就可以使用 sed 来处理 html 文件,也许使用 S3 作为临时存储)
希望我能找到更简单的方法:
https://aws.amazon.com/blogs/networking-and-content-delivery/resizing-images-with-amazon-cloudfront-lambdaedge-aws-cdn-blog/
不幸的是,根据我刚刚了解到的情况,您无法修改 Lambda@edge 中的响应主体。您只能擦除或完全替换正文内容。我希望能够清除旧站点的所有响应,但使用 Cloudfront Lambda@Edge 将不允许这样做。
正如 AWS 文档所述 here :
当您处理 HTTP 响应时,Lambda@Edge 不会将源服务器返回的主体暴露给源响应触发器。您可以通过将其设置为所需值来生成静态内容主体,或通过将值设置为空来移除函数内的主体。如果您不更新函数中的 body 字段,则原始服务器返回的原始 body 将返回给查看器。
我 运行 遇到了同样的问题,并且已经能够从请求 headers 中提取一些信息来拼凑一个 URL 从中我可以获取原始 body.
注意:我还不能确认这是一个“安全”的方法,就像它可能依赖于未记录的行为等,但现在它确实正确地获取了原始 body,对我来说。当然它还需要另一个请求/往返,可能会推断出一些额外的 t运行sfer 成本、执行时间等
const fetchOriginalBody = (request) => {
const host = request['headers']['host'][0]['value']; // xxxx.yyy.com
const uri = request['uri'];
const fetchOriginalBodyUrl = 'https://' + host + uri;
return httpsRequest(fetchOriginalBodyUrl);
}
// Helper that turns https.request into a promise
function httpsRequest(options) {
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
if (res.statusCode < 200 || res.statusCode >= 300) {
return reject(new Error('statusCode=' + res.statusCode));
}
var body = [];
res.on('data', function(chunk) {
body.push(chunk);
});
res.on('end', function() {
try {
body = Buffer.concat(body).toString();
// body = JSON.parse(Buffer.concat(body).toString());
} catch(e) {
reject(e);
}
resolve(body);
});
});
req.on('error', (e) => {
reject(e.message);
});
req.end();
});
}
exports.handler = async (event, context, callback) => {
const records = event.Records;
if (records && records.length > 0) {
const request = records[0].cf.request;
const body = await fetchOriginalBody(request);
}
...
我(可能错误地)假设 lambda@edge 可以修改 origin.responce 内容, 所以写了一个像这样的 lambda 函数:
/* this does not work. response.Body is not defined */
'use strict';
exports.handler = (event, context, callback) => {
var response = event.Records[0].cf.response;
var data = response.Body.replace(/OLDTEXT/g, 'NEWTEXT');
response.Body = data;
callback(null, response);
};
失败,因为您无法使用此语法引用原始响应正文。
我能否修改此脚本以使其按预期工作,或者我是否应该考虑在 AWS 上使用其他服务?
我的背景:
我们正在尝试设置一个 AWS Cloudfront 分布,它整合了对多个网站的访问,如下所示:
ttp://foo.com/ -> https:/newsite.com/foo/
ttp://bar.com/ -> https:/newsite.com/bar/
ttp://boo.com/ -> https:/newsite.com/boo/
这些网站目前由外部方管理。我们想禁止直接 public 访问 foo/bar/boo,并且只有 newsite.com 作为互联网上唯一可见的站点。
将原点映射到单个 c-f 分布相对简单。 但是这样做会破坏 html 指定文件的内容 url, 如果他们当前的域名已从网络中删除。
ttp://foo.com/images/1.jpg
-> (disable foo.com dns)
-> image not found
为了受益于云端缓存和其他优点,
我想 modify/rewrite html 文件中的所有绝对文件引用到相对 url -
所以
<img src="ttp://foo.com/images/1.jpg">
变成
<img src="/foo/images/1.jpg">
//(accessed as https:/newsite.com/foo/images/1.jpg from a user)
//(maybe I should make it an absolte url for SEO purpose)
(http改为ttp,由于使用被禁域名的限制foo.com)
(编辑) 我找到了这个 AWS 博客,这可能是一个很好的提示,但感觉有点太复杂了,超出了我的预期。 (设置一个 linux 容器,这样我就可以使用 sed 来处理 html 文件,也许使用 S3 作为临时存储) 希望我能找到更简单的方法: https://aws.amazon.com/blogs/networking-and-content-delivery/resizing-images-with-amazon-cloudfront-lambdaedge-aws-cdn-blog/
不幸的是,根据我刚刚了解到的情况,您无法修改 Lambda@edge 中的响应主体。您只能擦除或完全替换正文内容。我希望能够清除旧站点的所有响应,但使用 Cloudfront Lambda@Edge 将不允许这样做。
正如 AWS 文档所述 here :
当您处理 HTTP 响应时,Lambda@Edge 不会将源服务器返回的主体暴露给源响应触发器。您可以通过将其设置为所需值来生成静态内容主体,或通过将值设置为空来移除函数内的主体。如果您不更新函数中的 body 字段,则原始服务器返回的原始 body 将返回给查看器。
我 运行 遇到了同样的问题,并且已经能够从请求 headers 中提取一些信息来拼凑一个 URL 从中我可以获取原始 body.
注意:我还不能确认这是一个“安全”的方法,就像它可能依赖于未记录的行为等,但现在它确实正确地获取了原始 body,对我来说。当然它还需要另一个请求/往返,可能会推断出一些额外的 t运行sfer 成本、执行时间等
const fetchOriginalBody = (request) => {
const host = request['headers']['host'][0]['value']; // xxxx.yyy.com
const uri = request['uri'];
const fetchOriginalBodyUrl = 'https://' + host + uri;
return httpsRequest(fetchOriginalBodyUrl);
}
// Helper that turns https.request into a promise
function httpsRequest(options) {
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
if (res.statusCode < 200 || res.statusCode >= 300) {
return reject(new Error('statusCode=' + res.statusCode));
}
var body = [];
res.on('data', function(chunk) {
body.push(chunk);
});
res.on('end', function() {
try {
body = Buffer.concat(body).toString();
// body = JSON.parse(Buffer.concat(body).toString());
} catch(e) {
reject(e);
}
resolve(body);
});
});
req.on('error', (e) => {
reject(e.message);
});
req.end();
});
}
exports.handler = async (event, context, callback) => {
const records = event.Records;
if (records && records.length > 0) {
const request = records[0].cf.request;
const body = await fetchOriginalBody(request);
}
...