后会无期之jshttp解析(2)

从小到大都是优,你让我怎么从良git

昨天解读了fresh这个库,咱们了解了服务器是如何对比文件是否更新了,其中用到了etag,那么今天咱们就趁热打铁,了解下etag是怎么生成的,一样是来自jshttp的ETag库。github

ETag

ETag是干什么的,这里摘读下MDN的官方解释bash

若是给定URL中的资源更改,则必定要生成新的Etag值。 所以Etags相似于指纹,也可能被某些服务器用于跟踪。 比较etags能快速肯定此资源是否变化,但也可能被跟踪服务器永久存留。服务器

也就是说,资源发生改变的时候,etag的值必定发生了改变,那么etag又是如何生成的呢?内容变化,etag值就发生变化,那么就是说咱们能够借助内容来生成etag。又到了,talk is cheap, show me the code 环节。ui

解读ETag

首先看入口,其中参数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之旅吧

相关文章
相关标签/搜索