概述
vary 库功能介绍
vary 主要功能是为了方便在响应头上添加 Vary 头信息。
HTTP 响应头 Vary 介绍
响应头 Vary 主要是用于缓存的控制。源服务器会通过 Vary 告诉代理服务器如何使用缓存。
当代理服务器接收到源服务器带有 Vary 响应头的响应后,若要对其进行缓存,则该缓存只对 Vary 指定的请求头字段相同的请求生效。所以即使对相同资源发起请求,但由于 Vary 指定的请求头字段不相同,也无法使用缓存,必须从源服务器中重新获取资源。
基本使用
vary
直接对 response 添加 vary 头信息
// 类型声明declare function vary(res: ServerResponse, field: string | string[]): void;// 使用http.createServer(function(req, res) {//...vary(res, 'User-Agent')//...})
vary.append
直接在已有的 vary 头信息上添加新的字段信息,合并两个 vary 字段
// 类型声明declare namespace vary {function append(header: string, field: string | string[]): string;}// 使用http.createServer(function(req, res) {//...const vary = vary.append('Accept, User-Agent', 'Origin')res.setHeader//...})
源码阅读
parse
主要对已有的 vary 字段信息进行结构,由字符串信息变成更好处理的数组。
eg: 'Origin, Accept, Origin' => ['Origin', 'Accept', 'Origin']
function parse (header: string): string[] {var end = 0var start = 0var list: string[] = []// gather tokens// 类似于词法分析中的直接扫描法for (var i = 0, len = header.length; i < len; i++) {// 遍历字符串switch (header.charCodeAt(i)) {// 可以忽略字段开头和结尾的空格,但不会忽略字段中间的空格case 0x20: /* */if (start === end) {start = end = i + 1}break// 根据 ',' 拆分字段,并加入集合case 0x2c: /* , */list.push(header.substring(start, end))start = end = i + 1break// 正常字符则略过default:end = i + 1break}}// final token// 可能有残留字段,并加入集合list.push(header.substring(start, end))return list}
vary.append
function append (header, field) {// 入参校验if (typeof header !== 'string') {throw new TypeError('header argument is required')}// 入参校验if (!field) {throw new TypeError('field argument is required')}// get fields array// 解析需要添加的字段成数组var fields = !Array.isArray(field)? parse(String(field)): field// assert on invalid field names// 字段验证for (var j = 0; j < fields.length; j++) {if (!FIELD_NAME_REGEXP.test(fields[j])) {throw new TypeError('field argument contains an invalid header name')}}// existing, unspecified vary// 如果本来的 vary 是 '*' (所有的请求都被视为唯一并且非缓存的),则忽略合并并返回if (header === '*') {return header}// enumerate current values// 解析原有的 vary 字段成数组var val = headervar vals = parse(header.toLowerCase())// 与上面一个判断同等意义,如果有 vary 字段为 '*',则直接返回 '*'if (fields.indexOf('*') !== -1 || vals.indexOf('*') !== -1) {return '*'}// 合并字段for (var i = 0; i < fields.length; i++) {var fld = fields[i].toLowerCase()// append value (case-preserving)if (vals.indexOf(fld) === -1) {vals.push(fld)val = val? val + ', ' + fields[i]: fields[i]}}return val}
vary
function vary (res, field) {// 判断 res 是否为 SeverResponseif (!res || !res.getHeader || !res.setHeader) {// quack quackthrow new TypeError('res argument is required')}// get existing header// 获取原有的 Vary 头信息,并字符串化var val = res.getHeader('Vary') || ''var header = Array.isArray(val)? val.join(', '): String(val)// set new header// 调用 append 合并字段,并设置为新值if ((val = append(header, field))) {res.setHeader('Vary', val)}}
