响应头中的 ETag 值是如何生成的
要求
- 当文件不会更改时,ETAG 值保持不变。
- 计算速度快,耗费CPU资源少
- 便于扩展,多个服务器上此资源生成的 ETAG 值要相同。
nginx 中 ETag 的生成
Content-Length: 612
Last-Modified: Tue, 23 Apr 2019 10:18:21 GMT
ETag: "5cbee66d-264"
计算规则
new Date(parseInt('5cbee66d', 16) * 1000).toJSON()
"2019-04-23T10:18:21.000Z"
> parseInt('264', 16)
612
express 采用 etag 库生成
静态资源一般都只是生成的长度-时间戳而已,
动态接口可以配置,可以算hash。
app.set('etag', true); // weak ETag,长度-时间戳
app.set('etag', 'strong'); // strong ETag, hash
app.set('etag', function (body, encoding) {
return generateHash(body, encoding); // consider the function is defined
});
核心代码详细见这里,截取一小段:
function etag (entity, options) {
// ...
// generate entity tag
var tag = isStats
? stattag(entity)
: entitytag(entity)
// ...
}
function entitytag (entity) {
if (entity.length === 0) {
// fast-path empty
return '"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"'
}
// compute hash of entity
var hash = crypto
.createHash('sha1')
.update(entity, 'utf8')
.digest('base64')
.substring(0, 27)
// compute length of entity
var len = typeof entity === 'string'
? Buffer.byteLength(entity, 'utf8')
: entity.length
return '"' + len.toString(16) + '-' + hash + '"'
}
function stattag (stat) {
var mtime = stat.mtime.getTime().toString(16)
var size = stat.size.toString(16)
return '"' + size + '-' + mtime + '"'
}
真实线上静态资源文件的Etag。
ETag: "713-1551786316000"
Last-Modified: Tue, 05 Mar 2019 11:45:16 GMT
ETag: "SAdqG/HfsklgQeEc+r/SZg=="
Last-Modified: Thu, 16 Jan 2020 06:17:05 GMT
而 koa 的 etag 插件底层还是 express 的 etag。