pax_global_header00006660000000000000000000000064123717567650014535gustar00rootroot0000000000000052 comment=56acecd9fa20888132563b00576625ea02a69a35 vary-1.0.0/000077500000000000000000000000001237175676500125145ustar00rootroot00000000000000vary-1.0.0/.gitignore000066400000000000000000000000301237175676500144750ustar00rootroot00000000000000coverage/ node_modules/ vary-1.0.0/.npmignore000066400000000000000000000000341237175676500145100ustar00rootroot00000000000000coverage/ test/ .travis.yml vary-1.0.0/.travis.yml000066400000000000000000000004371237175676500146310ustar00rootroot00000000000000language: node_js node_js: - "0.8" - "0.10" - "0.11" matrix: allow_failures: - node_js: "0.11" fast_finish: true script: "npm run-script test-travis" after_script: "test $TRAVIS_NODE_VERSION = '0.10' && npm install coveralls@2.11.1 && cat ./coverage/lcov.info | coveralls" vary-1.0.0/History.md000066400000000000000000000004711237175676500145010ustar00rootroot000000000000001.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.0.0/LICENSE000066400000000000000000000021011237175676500135130ustar00rootroot00000000000000(The MIT License) Copyright (c) 2014 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.0.0/README.md000066400000000000000000000031031237175676500137700ustar00rootroot00000000000000# vary [![NPM Version](http://img.shields.io/npm/v/vary.svg?style=flat)](https://www.npmjs.org/package/vary) [![Node.js Version](http://img.shields.io/badge/node.js->=_0.8-blue.svg?style=flat)](http://nodejs.org/download/) [![Build Status](http://img.shields.io/travis/jshttp/vary.svg?style=flat)](https://travis-ci.org/jshttp/vary) [![Coverage Status](https://img.shields.io/coveralls/jshttp/vary.svg?style=flat)](https://coveralls.io/r/jshttp/vary) [![Gittip](http://img.shields.io/gittip/dougwilson.svg?style=flat)](https://www.gittip.com/dougwilson/) Manipulate the HTTP Vary header ## Install ```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') ``` ## Testing ```sh $ npm test ``` ## License [MIT](LICENSE) vary-1.0.0/index.js000066400000000000000000000041071237175676500141630ustar00rootroot00000000000000/*! * vary * Copyright(c) 2014 Douglas Christopher Wilson * MIT Licensed */ /** * Module exports. */ module.exports = vary; module.exports.append = append; /** * Variables. */ var separators = /[\(\)<>@,;:\\"\/\[\]\?=\{\}\u0020\u0009]/; /** * Append a field to a vary header. * * @param {String} header * @param {String|Array} field * @return {String} * @api 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 fields for (var i = 0; i < fields.length; i++) { if (separators.test(fields[i])) { throw new TypeError('field argument contains an invalid header'); } } // existing, unspecified vary if (header === '*') { return header; } // enumerate current values var vals = parse(header.toLowerCase()); // unspecified vary if (fields.indexOf('*') !== -1 || vals.indexOf('*') !== -1) { return '*'; } for (var i = 0; i < fields.length; i++) { field = fields[i].toLowerCase(); // append value (case-preserving) if (vals.indexOf(field) === -1) { vals.push(field); header = header ? header + ', ' + fields[i] : fields[i]; } } return header; } /** * Parse a vary header into an array. * * @param {String} header * @return {Array} * @api private */ function parse(header) { return header.trim().split(/ *, */); } /** * Mark that a request is varied on a header field. * * @param {Object} res * @param {String|Array} field * @api 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 res.setHeader('Vary', append(header, field)); } vary-1.0.0/package.json000066400000000000000000000013541237175676500150050ustar00rootroot00000000000000{ "name": "vary", "description": "Manipulate the HTTP Vary header", "version": "1.0.0", "author": "Douglas Christopher Wilson ", "license": "MIT", "keywords": [ "http", "res", "vary" ], "repository": "jshttp/vary", "devDependencies": { "istanbul": "0.3.0", "mocha": "~1.21.4", "should": "~4.0.4", "supertest": "~0.13.0" }, "engines": { "node": ">= 0.8.0" }, "scripts": { "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.0.0/test/000077500000000000000000000000001237175676500134735ustar00rootroot00000000000000vary-1.0.0/test/test.js000066400000000000000000000270521237175676500150160ustar00rootroot00000000000000 var http = require('http'); var request = require('supertest'); var should = require('should'); var vary = require('..'); describe('vary(res, field)', function () { describe('arguments', function () { describe('res', function () { it('should be required', function () { vary.bind().should.throw(/res.*required/); }); it('should not allow non-res-like objects', function () { vary.bind(null, {}).should.throw(/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); }); }); 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 () { vary.append.bind().should.throw(/header.*required/); }); it('should be a string', function () { vary.append.bind(null, 42).should.throw(/header.*required/); }); }); describe('field', function () { it('should be required', function () { vary.append.bind(null, '').should.throw(/field.*required/); }); it('should accept string', function () { vary.append.bind(null, '', 'foo').should.not.throw(); }); it('should accept string that is Vary header', function () { vary.append.bind(null, '', 'foo, bar').should.not.throw(); }); it('should accept array of string', function () { vary.append.bind(null, '', ['foo', 'bar']).should.not.throw(); }); it('should not allow separator ":"', function () { vary.append.bind(null, '', 'invalid:header').should.throw(/field.*contains.*invalid/); }); it('should not allow separator " "', function () { vary.append.bind(null, '', 'invalid header').should.throw(/field.*contains.*invalid/); }); }); }); describe('when header empty', function () { it('should set value', function () { vary.append('', 'Origin').should.equal('Origin'); }); it('should set value with array', function () { vary.append('', ['Origin', 'User-Agent']).should.equal('Origin, User-Agent'); }); it('should preserve case', function () { vary.append('', ['ORIGIN', 'user-agent', 'AccepT']).should.equal('ORIGIN, user-agent, AccepT'); }); }); describe('when header has values', function () { it('should set value', function () { vary.append('Accept', 'Origin').should.equal('Accept, Origin'); }); it('should set value with array', function () { vary.append('Accept', ['Origin', 'User-Agent']).should.equal('Accept, Origin, User-Agent'); }); it('should not duplicate existing value', function () { vary.append('Accept', 'Accept').should.equal('Accept'); }); it('should compare case-insensitive', function () { vary.append('Accept', 'accEPT').should.equal('Accept'); }); it('should preserve case', function () { vary.append('Accept', 'AccepT').should.equal('Accept'); }); }); describe('when *', function () { it('should set value', function () { vary.append('', '*').should.equal('*'); }); it('should act as if all values already set', function () { vary.append('*', 'Origin').should.equal('*'); }); it('should erradicate existing values', function () { vary.append('Accept, Accept-Encoding', '*').should.equal('*'); }); it('should update bad existing header', function () { vary.append('Accept, Accept-Encoding, *', 'Origin').should.equal('*'); }); }); describe('when field is string', function () { it('should set value', function () { vary.append('', 'Accept').should.equal('Accept'); }); it('should set value when vary header', function () { vary.append('', 'Accept, Accept-Encoding').should.equal('Accept, Accept-Encoding'); }); it('should acept LWS', function () { vary.append('', ' Accept , Origin ').should.equal('Accept, Origin'); }); it('should handle contained *', function () { vary.append('', 'Accept,*').should.equal('*'); }); }); describe('when field is array', function () { it('should set value', function () { vary.append('', ['Accept', 'Accept-Language']).should.equal('Accept, Accept-Language'); }); it('should ignore double-entries', function () { vary.append('', ['Accept', 'Accept']).should.equal('Accept'); }); it('should be case-insensitive', function () { vary.append('', ['Accept', 'ACCEPT']).should.equal('Accept'); }); it('should handle contained *', function () { vary.append('', ['Origin', 'User-Agent', '*', 'Accept']).should.equal('*'); }); it('should handle existing values', function () { vary.append('Accept, Accept-Encoding', ['origin', 'accept', 'accept-charset']).should.equal('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(); } }); }