後會無期之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之旅吧

相關文章
相關標籤/搜索