[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