概述

vary 库功能介绍

vary 主要功能是为了方便在响应头上添加 Vary 头信息。

HTTP 响应头 Vary 介绍

响应头 Vary 主要是用于缓存的控制。源服务器会通过 Vary 告诉代理服务器如何使用缓存。
当代理服务器接收到源服务器带有 Vary 响应头的响应后,若要对其进行缓存,则该缓存只对 Vary 指定的请求头字段相同的请求生效。所以即使对相同资源发起请求,但由于 Vary 指定的请求头字段不相同,也无法使用缓存,必须从源服务器中重新获取资源。

基本使用

vary

直接对 response 添加 vary 头信息

  1. // 类型声明
  2. declare function vary(res: ServerResponse, field: string | string[]): void;
  3. // 使用
  4. http.createServer(function(req, res) {
  5. //...
  6. vary(res, 'User-Agent')
  7. //...
  8. })

vary.append

直接在已有的 vary 头信息上添加新的字段信息,合并两个 vary 字段

  1. // 类型声明
  2. declare namespace vary {
  3. function append(header: string, field: string | string[]): string;
  4. }
  5. // 使用
  6. http.createServer(function(req, res) {
  7. //...
  8. const vary = vary.append('Accept, User-Agent', 'Origin')
  9. res.setHeader
  10. //...
  11. })

源码阅读

parse

主要对已有的 vary 字段信息进行结构,由字符串信息变成更好处理的数组。
eg: 'Origin, Accept, Origin' => ['Origin', 'Accept', 'Origin']

  1. function parse (header: string): string[] {
  2. var end = 0
  3. var start = 0
  4. var list: string[] = []
  5. // gather tokens
  6. // 类似于词法分析中的直接扫描法
  7. for (var i = 0, len = header.length; i < len; i++) {
  8. // 遍历字符串
  9. switch (header.charCodeAt(i)) {
  10. // 可以忽略字段开头和结尾的空格,但不会忽略字段中间的空格
  11. case 0x20: /* */
  12. if (start === end) {
  13. start = end = i + 1
  14. }
  15. break
  16. // 根据 ',' 拆分字段,并加入集合
  17. case 0x2c: /* , */
  18. list.push(header.substring(start, end))
  19. start = end = i + 1
  20. break
  21. // 正常字符则略过
  22. default:
  23. end = i + 1
  24. break
  25. }
  26. }
  27. // final token
  28. // 可能有残留字段,并加入集合
  29. list.push(header.substring(start, end))
  30. return list
  31. }

vary.append

  1. function append (header, field) {
  2. // 入参校验
  3. if (typeof header !== 'string') {
  4. throw new TypeError('header argument is required')
  5. }
  6. // 入参校验
  7. if (!field) {
  8. throw new TypeError('field argument is required')
  9. }
  10. // get fields array
  11. // 解析需要添加的字段成数组
  12. var fields = !Array.isArray(field)
  13. ? parse(String(field))
  14. : field
  15. // assert on invalid field names
  16. // 字段验证
  17. for (var j = 0; j < fields.length; j++) {
  18. if (!FIELD_NAME_REGEXP.test(fields[j])) {
  19. throw new TypeError('field argument contains an invalid header name')
  20. }
  21. }
  22. // existing, unspecified vary
  23. // 如果本来的 vary 是 '*' (所有的请求都被视为唯一并且非缓存的),则忽略合并并返回
  24. if (header === '*') {
  25. return header
  26. }
  27. // enumerate current values
  28. // 解析原有的 vary 字段成数组
  29. var val = header
  30. var vals = parse(header.toLowerCase())
  31. // 与上面一个判断同等意义,如果有 vary 字段为 '*',则直接返回 '*'
  32. if (fields.indexOf('*') !== -1 || vals.indexOf('*') !== -1) {
  33. return '*'
  34. }
  35. // 合并字段
  36. for (var i = 0; i < fields.length; i++) {
  37. var fld = fields[i].toLowerCase()
  38. // append value (case-preserving)
  39. if (vals.indexOf(fld) === -1) {
  40. vals.push(fld)
  41. val = val
  42. ? val + ', ' + fields[i]
  43. : fields[i]
  44. }
  45. }
  46. return val
  47. }

vary

  1. function vary (res, field) {
  2. // 判断 res 是否为 SeverResponse
  3. if (!res || !res.getHeader || !res.setHeader) {
  4. // quack quack
  5. throw new TypeError('res argument is required')
  6. }
  7. // get existing header
  8. // 获取原有的 Vary 头信息,并字符串化
  9. var val = res.getHeader('Vary') || ''
  10. var header = Array.isArray(val)
  11. ? val.join(', ')
  12. : String(val)
  13. // set new header
  14. // 调用 append 合并字段,并设置为新值
  15. if ((val = append(header, field))) {
  16. res.setHeader('Vary', val)
  17. }
  18. }

参考

Vary | MDN
HTTP 协议中 Vary 的一些研究