[Pkg-javascript-commits] [node-serve-favicon] 01/01: Imported Upstream version 2.0.1
Leo Iannacone
l3on-guest at moszumanska.debian.org
Sat Jun 14 17:15:41 UTC 2014
This is an automated email from the git hooks/post-receive script.
l3on-guest pushed a commit to branch master
in repository node-serve-favicon.
commit 3c2db101f84140ec1fc1e0ee4f437c613628d432
Author: Leo Iannacone <l3on at ubuntu.com>
Date: Sat Jun 14 19:02:22 2014 +0200
Imported Upstream version 2.0.1
---
.npmignore | 3 +
.travis.yml | 11 ++
History.md | 31 +++++
LICENSE | 25 ++++
README.md | 86 +++++++++++++
index.js | 109 +++++++++++++++++
package.json | 30 +++++
test/fixtures/favicon.ico | Bin 0 -> 1406 bytes
test/test.js | 305 ++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 600 insertions(+)
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..cd39b77
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,3 @@
+coverage/
+test/
+.travis.yml
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..1ff243c
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,11 @@
+language: 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: "npm install coveralls at 2.10.0 && cat ./coverage/lcov.info | coveralls"
diff --git a/History.md b/History.md
new file mode 100644
index 0000000..a1bdd39
--- /dev/null
+++ b/History.md
@@ -0,0 +1,31 @@
+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
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..813ed6c
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,25 @@
+(The MIT License)
+
+Copyright (c) 2010 Sencha Inc.
+Copyright (c) 2011 LearnBoost
+Copyright (c) 2011 TJ Holowaychuk
+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.
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b7c6541
--- /dev/null
+++ b/README.md
@@ -0,0 +1,86 @@
+# serve-favicon
+
+[![NPM version](https://badge.fury.io/js/serve-favicon.svg)](http://badge.fury.io/js/serve-favicon)
+[![Build Status](https://travis-ci.org/expressjs/serve-favicon.svg?branch=master)](https://travis-ci.org/expressjs/serve-favicon)
+[![Coverage Status](https://img.shields.io/coveralls/expressjs/serve-favicon.svg?branch=master)](https://coveralls.io/r/expressjs/serve-favicon)
+
+Node.js middleware for serving a favicon.
+
+## Install
+
+ 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
+
+ - `maxAge` - cache-control max-age directive in `ms`, defaulting to 1 day.
+
+## 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 3.x/4.x
+
+```javascript
+var express = require('express');
+var favicon = require('serve-favicon');
+
+var app = express();
+app.use(favicon(__dirname + '/public/favicon.ico'));
+
+// Add your routes here, etc.
+
+app.listen(3000);
+```
+
+### connect
+
+```javascript
+var connect = require('connect');
+var favicon = require('serve-favicon');
+
+var app = connect();
+app.use(favicon(__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 _favicon = favicon(__dirname + '/public/favicon.ico');
+
+var server = http.createServer(function onRequest(req, res) {
+ _favicon(req, res, function onNext(err) {
+ if (err) {
+ res.statusCode = 500;
+ res.end();
+ return;
+ }
+
+ // continue to process the request here, etc.
+
+ res.statusCode = 404;
+ res.end('oops');
+ });
+});
+
+server.listen(3000);
+```
+
+## License
+
+[MIT](LICENSE)
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..4c44f0c
--- /dev/null
+++ b/index.js
@@ -0,0 +1,109 @@
+/*!
+ * serve-favicon
+ * Copyright(c) 2010 Sencha Inc.
+ * Copyright(c) 2011 TJ Holowaychuk
+ * Copyright(c) 2014 Douglas Christopher Wilson
+ * MIT Licensed
+ */
+
+/**
+ * Module dependencies.
+ */
+
+var crypto = require('crypto');
+var fresh = require('fresh');
+var fs = require('fs');
+var path = require('path');
+var resolve = path.resolve;
+
+/**
+ * Serves the favicon located by the given `path`.
+ *
+ * @param {String|Buffer} path
+ * @param {Object} options
+ * @return {Function} middleware
+ * @api public
+ */
+
+module.exports = function favicon(path, options){
+ options = options || {};
+
+ var buf;
+ var icon; // favicon cache
+ var maxAge = options.maxAge == null
+ ? 86400000
+ : Math.min(Math.max(0, options.maxAge), 31556926000);
+ var stat;
+
+ if (!path) throw new TypeError('path to favicon.ico is required');
+
+ if (Buffer.isBuffer(path)) {
+ buf = new Buffer(path.length);
+ path.copy(buf);
+
+ icon = createIcon(buf, maxAge);
+ } else if (typeof path === 'string') {
+ path = resolve(path);
+ stat = fs.statSync(path);
+ if (stat.isDirectory()) throw createIsDirError(path);
+ } else {
+ throw new TypeError('path to favicon.ico must be string or buffer');
+ }
+
+ return function favicon(req, res, next){
+ if ('/favicon.ico' !== req.url) return next();
+
+ if ('GET' !== req.method && 'HEAD' !== req.method) {
+ var status = 'OPTIONS' === req.method ? 200 : 405;
+ res.writeHead(status, {'Allow': 'GET, HEAD, OPTIONS'});
+ res.end();
+ return;
+ }
+
+ if (icon) return send(req, res, icon);
+
+ fs.readFile(path, function(err, buf){
+ if (err) return next(err);
+ icon = createIcon(buf, maxAge);
+ send(req, res, icon);
+ });
+ };
+};
+
+function createIcon(buf, maxAge) {
+ return {
+ body: buf,
+ headers: {
+ 'Content-Type': 'image/x-icon',
+ 'Content-Length': buf.length,
+ 'Cache-Control': 'public, max-age=' + ~~(maxAge / 1000),
+ 'etag': etag(buf)
+ }
+ };
+}
+
+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;
+}
+
+function etag(buf){
+ var hash = crypto
+ .createHash('md5')
+ .update(buf)
+ .digest('base64');
+ return '"' + hash + '"';
+}
+
+function send(req, res, icon){
+ var _fresh = fresh(req.headers, icon.headers);
+ var buf = _fresh ? '' : icon.body;
+ var status = _fresh ? 304 : 200;
+
+ res.writeHead(status, icon.headers);
+ res.end(buf);
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..10119b2
--- /dev/null
+++ b/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "serve-favicon",
+ "description": "favicon serving middleware with caching",
+ "version": "2.0.1",
+ "author": "Douglas Christopher Wilson <doug at somethingdoug.com>",
+ "license": "MIT",
+ "keywords": [
+ "favicon",
+ "middleware"
+ ],
+ "repository": "expressjs/serve-favicon",
+ "dependencies": {
+ "fresh": "0.2.2"
+ },
+ "devDependencies": {
+ "istanbul": "0.2.10",
+ "mocha": "~1.20.1",
+ "proxyquire": "~1.0.1",
+ "should": "~4.0.1",
+ "supertest": "~0.13.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ },
+ "scripts": {
+ "test": "mocha --reporter dot test/",
+ "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot test/",
+ "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec test/"
+ }
+}
diff --git a/test/fixtures/favicon.ico b/test/fixtures/favicon.ico
new file mode 100644
index 0000000..895fc96
Binary files /dev/null and b/test/fixtures/favicon.ico differ
diff --git a/test/test.js b/test/test.js
new file mode 100644
index 0000000..bd3bb79
--- /dev/null
+++ b/test/test.js
@@ -0,0 +1,305 @@
+
+var fs = require('fs');
+var http = require('http');
+var path = require('path');
+var proxyquire = require('proxyquire');
+var request = require('supertest');
+var resolve = path.resolve;
+var should = require('should');
+
+var favicon = proxyquire('..', {
+ fs: {readFile: readFile}
+});
+
+var fixtures = __dirname + '/fixtures';
+
+describe('favicon()', function(){
+ describe('arguments', function(){
+ describe('path', function(){
+ it('should be required', function(){
+ favicon.bind().should.throw(/path.*required/);
+ })
+
+ it('should accept file path', function(){
+ favicon.bind(null, path.join(fixtures, 'favicon.ico')).should.not.throw();
+ })
+
+ it('should accept buffer', function(){
+ favicon.bind(null, new Buffer(20)).should.not.throw();
+ })
+
+ it('should exist', function(){
+ favicon.bind(null, path.join(fixtures, 'nothing')).should.throw(/ENOENT.*nothing/);
+ })
+
+ it('should not be dir', function(){
+ favicon.bind(null, fixtures).should.throw(/EISDIR.*fixtures/);
+ })
+
+ it('should not be number', function(){
+ favicon.bind(null, 12).should.throw(/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 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 31556926', function(done){
+ var server = createServer(null, {maxAge: 900000000000});
+ request(server)
+ .get('/favicon.ico')
+ .expect('Cache-Control', 'public, max-age=31556926')
+ .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=31556926')
+ .expect(200, done);
+ })
+ })
+ })
+
+ describe('requests', function(){
+ var server;
+ before(function () {
+ server = createServer();
+ });
+
+ it('should serve icon', function(done){
+ request(server)
+ .get('/favicon.ico')
+ .expect('Content-Type', 'image/x-icon')
+ .expect(200, done);
+ });
+
+ it('should include cache-control', function(done){
+ request(server)
+ .get('/favicon.ico')
+ .expect('Cache-Control', /public/)
+ .expect(200, done);
+ });
+
+ it('should include etag', function(done){
+ request(server)
+ .get('/favicon.ico')
+ .expect('ETag', /"[^"]+"/)
+ .expect(200, done);
+ });
+
+ it('should deny POST', function(done){
+ request(server)
+ .post('/favicon.ico')
+ .expect('Allow', 'GET, HEAD, OPTIONS')
+ .expect(405, done);
+ });
+
+ it('should understand OPTIONS', function(done){
+ request(server)
+ .options('/favicon.ico')
+ .expect('Allow', 'GET, HEAD, OPTIONS')
+ .expect(200, done);
+ });
+
+ it('should understand If-None-Match', function(done){
+ request(server.listen())
+ .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 ignore non-favicon requests', function(done){
+ request(server)
+ .get('/')
+ .expect(404, 'oops', done);
+ });
+ });
+
+ describe('icon', function(){
+ describe('file', function(){
+ var icon = path.join(fixtures, 'favicon.ico');
+ var server;
+ beforeEach(function () {
+ readFile.resetReadCount();
+ server = createServer(icon);
+ });
+
+ it('should be read on first request', function(done){
+ request(server)
+ .get('/favicon.ico')
+ .expect(200, function(err){
+ if (err) return done(err);
+ readFile.getReadCount(icon).should.equal(1);
+ done();
+ });
+ });
+
+ it('should cache for second request', function(done){
+ request(server.listen())
+ .get('/favicon.ico')
+ .expect(200, function(err){
+ if (err) return done(err);
+ request(server)
+ .get('/favicon.ico')
+ .expect(200, function(err){
+ if (err) return done(err);
+ readFile.getReadCount(icon).should.equal(1);
+ done();
+ });
+ });
+ });
+ });
+
+ describe('file error', function(){
+ var icon = path.join(fixtures, 'favicon.ico');
+ var server;
+ beforeEach(function () {
+ readFile.resetReadCount();
+ server = createServer(icon);
+ });
+
+ it('should next() file read errors', function(done){
+ readFile.setNextError(new Error('oh no'));
+ request(server)
+ .get('/favicon.ico')
+ .expect(500, 'oh no', function(err){
+ if (err) return done(err);
+ readFile.getReadCount(icon).should.equal(1);
+ done();
+ });
+ });
+
+ it('should retry reading file after error', function(done){
+ readFile.setNextError(new Error('oh no'));
+ request(server.listen())
+ .get('/favicon.ico')
+ .expect(500, 'oh no', function(err){
+ if (err) return done(err);
+ request(server)
+ .get('/favicon.ico')
+ .expect(200, function(err){
+ if (err) return done(err);
+ readFile.getReadCount(icon).should.equal(2);
+ done();
+ });
+ });
+ });
+ });
+
+ describe('buffer', function(){
+ var buf = new Buffer(20);
+ var server;
+ before(function () {
+ buf.fill(35);
+ server = createServer(buf);
+ });
+
+ it('should be served from buffer', function(done){
+ request(server)
+ .get('/favicon.ico')
+ .expect('Content-Length', buf.length)
+ .expect(200, done);
+ });
+
+ it('should be copied', function(done){
+ buf.fill(46);
+ request(server)
+ .get('/favicon.ico')
+ .expect('Content-Length', buf.length)
+ .expect(200, '####################', done);
+ });
+ });
+ });
+});
+
+function createServer(icon, opts) {
+ icon = icon || path.join(fixtures, 'favicon.ico');
+
+ var _favicon = favicon(icon, 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;
+}
+
+function readFile(path, options, callback) {
+ var key = resolve(path);
+
+ readFile._readCount[key] = (readFile._readCount[key] || 0) + 1;
+
+ if (readFile._nextError) {
+ var cb = callback || options;
+ var err = readFile._nextError;
+
+ readFile._nextError = null;
+
+ return cb (err);
+ }
+
+ return fs.readFile.apply(this, arguments);
+}
+
+readFile._nextError = null;
+readFile._readCount = Object.create(null);
+
+readFile.getReadCount = function getReadCount(path) {
+ var key = resolve(path);
+ return readFile._readCount[key] || 0;
+};
+
+readFile.resetReadCount = function resetReadCount() {
+ readFile._readCount = Object.create(null);
+};
+
+readFile.setNextError = function setNextError(err) {
+ readFile._nextError = err;
+};
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-serve-favicon.git
More information about the Pkg-javascript-commits
mailing list