pax_global_header00006660000000000000000000000064131616070550014516gustar00rootroot0000000000000052 comment=4067e646233fbc8ec9e7a9cd78d6f063c6fdc17e vary-1.1.2/000077500000000000000000000000001316160705500125005ustar00rootroot00000000000000vary-1.1.2/.eslintignore000066400000000000000000000000261316160705500152010ustar00rootroot00000000000000coverage node_modules vary-1.1.2/.eslintrc000066400000000000000000000000341316160705500143210ustar00rootroot00000000000000{ "extends": "standard" } vary-1.1.2/.gitignore000066400000000000000000000000521316160705500144650ustar00rootroot00000000000000coverage/ node_modules/ package-lock.json vary-1.1.2/.travis.yml000066400000000000000000000021171316160705500146120ustar00rootroot00000000000000language: node_js node_js: - "0.8" - "0.10" - "0.12" - "1.8" - "2.5" - "3.3" - "4.8" - "5.12" - "6.11" - "7.10" - "8.5" sudo: false cache: directories: - node_modules before_install: # Skip updating shrinkwrap / lock - "npm config set shrinkwrap false" # Remove all non-test dependencies - "npm rm --save-dev beautify-benchmark benchmark" # Setup Node.js version-specific dependencies - "test $TRAVIS_NODE_VERSION != '0.8' || npm rm --save-dev istanbul" - "test $(echo $TRAVIS_NODE_VERSION | cut -d. -f1) -ge 4 || npm rm --save-dev $(grep -E '\"eslint\\S*\"' package.json | cut -d'\"' -f2)" # Update Node.js modules - "test ! -d node_modules || npm prune" - "test ! -d node_modules || npm rebuild" script: # Run test script, depending on istanbul install - "test ! -z $(npm -ps ls istanbul) || npm test" - "test -z $(npm -ps ls istanbul) || npm run-script test-travis" - "test -z $(npm -ps ls eslint ) || npm run-script lint" after_script: - "test -e ./coverage/lcov.info && npm install coveralls@2 && cat ./coverage/lcov.info | coveralls" vary-1.1.2/HISTORY.md000066400000000000000000000014301316160705500141610ustar00rootroot000000000000001.1.2 / 2017-09-23 ================== * perf: improve header token parsing speed 1.1.1 / 2017-03-20 ================== * perf: hoist regular expression 1.1.0 / 2015-09-29 ================== * Only accept valid field names in the `field` argument - Ensures the resulting string is a valid HTTP header value 1.0.1 / 2015-07-08 ================== * Fix setting empty header from empty `field` * perf: enable strict mode * perf: remove argument reassignments 1.0.0 / 2014-08-10 ================== * Accept valid `Vary` header string as `field` * Add `vary.append` for low-level string manipulation * Move to `jshttp` orgainzation 0.1.0 / 2014-06-05 ================== * Support array of fields to set 0.0.0 / 2014-06-04 ================== * Initial release vary-1.1.2/LICENSE000066400000000000000000000021061316160705500135040ustar00rootroot00000000000000(The MIT License) Copyright (c) 2014-2017 Douglas Christopher Wilson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. vary-1.1.2/README.md000066400000000000000000000052341316160705500137630ustar00rootroot00000000000000# vary [![NPM Version][npm-image]][npm-url] [![NPM Downloads][downloads-image]][downloads-url] [![Node.js Version][node-version-image]][node-version-url] [![Build Status][travis-image]][travis-url] [![Test Coverage][coveralls-image]][coveralls-url] Manipulate the HTTP Vary header ## Installation This is a [Node.js](https://nodejs.org/en/) module available through the [npm registry](https://www.npmjs.com/). Installation is done using the [`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally): ```sh $ npm install vary ``` ## API ```js var vary = require('vary') ``` ### vary(res, field) Adds the given header `field` to the `Vary` response header of `res`. This can be a string of a single field, a string of a valid `Vary` header, or an array of multiple fields. This will append the header if not already listed, otherwise leaves it listed in the current location. ```js // Append "Origin" to the Vary header of the response vary(res, 'Origin') ``` ### vary.append(header, field) Adds the given header `field` to the `Vary` response header string `header`. This can be a string of a single field, a string of a valid `Vary` header, or an array of multiple fields. This will append the header if not already listed, otherwise leaves it listed in the current location. The new header string is returned. ```js // Get header string appending "Origin" to "Accept, User-Agent" vary.append('Accept, User-Agent', 'Origin') ``` ## Examples ### Updating the Vary header when content is based on it ```js var http = require('http') var vary = require('vary') http.createServer(function onRequest (req, res) { // about to user-agent sniff vary(res, 'User-Agent') var ua = req.headers['user-agent'] || '' var isMobile = /mobi|android|touch|mini/i.test(ua) // serve site, depending on isMobile res.setHeader('Content-Type', 'text/html') res.end('You are (probably) ' + (isMobile ? '' : 'not ') + 'a mobile user') }) ``` ## Testing ```sh $ npm test ``` ## License [MIT](LICENSE) [npm-image]: https://img.shields.io/npm/v/vary.svg [npm-url]: https://npmjs.org/package/vary [node-version-image]: https://img.shields.io/node/v/vary.svg [node-version-url]: https://nodejs.org/en/download [travis-image]: https://img.shields.io/travis/jshttp/vary/master.svg [travis-url]: https://travis-ci.org/jshttp/vary [coveralls-image]: https://img.shields.io/coveralls/jshttp/vary/master.svg [coveralls-url]: https://coveralls.io/r/jshttp/vary [downloads-image]: https://img.shields.io/npm/dm/vary.svg [downloads-url]: https://npmjs.org/package/vary vary-1.1.2/benchmark/000077500000000000000000000000001316160705500144325ustar00rootroot00000000000000vary-1.1.2/benchmark/append.js000066400000000000000000000024011316160705500162340ustar00rootroot00000000000000 /** * Module dependencies. */ var benchmark = require('benchmark') var benchmarks = require('beautify-benchmark') /** * Globals for benchmark.js */ global.vary = require('..') var suite = new benchmark.Suite() suite.add({ name: 'field to *', minSamples: 100, fn: 'var header = vary.append("*", "Accept-Encoding")' }) suite.add({ name: '* to field', minSamples: 100, fn: 'var header = vary.append("Accept-Encoding", "*")' }) suite.add({ name: 'field to empty', minSamples: 100, fn: 'var header = vary.append("", "Accept-Encoding")' }) suite.add({ name: 'fields array to empty', minSamples: 100, fn: 'var header = vary.append("", ["Accept", "Accept-Encoding", "Accept-Language"])' }) suite.add({ name: 'fields string to empty', minSamples: 100, fn: 'var header = vary.append("", "Accept, Accept-Encoding, Accept-Language")' }) suite.add({ name: 'field to fields', minSamples: 100, fn: 'var header = vary.append("Accept, Accept-Encoding, Accept-Language", "X-Foo")' }) suite.on('start', function onCycle (event) { process.stdout.write(' append\n\n') }) suite.on('cycle', function onCycle (event) { benchmarks.add(event.target) }) suite.on('complete', function onComplete () { benchmarks.log() }) suite.run({async: false}) vary-1.1.2/benchmark/index.js000066400000000000000000000013751316160705500161050ustar00rootroot00000000000000var fs = require('fs') var path = require('path') var spawn = require('child_process').spawn var exe = process.argv[0] var cwd = process.cwd() for (var dep in process.versions) { console.log(' %s@%s', dep, process.versions[dep]) } console.log('') runScripts(fs.readdirSync(__dirname)) function runScripts (fileNames) { var fileName = fileNames.shift() if (!fileName) return if (!/\.js$/i.test(fileName)) return runScripts(fileNames) if (fileName.toLowerCase() === 'index.js') return runScripts(fileNames) var fullPath = path.join(__dirname, fileName) console.log('> %s %s', exe, path.relative(cwd, fullPath)) var proc = spawn(exe, [fullPath], { 'stdio': 'inherit' }) proc.on('exit', function () { runScripts(fileNames) }) } vary-1.1.2/index.js000066400000000000000000000055621316160705500141550ustar00rootroot00000000000000/*! * vary * Copyright(c) 2014-2017 Douglas Christopher Wilson * MIT Licensed */ 'use strict' /** * Module exports. */ module.exports = vary module.exports.append = append /** * RegExp to match field-name in RFC 7230 sec 3.2 * * field-name = token * token = 1*tchar * tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" * / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" * / DIGIT / ALPHA * ; any VCHAR, except delimiters */ var FIELD_NAME_REGEXP = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/ /** * Append a field to a vary header. * * @param {String} header * @param {String|Array} field * @return {String} * @public */ 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 if (header === '*') { return header } // enumerate current values var val = header var vals = parse(header.toLowerCase()) // unspecified 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 } /** * Parse a vary header into an array. * * @param {String} header * @return {Array} * @private */ function parse (header) { var end = 0 var list = [] var start = 0 // 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 + 1 break default: end = i + 1 break } } // final token list.push(header.substring(start, end)) return list } /** * Mark that a request is varied on a header field. * * @param {Object} res * @param {String|Array} field * @public */ function vary (res, field) { if (!res || !res.getHeader || !res.setHeader) { // quack quack throw new TypeError('res argument is required') } // get existing header var val = res.getHeader('Vary') || '' var header = Array.isArray(val) ? val.join(', ') : String(val) // set new header if ((val = append(header, field))) { res.setHeader('Vary', val) } } vary-1.1.2/package.json000066400000000000000000000022771316160705500147760ustar00rootroot00000000000000{ "name": "vary", "description": "Manipulate the HTTP Vary header", "version": "1.1.2", "author": "Douglas Christopher Wilson ", "license": "MIT", "keywords": [ "http", "res", "vary" ], "repository": "jshttp/vary", "devDependencies": { "beautify-benchmark": "0.2.4", "benchmark": "2.1.4", "eslint": "3.19.0", "eslint-config-standard": "10.2.1", "eslint-plugin-import": "2.7.0", "eslint-plugin-markdown": "1.0.0-beta.6", "eslint-plugin-node": "5.1.1", "eslint-plugin-promise": "3.5.0", "eslint-plugin-standard": "3.0.1", "istanbul": "0.4.5", "mocha": "2.5.3", "supertest": "1.1.0" }, "files": [ "HISTORY.md", "LICENSE", "README.md", "index.js" ], "engines": { "node": ">= 0.8" }, "scripts": { "bench": "node benchmark/index.js", "lint": "eslint --plugin markdown --ext js,md .", "test": "mocha --reporter spec --bail --check-leaks test/", "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/" } } vary-1.1.2/test/000077500000000000000000000000001316160705500134575ustar00rootroot00000000000000vary-1.1.2/test/.eslintrc000066400000000000000000000000451316160705500153020ustar00rootroot00000000000000{ "env": { "mocha": true } } vary-1.1.2/test/test.js000066400000000000000000000300661316160705500150010ustar00rootroot00000000000000 var assert = require('assert') var http = require('http') var request = require('supertest') var vary = require('..') describe('vary(res, field)', function () { describe('arguments', function () { describe('res', function () { it('should be required', function () { assert.throws(vary.bind(), /res.*required/) }) it('should not allow non-res-like objects', function () { assert.throws(vary.bind(null, {}), /res.*required/) }) }) describe('field', function () { it('should be required', function (done) { request(createServer(callVary())) .get('/') .expect(500, /field.*required/, done) }) it('should accept string', function (done) { request(createServer(callVary('foo'))) .get('/') .expect(200, done) }) it('should accept array of string', function (done) { request(createServer(callVary(['foo', 'bar']))) .get('/') .expect(200, done) }) it('should accept string that is Vary header', function (done) { request(createServer(callVary('foo, bar'))) .get('/') .expect(200, done) }) it('should not allow separator ":"', function (done) { request(createServer(callVary('invalid:header'))) .get('/') .expect(500, /field.*contains.*invalid/, done) }) it('should not allow separator " "', function (done) { request(createServer(callVary('invalid header'))) .get('/') .expect(500, /field.*contains.*invalid/, done) }) }) }) describe('when no Vary', function () { it('should set value', function (done) { request(createServer(callVary('Origin'))) .get('/') .expect('Vary', 'Origin') .expect(200, done) }) it('should set value with multiple calls', function (done) { request(createServer(callVary(['Origin', 'User-Agent']))) .get('/') .expect('Vary', 'Origin, User-Agent') .expect(200, done) }) it('should preserve case', function (done) { request(createServer(callVary(['ORIGIN', 'user-agent', 'AccepT']))) .get('/') .expect('Vary', 'ORIGIN, user-agent, AccepT') .expect(200, done) }) it('should not set Vary on empty array', function (done) { request(createServer(callVary([]))) .get('/') .expect(shouldNotHaveHeader('Vary')) .expect(200, done) }) }) describe('when existing Vary', function () { it('should set value', function (done) { request(createServer(alterVary('Accept', 'Origin'))) .get('/') .expect('Vary', 'Accept, Origin') .expect(200, done) }) it('should set value with multiple calls', function (done) { var server = createServer(function (req, res) { res.setHeader('Vary', 'Accept') vary(res, 'Origin') vary(res, 'User-Agent') }) request(server) .get('/') .expect('Vary', 'Accept, Origin, User-Agent') .expect(200, done) }) it('should not duplicate existing value', function (done) { request(createServer(alterVary('Accept', 'Accept'))) .get('/') .expect('Vary', 'Accept') .expect(200, done) }) it('should compare case-insensitive', function (done) { request(createServer(alterVary('Accept', 'accEPT'))) .get('/') .expect('Vary', 'Accept') .expect(200, done) }) it('should preserve case', function (done) { request(createServer(alterVary('AccepT', ['accEPT', 'ORIGIN']))) .get('/') .expect('Vary', 'AccepT, ORIGIN') .expect(200, done) }) }) describe('when existing Vary as array', function () { it('should set value', function (done) { request(createServer(alterVary(['Accept', 'Accept-Encoding'], 'Origin'))) .get('/') .expect('Vary', 'Accept, Accept-Encoding, Origin') .expect(200, done) }) it('should not duplicate existing value', function (done) { request(createServer(alterVary(['Accept', 'Accept-Encoding'], ['accept', 'origin']))) .get('/') .expect('Vary', 'Accept, Accept-Encoding, origin') .expect(200, done) }) }) describe('when Vary: *', function () { it('should set value', function (done) { request(createServer(callVary('*'))) .get('/') .expect('Vary', '*') .expect(200, done) }) it('should act as if all values alread set', function (done) { request(createServer(alterVary('*', ['Origin', 'User-Agent']))) .get('/') .expect('Vary', '*') .expect(200, done) }) it('should erradicate existing values', function (done) { request(createServer(alterVary('Accept, Accept-Encoding', '*'))) .get('/') .expect('Vary', '*') .expect(200, done) }) it('should update bad existing header', function (done) { request(createServer(alterVary('Accept, Accept-Encoding, *', 'Origin'))) .get('/') .expect('Vary', '*') .expect(200, done) }) }) describe('when field is string', function () { it('should set value', function (done) { request(createServer(callVary('Accept'))) .get('/') .expect('Vary', 'Accept') .expect(200, done) }) it('should set value when vary header', function (done) { request(createServer(callVary('Accept, Accept-Encoding'))) .get('/') .expect('Vary', 'Accept, Accept-Encoding') .expect(200, done) }) it('should acept LWS', function (done) { request(createServer(callVary(' Accept , Origin '))) .get('/') .expect('Vary', 'Accept, Origin') .expect(200, done) }) it('should handle contained *', function (done) { request(createServer(callVary('Accept,*'))) .get('/') .expect('Vary', '*') .expect(200, done) }) }) describe('when field is array', function () { it('should set value', function (done) { request(createServer(callVary(['Accept', 'Accept-Language']))) .get('/') .expect('Vary', 'Accept, Accept-Language') .expect(200, done) }) it('should ignore double-entries', function (done) { request(createServer(callVary(['Accept', 'Accept']))) .get('/') .expect('Vary', 'Accept') .expect(200, done) }) it('should be case-insensitive', function (done) { request(createServer(callVary(['Accept', 'ACCEPT']))) .get('/') .expect('Vary', 'Accept') .expect(200, done) }) it('should handle contained *', function (done) { request(createServer(callVary(['Origin', 'User-Agent', '*', 'Accept']))) .get('/') .expect('Vary', '*') .expect(200, done) }) it('should handle existing values', function (done) { request(createServer(alterVary('Accept, Accept-Encoding', ['origin', 'accept', 'accept-charset']))) .get('/') .expect('Vary', 'Accept, Accept-Encoding, origin, accept-charset') .expect(200, done) }) }) }) describe('vary.append(header, field)', function () { describe('arguments', function () { describe('header', function () { it('should be required', function () { assert.throws(vary.append.bind(), /header.*required/) }) it('should be a string', function () { assert.throws(vary.append.bind(null, 42), /header.*required/) }) }) describe('field', function () { it('should be required', function () { assert.throws(vary.append.bind(null, ''), /field.*required/) }) it('should accept string', function () { assert.doesNotThrow(vary.append.bind(null, '', 'foo')) }) it('should accept string that is Vary header', function () { assert.doesNotThrow(vary.append.bind(null, '', 'foo, bar')) }) it('should accept array of string', function () { assert.doesNotThrow(vary.append.bind(null, '', ['foo', 'bar'])) }) it('should not allow separator ":"', function () { assert.throws(vary.append.bind(null, '', 'invalid:header'), /field.*contains.*invalid/) }) it('should not allow separator " "', function () { assert.throws(vary.append.bind(null, '', 'invalid header'), /field.*contains.*invalid/) }) it('should not allow non-token characters', function () { assert.throws(vary.append.bind(null, '', 'invalid\nheader'), /field.*contains.*invalid/) assert.throws(vary.append.bind(null, '', 'invalid\u0080header'), /field.*contains.*invalid/) }) }) }) describe('when header empty', function () { it('should set value', function () { assert.equal(vary.append('', 'Origin'), 'Origin') }) it('should set value with array', function () { assert.equal(vary.append('', ['Origin', 'User-Agent']), 'Origin, User-Agent') }) it('should preserve case', function () { assert.equal(vary.append('', ['ORIGIN', 'user-agent', 'AccepT']), 'ORIGIN, user-agent, AccepT') }) }) describe('when header has values', function () { it('should set value', function () { assert.equal(vary.append('Accept', 'Origin'), 'Accept, Origin') }) it('should set value with array', function () { assert.equal(vary.append('Accept', ['Origin', 'User-Agent']), 'Accept, Origin, User-Agent') }) it('should not duplicate existing value', function () { assert.equal(vary.append('Accept', 'Accept'), 'Accept') }) it('should compare case-insensitive', function () { assert.equal(vary.append('Accept', 'accEPT'), 'Accept') }) it('should preserve case', function () { assert.equal(vary.append('Accept', 'AccepT'), 'Accept') }) }) describe('when *', function () { it('should set value', function () { assert.equal(vary.append('', '*'), '*') }) it('should act as if all values already set', function () { assert.equal(vary.append('*', 'Origin'), '*') }) it('should erradicate existing values', function () { assert.equal(vary.append('Accept, Accept-Encoding', '*'), '*') }) it('should update bad existing header', function () { assert.equal(vary.append('Accept, Accept-Encoding, *', 'Origin'), '*') }) }) describe('when field is string', function () { it('should set value', function () { assert.equal(vary.append('', 'Accept'), 'Accept') }) it('should set value when vary header', function () { assert.equal(vary.append('', 'Accept, Accept-Encoding'), 'Accept, Accept-Encoding') }) it('should acept LWS', function () { assert.equal(vary.append('', ' Accept , Origin '), 'Accept, Origin') }) it('should handle contained *', function () { assert.equal(vary.append('', 'Accept,*'), '*') }) }) describe('when field is array', function () { it('should set value', function () { assert.equal(vary.append('', ['Accept', 'Accept-Language']), 'Accept, Accept-Language') }) it('should ignore double-entries', function () { assert.equal(vary.append('', ['Accept', 'Accept']), 'Accept') }) it('should be case-insensitive', function () { assert.equal(vary.append('', ['Accept', 'ACCEPT']), 'Accept') }) it('should handle contained *', function () { assert.equal(vary.append('', ['Origin', 'User-Agent', '*', 'Accept']), '*') }) it('should handle existing values', function () { assert.equal(vary.append('Accept, Accept-Encoding', ['origin', 'accept', 'accept-charset']), 'Accept, Accept-Encoding, origin, accept-charset') }) }) }) function alterVary (header, field) { return function call (req, res) { res.setHeader('Vary', header) vary(res, field) } } function callVary (field) { return function call (req, res) { vary(res, field) } } function createServer (fn) { return http.createServer(function onRequest (req, res) { try { fn(req, res) res.statusCode = 200 } catch (err) { res.statusCode = 500 res.write(err.message) } finally { res.end() } }) } function shouldNotHaveHeader (header) { return function (res) { assert.ok(!(header.toLowerCase() in res.headers), 'should not have header ' + header) } }