從小到大都是優,你讓我怎麼從良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之旅吧