3 * Copyright(c) 2014-2017 Douglas Christopher Wilson
14 module.exports.append = append
17 * RegExp to match field-name in RFC 7230 sec 3.2
21 * tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
22 * / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
24 * ; any VCHAR, except delimiters
27 var FIELD_NAME_REGEXP = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/
30 * Append a field to a vary header.
32 * @param {String} header
33 * @param {String|Array} field
38 function append (header, field) {
39 if (typeof header !== 'string') {
40 throw new TypeError('header argument is required')
44 throw new TypeError('field argument is required')
48 var fields = !Array.isArray(field)
49 ? parse(String(field))
52 // assert on invalid field names
53 for (var j = 0; j < fields.length; j++) {
54 if (!FIELD_NAME_REGEXP.test(fields[j])) {
55 throw new TypeError('field argument contains an invalid header name')
59 // existing, unspecified vary
64 // enumerate current values
66 var vals = parse(header.toLowerCase())
69 if (fields.indexOf('*') !== -1 || vals.indexOf('*') !== -1) {
73 for (var i = 0; i < fields.length; i++) {
74 var fld = fields[i].toLowerCase()
76 // append value (case-preserving)
77 if (vals.indexOf(fld) === -1) {
80 ? val + ', ' + fields[i]
89 * Parse a vary header into an array.
91 * @param {String} header
96 function parse (header) {
102 for (var i = 0, len = header.length; i < len; i++) {
103 switch (header.charCodeAt(i)) {
110 list.push(header.substring(start, end))
120 list.push(header.substring(start, end))
126 * Mark that a request is varied on a header field.
128 * @param {Object} res
129 * @param {String|Array} field
133 function vary (res, field) {
134 if (!res || !res.getHeader || !res.setHeader) {
136 throw new TypeError('res argument is required')
139 // get existing header
140 var val = res.getHeader('Vary') || ''
141 var header = Array.isArray(val)
146 if ((val = append(header, field))) {
147 res.setHeader('Vary', val)