You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
blockvote/node_modules/fresh/index.js

153 lines
6.4 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*!
* fresh
* Copyright(c) 2012 TJ Holowaychuk
* Copyright(c) 2016-2017 Douglas Christopher Wilson
* MIT Licensed
*/
'use strict'
/**
* RegExp to check for no-cache token in Cache-Control.
* @private
*/
var CACHE_CONTROL_NO_CACHE_REGEXP = /(?:^|,)\s*?no-cache\s*?(?:,|$)/
/**
* Module exports.
* @public
*/
module.exports = fresh
/**
* Check freshness of the response using request and response headers.
*
* @param {Object} reqHeaders
* @param {Object} resHeaders
* @return {Boolean}
* @public
*/
// 判断缓存中的资源是否新鲜的函数,接收请求头和响应头作为参数
function fresh (reqHeaders, resHeaders) {
// 从请求头中获取 if-modified-since 字段的值,用于后续判断资源是否有修改
var modifiedSince = reqHeaders['if-modified-since'];
// 从请求头中获取 if-none-match 字段的值用于基于实体标签ETag验证资源是否变化
var noneMatch = reqHeaders['if-none-match'];
// unconditional request
// 如果 if-modified-since 和 if-none-match 字段都不存在,说明是无条件请求,直接返回 false表示资源不是新鲜的需重新获取资源
if (!modifiedSince &&!noneMatch) {
return false;
}
// Always return stale when Cache-Control: no-cache
// to support end-to-end reload requests
// https://tools.ietf.org/html/rfc2616#section-14.9.4
// 从请求头中获取 cache-control 字段的值,用于判断是否有 no-cache 指令
var cacheControl = reqHeaders['cache-control'];
// 如果 cache-control 字段存在且匹配表示 no-cache 的正则表达式(这里假设 CACHE_CONTROL_NO_CACHE_REGEXP 在外部已定义好),则返回 false意味着要忽略缓存重新获取资源遵循相关网络协议标准
if (cacheControl && CACHE_CONTROL_NO_CACHE_REGEXP.test(cacheControl)) {
return false;
}
// if-none-match
// 如果 if-none-match 字段存在且不等于通配符 *,则进行基于 ETag 的验证
if (noneMatch && noneMatch!== '*') {
// 从响应头中获取 etag实体标签的值用于和请求头中的 if-none-match 进行匹配对比
var etag = resHeaders['etag'];
// 如果响应头中没有 etag则返回 false即资源不是新鲜的
if (!etag) {
return false;
}
// 先假设基于 ETag 的验证是不通过的(资源是陈旧的),后续通过循环对比来更新这个状态
var etagStale = true;
// 调用 parseTokenList 函数解析 if-none-match 字段的值为一个令牌列表,方便后续逐个对比
var matches = parseTokenList(noneMatch);
for (var i = 0; i < matches.length; i++) {
var match = matches[i];
// 将列表中的每个值与 etag 进行比较(考虑了弱验证等不同格式匹配情况,如 W/ 开头的弱验证格式),只要有匹配的就将 etagStale 设为 false表示资源是新鲜的基于 ETag 验证通过)
if (match === etag || match === 'W/' + etag || 'W/' + match === etag) {
etagStale = false;
break;
}
}
// 如果经过遍历对比后,基于 ETag 的验证还是不通过etagStale 为 true则返回 false即资源不是新鲜的
if (etagStale) {
return false;
}
}
// if-modified-since
// 如果 if-modified-since 字段存在,则进行基于资源最后修改时间的验证
if (modifiedSince) {
// 从响应头中获取 last-modified资源最后修改时间的值
var lastModified = resHeaders['last-modified'];
// 判断资源是否陈旧modifiedStale如果 lastModified 不存在或者通过 parseHttpDate 函数解析后的 lastModified 时间大于 modifiedSince 时间(即资源在 if-modified-since 所指定的时间之后有修改就认为资源是陈旧的modifiedStale 为 true
var modifiedStale =!lastModified ||!(parseHttpDate(lastModified) <= parseHttpDate(modifiedSince));
// 如果资源是陈旧的modifiedStale 为 true则返回 false即资源不是新鲜的
if (modifiedStale) {
return false;
}
}
// 如果经过前面所有的验证条件都通过了,最后返回 true表示资源是新鲜的可以使用缓存
return true;
}
// Parse an HTTP Date into a number.
// 将一个 HTTP 日期格式的字符串解析为时间戳(数字形式),该函数是私有函数(可能只在内部使用)
function parseHttpDate (date) {
// 尝试使用 Date.parse 方法解析传入的日期字符串,将解析结果赋给 timestamp 变量
var timestamp = date && Date.parse(date);
// istanbul ignore next: guard against date.js Date.parse patching
// 通过条件判断,如果 timestamp 的类型是 number就返回该时间戳否则返回 NaN处理 Date.parse 可能出现的解析失败情况,同时告诉测试框架(如 Istanbul忽略下面这行代码的覆盖情况可能因外部对 Date.parse 修改等特殊情况不好测试)
return typeof timestamp === 'number'
? timestamp
: NaN;
}
// Parse a HTTP token list.
// 解析一个类似 HTTP 令牌列表格式的字符串,该函数是私有函数(可能只在内部使用)
function parseTokenList (str) {
var end = 0;
var list = [];
var start = 0;
// gather tokens
// 遍历字符串中的每个字符根据字符的编码来判断如何划分令牌token
for (var i = 0, len = str.length; i < len; i++) {
switch (str.charCodeAt(i)) {
case 0x20: /* */
// 如果字符编码是空格0x20且当前开始位置和结束位置相同说明连续的空格开头情况就更新开始和结束位置为下一个字符位置
if (start === end) {
start = end = i + 1;
}
break;
case 0x2c: /*, */
// 如果字符编码是逗号0x2c就将从 start 到 end 位置的子字符串作为一个令牌添加到 list 数组中,并更新 start 和 end 位置为下一个字符位置
list.push(str.substring(start, end));
start = end = i + 1;
break;
default:
// 对于其他字符情况,就更新 end 位置到下一个字符
end = i + 1;
break;
}
}
// final token
// 将最后一个划分出来的令牌(从 start 到 end 位置的子字符串)添加到 list 数组中
list.push(str.substring(start, end));
// 返回解析好的令牌列表数组,用于后续对类似 if-none-match 等字段值的进一步处理
return list;
}