pax_global_header00006660000000000000000000000064132572220020014505gustar00rootroot0000000000000052 comment=266aba1ba1dadb11ca70264490ed78b68ab9b062 serve-favicon-2.5.0/000077500000000000000000000000001325722200200142605ustar00rootroot00000000000000serve-favicon-2.5.0/.eslintignore000066400000000000000000000000261325722200200167610ustar00rootroot00000000000000coverage node_modules serve-favicon-2.5.0/.eslintrc000066400000000000000000000000341325722200200161010ustar00rootroot00000000000000{ "extends": "standard" } serve-favicon-2.5.0/.gitignore000066400000000000000000000000701325722200200162450ustar00rootroot00000000000000coverage/ node_modules/ npm-debug.log package-lock.json serve-favicon-2.5.0/.travis.yml000066400000000000000000000021721325722200200163730ustar00rootroot00000000000000language: node_js node_js: - "0.8" - "0.10" - "0.12" - "1.8" - "2.5" - "3.3" - "4.8" - "5.12" - "6.13" - "7.10" - "8.10" - "9.8" sudo: false cache: directories: - node_modules before_install: # Skip updating shrinkwrap / lock - "npm config set shrinkwrap false" # SSL certificate not in older Node.js versions - "test $TRAVIS_NODE_VERSION != '0.8' || npm config set strict-ssl false" # 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-ci" - "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" serve-favicon-2.5.0/HISTORY.md000066400000000000000000000100471325722200200157450ustar00rootroot000000000000002.5.0 / 2018-03-29 ================== * Ignore requests without `url` property * deps: ms@2.1.1 - Add `week` - Add `w` 2.4.5 / 2017-09-26 ================== * deps: etag@~1.8.1 - perf: replace regular expression with substring * deps: fresh@0.5.2 - Fix regression matching multiple ETags in `If-None-Match` - perf: improve `If-None-Match` token parsing 2.4.4 / 2017-09-11 ================== * deps: fresh@0.5.1 - Fix handling of modified headers with invalid dates - perf: improve ETag match loop * deps: parseurl@~1.3.2 - perf: reduce overhead for full URLs - perf: unroll the "fast-path" `RegExp` * deps: safe-buffer@5.1.1 2.4.3 / 2017-05-16 ================== * Use `safe-buffer` for improved Buffer API * deps: ms@2.0.0 2.4.2 / 2017-03-24 ================== * deps: ms@1.0.0 2.4.1 / 2017-02-27 ================== * Remove usage of `res._headers` private field * deps: fresh@0.5.0 - Fix incorrect result when `If-None-Match` has both `*` and ETags - Fix weak `ETag` matching to match spec - perf: skip checking modified time if ETag check failed - perf: skip parsing `If-None-Match` when no `ETag` header - perf: use `Date.parse` instead of `new Date` 2.4.0 / 2017-02-19 ================== * deps: etag@~1.8.0 - Use SHA1 instead of MD5 for ETag hashing - Works with FIPS 140-2 OpenSSL configuration * deps: fresh@0.4.0 - Fix false detection of `no-cache` request directive - perf: enable strict mode - perf: hoist regular expressions - perf: remove duplicate conditional - perf: remove unnecessary boolean coercions * perf: simplify initial argument checking 2.3.2 / 2016-11-16 ================== * deps: ms@0.7.2 2.3.1 / 2016-01-23 ================== * deps: parseurl@~1.3.1 - perf: enable strict mode 2.3.0 / 2015-06-13 ================== * Send non-chunked response for `OPTIONS` * deps: etag@~1.7.0 - Always include entity length in ETags for hash length extensions - Generate non-Stats ETags using MD5 only (no longer CRC32) - Remove base64 padding in ETags to shorten * deps: fresh@0.3.0 - Add weak `ETag` matching support * perf: enable strict mode * perf: remove argument reassignment * perf: remove bitwise operations 2.2.1 / 2015-05-14 ================== * deps: etag@~1.6.0 - Improve support for JXcore - Support "fake" stats objects in environments without `fs` * deps: ms@0.7.1 - Prevent extraordinarily long inputs 2.2.0 / 2014-12-18 ================== * Support query string in the URL * deps: etag@~1.5.1 - deps: crc@3.2.1 * deps: ms@0.7.0 - Add `milliseconds` - Add `msecs` - Add `secs` - Add `mins` - Add `hrs` - Add `yrs` 2.1.7 / 2014-11-19 ================== * Avoid errors from enumerables on `Object.prototype` 2.1.6 / 2014-10-16 ================== * deps: etag@~1.5.0 2.1.5 / 2014-09-24 ================== * deps: etag@~1.4.0 2.1.4 / 2014-09-15 ================== * Fix content headers being sent in 304 response * deps: etag@~1.3.1 - Improve ETag generation speed 2.1.3 / 2014-09-07 ================== * deps: fresh@0.2.4 2.1.2 / 2014-09-05 ================== * deps: etag@~1.3.0 - Improve ETag generation speed 2.1.1 / 2014-08-25 ================== * Fix `ms` to be listed as a dependency 2.1.0 / 2014-08-24 ================== * Accept string for `maxAge` (converted by `ms`) * Use `etag` to generate `ETag` header 2.0.1 / 2014-06-05 ================== * Reduce byte size of `ETag` header 2.0.0 / 2014-05-02 ================== * `path` argument is required; there is no default icon. * Accept `Buffer` of icon as first argument. * Non-GET and HEAD requests are denied. * Send valid max-age value * Support conditional requests * Support max-age=0 * Support OPTIONS method * Throw if `path` argument is directory. 1.0.2 / 2014-03-16 ================== * Fixed content of default icon. 1.0.1 / 2014-03-11 ================== * Fixed path to default icon. 1.0.0 / 2014-02-15 ================== * Initial release serve-favicon-2.5.0/LICENSE000066400000000000000000000022451325722200200152700ustar00rootroot00000000000000(The MIT License) Copyright (c) 2010 Sencha Inc. Copyright (c) 2011 LearnBoost Copyright (c) 2011 TJ Holowaychuk 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. serve-favicon-2.5.0/README.md000066400000000000000000000100731325722200200155400ustar00rootroot00000000000000# serve-favicon [![NPM Version][npm-image]][npm-url] [![NPM Downloads][downloads-image]][downloads-url] [![Linux Build][travis-image]][travis-url] [![Windows Build][appveyor-image]][appveyor-url] [![Test Coverage][coveralls-image]][coveralls-url] Node.js middleware for serving a favicon. A favicon is a visual cue that client software, like browsers, use to identify a site. For an example and more information, please visit [the Wikipedia article on favicons](https://en.wikipedia.org/wiki/Favicon). Why use this module? - User agents request `favicon.ico` frequently and indiscriminately, so you may wish to exclude these requests from your logs by using this middleware before your logger middleware. - This module caches the icon in memory to improve performance by skipping disk access. - This module provides an `ETag` based on the contents of the icon, rather than file system properties. - This module will serve with the most compatible `Content-Type`. **Note** This module is exclusively for serving the "default, implicit favicon", which is `GET /favicon.ico`. For additional vendor-specific icons that require HTML markup, additional middleware is required to serve the relevant files, for example [serve-static](https://npmjs.org/package/serve-static). ## Install 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 serve-favicon ``` ## API ### favicon(path, options) Create new middleware to serve a favicon from the given `path` to a favicon file. `path` may also be a `Buffer` of the icon to serve. #### Options Serve favicon accepts these properties in the options object. ##### maxAge The `cache-control` `max-age` directive in `ms`, defaulting to 1 year. This can also be a string accepted by the [ms](https://www.npmjs.org/package/ms#readme) module. ## Examples Typically this middleware will come very early in your stack (maybe even first) to avoid processing any other middleware if we already know the request is for `/favicon.ico`. ### express ```javascript var express = require('express') var favicon = require('serve-favicon') var path = require('path') var app = express() app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))) // Add your routes here, etc. app.listen(3000) ``` ### connect ```javascript var connect = require('connect') var favicon = require('serve-favicon') var path = require('path') var app = connect() app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))) // Add your middleware here, etc. app.listen(3000) ``` ### vanilla http server This middleware can be used anywhere, even outside express/connect. It takes `req`, `res`, and `callback`. ```javascript var http = require('http') var favicon = require('serve-favicon') var finalhandler = require('finalhandler') var path = require('path') var _favicon = favicon(path.join(__dirname, 'public', 'favicon.ico')) var server = http.createServer(function onRequest (req, res) { var done = finalhandler(req, res) _favicon(req, res, function onNext (err) { if (err) return done(err) // continue to process the request here, etc. res.statusCode = 404 res.end('oops') }) }) server.listen(3000) ``` ## License [MIT](LICENSE) [npm-image]: https://img.shields.io/npm/v/serve-favicon.svg [npm-url]: https://npmjs.org/package/serve-favicon [travis-image]: https://img.shields.io/travis/expressjs/serve-favicon/master.svg?label=linux [travis-url]: https://travis-ci.org/expressjs/serve-favicon [appveyor-image]: https://img.shields.io/appveyor/ci/dougwilson/serve-favicon/master.svg?label=windows [appveyor-url]: https://ci.appveyor.com/project/dougwilson/serve-favicon [coveralls-image]: https://img.shields.io/coveralls/expressjs/serve-favicon.svg [coveralls-url]: https://coveralls.io/r/expressjs/serve-favicon?branch=master [downloads-image]: https://img.shields.io/npm/dm/serve-favicon.svg [downloads-url]: https://npmjs.org/package/serve-favicon serve-favicon-2.5.0/appveyor.yml000066400000000000000000000020111325722200200166420ustar00rootroot00000000000000environment: matrix: - nodejs_version: "0.8" - nodejs_version: "0.10" - nodejs_version: "0.12" - nodejs_version: "1.8" - nodejs_version: "2.5" - nodejs_version: "3.3" - nodejs_version: "4.8" - nodejs_version: "5.12" - nodejs_version: "6.13" - nodejs_version: "7.10" - nodejs_version: "8.10" - nodejs_version: "9.8" cache: - node_modules install: - ps: Install-Product node $env:nodejs_version - npm config set shrinkwrap false - if "%nodejs_version%" equ "0.8" npm config set strict-ssl false - if "%nodejs_version%" equ "0.8" npm rm --save-dev istanbul - for /f tokens^=2^ delims^=^" %%m in ('findstr /r /c:"eslint[^ ]" package.json') do call npm rm --save-dev %%m - if exist node_modules npm prune - if exist node_modules npm rebuild - npm install build: off test_script: - node --version - npm --version - set npm_test_command=test - for /f %%l in ('npm -ps ls istanbul') do set npm_test_command=test-ci - npm run %npm_test_command% version: "{build}" serve-favicon-2.5.0/index.js000066400000000000000000000101031325722200200157200ustar00rootroot00000000000000/*! * serve-favicon * Copyright(c) 2010 Sencha Inc. * Copyright(c) 2011 TJ Holowaychuk * Copyright(c) 2014-2017 Douglas Christopher Wilson * MIT Licensed */ 'use strict' /** * Module dependencies. * @private */ var Buffer = require('safe-buffer').Buffer var etag = require('etag') var fresh = require('fresh') var fs = require('fs') var ms = require('ms') var parseUrl = require('parseurl') var path = require('path') var resolve = path.resolve /** * Module exports. * @public */ module.exports = favicon /** * Module variables. * @private */ var ONE_YEAR_MS = 60 * 60 * 24 * 365 * 1000 // 1 year /** * Serves the favicon located by the given `path`. * * @public * @param {String|Buffer} path * @param {Object} [options] * @return {Function} middleware */ function favicon (path, options) { var opts = options || {} var icon // favicon cache var maxAge = calcMaxAge(opts.maxAge) if (!path) { throw new TypeError('path to favicon.ico is required') } if (Buffer.isBuffer(path)) { icon = createIcon(Buffer.from(path), maxAge) } else if (typeof path === 'string') { path = resolveSync(path) } else { throw new TypeError('path to favicon.ico must be string or buffer') } return function favicon (req, res, next) { if (getPathname(req) !== '/favicon.ico') { next() return } if (req.method !== 'GET' && req.method !== 'HEAD') { res.statusCode = req.method === 'OPTIONS' ? 200 : 405 res.setHeader('Allow', 'GET, HEAD, OPTIONS') res.setHeader('Content-Length', '0') res.end() return } if (icon) { send(req, res, icon) return } fs.readFile(path, function (err, buf) { if (err) return next(err) icon = createIcon(buf, maxAge) send(req, res, icon) }) } } /** * Calculate the max-age from a configured value. * * @private * @param {string|number} val * @return {number} */ function calcMaxAge (val) { var num = typeof val === 'string' ? ms(val) : val return num != null ? Math.min(Math.max(0, num), ONE_YEAR_MS) : ONE_YEAR_MS } /** * Create icon data from Buffer and max-age. * * @private * @param {Buffer} buf * @param {number} maxAge * @return {object} */ function createIcon (buf, maxAge) { return { body: buf, headers: { 'Cache-Control': 'public, max-age=' + Math.floor(maxAge / 1000), 'ETag': etag(buf) } } } /** * Create EISDIR error. * * @private * @param {string} path * @return {Error} */ function createIsDirError (path) { var error = new Error('EISDIR, illegal operation on directory \'' + path + '\'') error.code = 'EISDIR' error.errno = 28 error.path = path error.syscall = 'open' return error } /** * Get the request pathname. * * @param {object} req * @return {string} */ function getPathname (req) { try { return parseUrl(req).pathname } catch (e) { return undefined } } /** * Determine if the cached representation is fresh. * * @param {object} req * @param {object} res * @return {boolean} * @private */ function isFresh (req, res) { return fresh(req.headers, { 'etag': res.getHeader('ETag'), 'last-modified': res.getHeader('Last-Modified') }) } /** * Resolve the path to icon. * * @param {string} iconPath * @private */ function resolveSync (iconPath) { var path = resolve(iconPath) var stat = fs.statSync(path) if (stat.isDirectory()) { throw createIsDirError(path) } return path } /** * Send icon data in response to a request. * * @private * @param {IncomingMessage} req * @param {OutgoingMessage} res * @param {object} icon */ function send (req, res, icon) { // Set headers var headers = icon.headers var keys = Object.keys(headers) for (var i = 0; i < keys.length; i++) { var key = keys[i] res.setHeader(key, headers[key]) } // Validate freshness if (isFresh(req, res)) { res.statusCode = 304 res.end() return } // Send icon res.statusCode = 200 res.setHeader('Content-Length', icon.body.length) res.setHeader('Content-Type', 'image/x-icon') res.end(icon.body) } serve-favicon-2.5.0/package.json000066400000000000000000000024301325722200200165450ustar00rootroot00000000000000{ "name": "serve-favicon", "description": "favicon serving middleware with caching", "version": "2.5.0", "author": "Douglas Christopher Wilson ", "license": "MIT", "keywords": [ "express", "favicon", "middleware" ], "repository": "expressjs/serve-favicon", "dependencies": { "etag": "~1.8.1", "fresh": "0.5.2", "ms": "2.1.1", "parseurl": "~1.3.2", "safe-buffer": "5.1.1" }, "devDependencies": { "eslint": "4.19.1", "eslint-config-standard": "11.0.0", "eslint-plugin-import": "2.9.0", "eslint-plugin-markdown": "1.0.0-beta.6", "eslint-plugin-node": "6.0.1", "eslint-plugin-promise": "3.7.0", "eslint-plugin-standard": "3.0.1", "istanbul": "0.4.5", "mocha": "2.5.3", "supertest": "1.1.0", "temp-path": "1.0.0" }, "files": [ "LICENSE", "HISTORY.md", "index.js" ], "engines": { "node": ">= 0.8.0" }, "scripts": { "lint": "eslint --plugin markdown --ext js,md .", "test": "mocha --reporter spec --bail --check-leaks test/", "test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/", "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/" } } serve-favicon-2.5.0/test/000077500000000000000000000000001325722200200152375ustar00rootroot00000000000000serve-favicon-2.5.0/test/.eslintrc000066400000000000000000000000441325722200200170610ustar00rootroot00000000000000{ "env": { "mocha": true } } serve-favicon-2.5.0/test/fixtures/000077500000000000000000000000001325722200200171105ustar00rootroot00000000000000serve-favicon-2.5.0/test/fixtures/favicon.ico000066400000000000000000000025761325722200200212430ustar00rootroot00000000000000h( }@_UC˩~6Gcc<ep›lu4ơu            serve-favicon-2.5.0/test/support/000077500000000000000000000000001325722200200167535ustar00rootroot00000000000000serve-favicon-2.5.0/test/support/tempIcon.js000066400000000000000000000010741325722200200210710ustar00rootroot00000000000000 var crypto = require('crypto') var fs = require('fs') var tempPath = require('temp-path') module.exports = TempIcon function TempIcon () { this.data = crypto.pseudoRandomBytes(100) this.exists = false this.path = tempPath() } TempIcon.prototype.unlinkSync = function unlinkSync () { if (this.exists) { this.exists = false fs.unlinkSync(this.path) } } TempIcon.prototype.writeSync = function writeSync () { if (this.exists) { throw new Error('already written') } else { fs.writeFileSync(this.path, this.data) this.exists = true } } serve-favicon-2.5.0/test/test.js000066400000000000000000000212451325722200200165600ustar00rootroot00000000000000 var assert = require('assert') var Buffer = require('safe-buffer').Buffer var favicon = require('..') var http = require('http') var path = require('path') var request = require('supertest') var TempIcon = require('./support/tempIcon') var FIXTURES_PATH = path.join(__dirname, 'fixtures') var ICON_PATH = path.join(FIXTURES_PATH, 'favicon.ico') describe('favicon()', function () { describe('arguments', function () { describe('path', function () { it('should be required', function () { assert.throws(favicon.bind(), /path.*required/) }) it('should accept file path', function () { assert.doesNotThrow(favicon.bind(null, path.join(FIXTURES_PATH, 'favicon.ico'))) }) it('should accept buffer', function () { assert.doesNotThrow(favicon.bind(null, Buffer.alloc(20))) }) it('should exist', function () { assert.throws(favicon.bind(null, path.join(FIXTURES_PATH, 'nothing')), /ENOENT.*nothing/) }) it('should not be dir', function () { assert.throws(favicon.bind(null, FIXTURES_PATH), /EISDIR.*fixtures/) }) it('should not be number', function () { assert.throws(favicon.bind(null, 12), /path.*must be.*string/) }) }) describe('options.maxAge', function () { it('should be in cache-control', function (done) { var server = createServer(null, {maxAge: 5000}) request(server) .get('/favicon.ico') .expect('Cache-Control', 'public, max-age=5') .expect(200, done) }) it('should have a default', function (done) { var server = createServer() request(server) .get('/favicon.ico') .expect('Cache-Control', /public, max-age=[0-9]+/) .expect(200, done) }) it('should accept 0', function (done) { var server = createServer(null, {maxAge: 0}) request(server) .get('/favicon.ico') .expect('Cache-Control', 'public, max-age=0') .expect(200, done) }) it('should accept string', function (done) { var server = createServer(null, {maxAge: '30d'}) request(server) .get('/favicon.ico') .expect('Cache-Control', 'public, max-age=2592000') .expect(200, done) }) it('should be valid delta-seconds', function (done) { var server = createServer(null, {maxAge: 1234}) request(server) .get('/favicon.ico') .expect('Cache-Control', 'public, max-age=1') .expect(200, done) }) it('should floor at 0', function (done) { var server = createServer(null, {maxAge: -4000}) request(server) .get('/favicon.ico') .expect('Cache-Control', 'public, max-age=0') .expect(200, done) }) it('should ceil at 1 year', function (done) { var server = createServer(null, {maxAge: 900000000000}) request(server) .get('/favicon.ico') .expect('Cache-Control', 'public, max-age=31536000') .expect(200, done) }) it('should accept Inifnity', function (done) { var server = createServer(null, {maxAge: Infinity}) request(server) .get('/favicon.ico') .expect('Cache-Control', 'public, max-age=31536000') .expect(200, done) }) }) }) describe('requests', function () { before(function () { this.server = createServer() }) it('should serve icon', function (done) { request(this.server) .get('/favicon.ico') .expect('Content-Type', 'image/x-icon') .expect(200, done) }) it('should include cache-control', function (done) { request(this.server) .get('/favicon.ico') .expect('Cache-Control', /public/) .expect(200, done) }) it('should include strong etag', function (done) { request(this.server) .get('/favicon.ico') .expect('ETag', /^"[^"]+"$/) .expect(200, done) }) it('should deny POST', function (done) { request(this.server) .post('/favicon.ico') .expect('Allow', 'GET, HEAD, OPTIONS') .expect(405, done) }) it('should understand OPTIONS', function (done) { request(this.server) .options('/favicon.ico') .expect('Allow', 'GET, HEAD, OPTIONS') .expect(200, done) }) it('should 304 when If-None-Match matches', function (done) { var server = this.server request(server) .get('/favicon.ico') .expect(200, function (err, res) { if (err) return done(err) request(server) .get('/favicon.ico') .set('If-None-Match', res.headers.etag) .expect(304, done) }) }) it('should 304 when If-None-Match matches weakly', function (done) { var server = this.server request(server) .get('/favicon.ico') .expect(200, function (err, res) { if (err) return done(err) request(server) .get('/favicon.ico') .set('If-None-Match', 'W/' + res.headers.etag) .expect(304, done) }) }) it('should ignore non-favicon requests', function (done) { request(this.server) .get('/') .expect(404, 'oops', done) }) it('should work with query string', function (done) { request(this.server) .get('/favicon.ico?v=1') .expect('Content-Type', 'image/x-icon') .expect(200, done) }) describe('missing req.url', function () { it('should ignore the request', function (done) { var fn = favicon(ICON_PATH) fn({}, {}, done) }) }) }) describe('icon', function () { describe('file', function () { beforeEach(function () { this.icon = new TempIcon() this.icon.writeSync() }) afterEach(function () { this.icon.unlinkSync() this.icon = undefined }) it('should be read on first request', function (done) { var icon = this.icon var server = createServer(icon.path) request(server) .get('/favicon.ico') .expect(200, icon.data, done) }) it('should cache for second request', function (done) { var icon = this.icon var server = createServer(icon.path) request(server) .get('/favicon.ico') .expect(200, icon.data, function (err) { if (err) return done(err) icon.unlinkSync() request(server) .get('/favicon.ico') .expect(200, icon.data, done) }) }) }) describe('file error', function () { beforeEach(function () { this.icon = new TempIcon() this.icon.writeSync() }) afterEach(function () { this.icon.unlinkSync() this.icon = undefined }) it('should next() file read errors', function (done) { var icon = this.icon var server = createServer(icon.path) icon.unlinkSync() request(server) .get('/favicon.ico') .expect(500, /ENOENT/, done) }) it('should retry reading file after error', function (done) { var icon = this.icon var server = createServer(icon.path) icon.unlinkSync() request(server) .get('/favicon.ico') .expect(500, /ENOENT/, function (err) { if (err) return done(err) icon.writeSync() request(server) .get('/favicon.ico') .expect(200, icon.data, done) }) }) }) describe('buffer', function () { it('should be served from buffer', function (done) { var buffer = Buffer.alloc(20, '#') var server = createServer(buffer) request(server) .get('/favicon.ico') .expect('Content-Length', '20') .expect(200, buffer, done) }) it('should be copied', function (done) { var buffer = Buffer.alloc(20, '#') var server = createServer(buffer) assert.equal(buffer.toString(), '####################') buffer.fill('?') assert.equal(buffer.toString(), '????????????????????') request(server) .get('/favicon.ico') .expect('Content-Length', '20') .expect(200, Buffer.from('####################'), done) }) }) }) }) function createServer (path, opts) { var _path = path || ICON_PATH var _favicon = favicon(_path, opts) var server = http.createServer(function onRequest (req, res) { _favicon(req, res, function onNext (err) { res.statusCode = err ? (err.status || 500) : 404 res.end(err ? err.message : 'oops') }) }) return server }