从小到大都是优,你让我怎么从良git
昨天解读了fresh这个库,咱们了解了服务器是如何对比文件是否更新了,其中用到了etag,那么今天咱们就趁热打铁,了解下etag是怎么生成的,一样是来自jshttp的ETag库。github
ETag是干什么的,这里摘读下MDN的官方解释bash
若是给定URL中的资源更改,则必定要生成新的Etag值。 所以Etags相似于指纹,也可能被某些服务器用于跟踪。 比较etags能快速肯定此资源是否变化,但也可能被跟踪服务器永久存留。服务器
也就是说,资源发生改变的时候,etag的值必定发生了改变,那么etag又是如何生成的呢?内容变化,etag值就发生变化,那么就是说咱们能够借助内容来生成etag。又到了,talk is cheap, show me the code 环节。ui
首先看入口,其中参数entity是要传进来的内容,options是可选值,那么entity若是是空值,确定不行,直接抛出错误。spa
function etag (entity, options) {
if (entity == null) {
throw new TypeError('argument entity is required')
}
var isStats = isstats(entity)
...
}
复制代码
entity的值除了是资源内容以外,也有多是资源的状态对象,既包含文件建立时间、上次修改时间等一系列信息的对象,那么咱们这时要先判断一下,由于使用stats对象生成Etag的策略跟使用资源内容的策略稍微不一样。code
function isstats (obj) {
// genuine fs.Stats
if (typeof Stats === 'function' && obj instanceof Stats) {
return true
}
// quack quack
return obj && typeof obj === 'object' &&
'ctime' in obj && toString.call(obj.ctime) === '[object Date]' &&
'mtime' in obj && toString.call(obj.mtime) === '[object Date]' &&
'ino' in obj && typeof obj.ino === 'number' &&
'size' in obj && typeof obj.size === 'number'
}
复制代码
那么如何判断一个对象是不是stats呢?从上面咱们能够看到,先看看它是否是fs.Stats的实例或者它有没有Stats对象该有的属性,知足其中一个,则说明entity是Stats对象。cdn
接下来有个ETag的小知识点,不知道有没有人留意过,有些ETag是以W/
开头的,啥意思呢,咱们再来看看MDN的官方解释对象
'W/'(大小写敏感) 表示使用弱验证器。 弱验证器很容易生成,但不利于比较。 强验证器是比较的理想选择,但很难有效地生成。 相同资源的两个弱Etag值可能语义等同,但不是每一个字节都相同。blog
W/
开头的ETag是弱生成器。那么若是咱们前面传进来的option.weak的值为true或者entity是Stats对象,那么将会使用弱验证器,由于Stats对象是有必定概率同样的,因此也属于弱验证器,但若是一个文件在秒级别常常被修改,但最后一次修改完的内容跟第一次同样,对于弱etag来讲,可能etag不同,可是强etag来讲,值是同样的,
var weak = options && typeof options.weak === 'boolean'
? options.weak
: isStats
复制代码
在这里,咱们最后校验下传进来的参数,若是不是entity不是stats也不是string甚至也不是buffer,那直接抛出异常
// validate argument
if (!isStats && typeof entity !== 'string' && !Buffer.isBuffer(entity)) {
throw new TypeError('argument entity must be string, Buffer, or fs.Stats')
}
复制代码
OK,那若是参数都没问题,咱们就开始真正地生成Etag了
stat的entity使用stattag策略生成,其中stattag经过获取文件大小加上最近修改时间的16进制生成etag
而其余的使用entitytag生成策略,entity长度为0返回默认值,若是不为0,借助crypt去base64生成摘要且只截取前面27位,最后用文件大小和摘要生成etag。
咱们能够看到,两种生成策略,惟一的区别是,一个是用mtime另外一个是用内容摘要
// generate entity tag
var tag = isStats
? stattag(entity)
: entitytag(entity)
function stattag (stat) {
var mtime = stat.mtime.getTime().toString(16)
var size = stat.size.toString(16)
return '"' + size + '-' + mtime + '"'
}
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 + '"'
}
复制代码
最后一步,若是是弱etag,前面加上W/
,打完收工
return weak
? 'W/' + tag
: tag
复制代码
对的,咱们已经讲完如何生成ETag
了,是否是又加深了对http的理解,那就快快关注我,让咱们一块儿继续后面的jshttp之旅吧