[Pkg-javascript-commits] [node-finalhandler] 01/02: Imported Upstream version 0.3.0

Leo Iannacone l3on-guest at moszumanska.debian.org
Tue Oct 14 09:13:56 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-finalhandler.

commit 76f0feddcb1bbb853389a14f9b35c04f92c8dd2a
Author: Leo Iannacone <l3on at ubuntu.com>
Date:   Tue Oct 14 10:40:03 2014 +0200

    Imported Upstream version 0.3.0
---
 .travis.yml  |  11 +++
 HISTORY.md   |  39 +++++++++
 LICENSE      |  22 +++++
 README.md    | 133 +++++++++++++++++++++++++++++
 index.js     | 171 +++++++++++++++++++++++++++++++++++++
 package.json |  33 +++++++
 test/test.js | 275 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 684 insertions(+)

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..372d6d8
--- /dev/null
+++ b/HISTORY.md
@@ -0,0 +1,39 @@
+0.3.0 / 2014-09-17
+==================
+
+  * Terminate in progress response only on error
+  * Use `on-finished` to determine request status
+
+0.2.0 / 2014-09-03
+==================
+
+  * Set `X-Content-Type-Options: nosniff` header
+  * deps: debug@~2.0.0
+
+0.1.0 / 2014-07-16
+==================
+
+  * Respond after request fully read
+    - prevents hung responses and socket hang ups
+  * deps: debug at 1.0.4
+
+0.0.3 / 2014-07-11
+==================
+
+  * deps: debug at 1.0.3
+    - Add support for multiple wildcards in namespaces
+
+0.0.2 / 2014-06-19
+==================
+
+  * Handle invalid status codes
+
+0.0.1 / 2014-06-05
+==================
+
+  * deps: debug at 1.0.2
+
+0.0.0 / 2014-06-05
+==================
+
+  * Extracted from connect/express
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..eda2305
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+(The MIT License)
+
+Copyright (c) 2014 Douglas Christopher Wilson <doug at somethingdoug.com>
+
+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.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2015ac0
--- /dev/null
+++ b/README.md
@@ -0,0 +1,133 @@
+# finalhandler
+
+[![NPM Version][npm-image]][npm-url]
+[![NPM Downloads][downloads-image]][downloads-url]
+[![Node.js Version][node-image]][node-url]
+[![Build Status][travis-image]][travis-url]
+[![Test Coverage][coveralls-image]][coveralls-url]
+
+Node.js function to invoke as the final step to respond to HTTP request.
+
+## Installation
+
+```sh
+$ npm install finalhandler
+```
+
+## API
+
+```js
+var finalhandler = require('finalhandler')
+```
+
+### finalhandler(req, res, [options])
+
+Returns function to be invoked as the final step for the given `req` and `res`.
+This function is to be invoked as `fn(err)`. If `err` is falsy, the handler will
+write out a 404 response to the `res`. If it is truthy, an error response will
+be written out to the `res`, and `res.statusCode` is set from `err.status`.
+
+The final handler will also unpipe anything from `req` when it is invoked.
+
+#### options.env
+
+By default, the environment is determined by `NODE_ENV` variable, but it can be
+overridden by this option.
+
+#### options.onerror
+
+Provide a function to be called with the `err` when it exists. Can be used for
+writing errors to a central location without excessive function generation. Called
+as `onerror(err, req, res)`.
+
+## Examples
+
+### always 404
+
+```js
+var finalhandler = require('finalhandler')
+var http = require('http')
+
+var server = http.createServer(function (req, res) {
+  var done = finalhandler(req, res)
+  done()
+})
+
+server.listen(3000)
+```
+
+### perform simple action
+
+```js
+var finalhandler = require('finalhandler')
+var fs = require('fs')
+var http = require('http')
+
+var server = http.createServer(function (req, res) {
+  var done = finalhandler(req, res)
+
+  fs.readFile('index.html', function (err, buf) {
+    if (err) return done(err)
+    res.setHeader('Content-Type', 'text/html')
+    res.end(buf)
+  })
+})
+
+server.listen(3000)
+```
+
+### use with middleware-style functions
+
+```js
+var finalhandler = require('finalhandler')
+var http = require('http')
+var serveStatic = require('serve-static')
+
+var serve = serveStatic('public')
+
+var server = http.createServer(function (req, res) {
+  var done = finalhandler(req, res)
+  serve(req, res, done)
+})
+
+server.listen(3000)
+```
+
+### keep log of all errors
+
+```js
+var finalhandler = require('finalhandler')
+var fs = require('fs')
+var http = require('http')
+
+var server = http.createServer(function (req, res) {
+  var done = finalhandler(req, res, {onerror: logerror})
+
+  fs.readFile('index.html', function (err, buf) {
+    if (err) return done(err)
+    res.setHeader('Content-Type', 'text/html')
+    res.end(buf)
+  })
+})
+
+server.listen(3000)
+
+function logerror(err) {
+  console.error(err.stack || err.toString())
+}
+```
+
+## License
+
+[MIT](LICENSE)
+
+[npm-image]: https://img.shields.io/npm/v/finalhandler.svg?style=flat
+[npm-url]: https://npmjs.org/package/finalhandler
+[node-image]: https://img.shields.io/node/v/finalhandler.svg?style=flat
+[node-url]: http://nodejs.org/download/
+[travis-image]: https://img.shields.io/travis/pillarjs/finalhandler.svg?style=flat
+[travis-url]: https://travis-ci.org/pillarjs/finalhandler
+[coveralls-image]: https://img.shields.io/coveralls/pillarjs/finalhandler.svg?style=flat
+[coveralls-url]: https://coveralls.io/r/pillarjs/finalhandler?branch=master
+[downloads-image]: https://img.shields.io/npm/dm/finalhandler.svg?style=flat
+[downloads-url]: https://npmjs.org/package/finalhandler
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..bb2bb58
--- /dev/null
+++ b/index.js
@@ -0,0 +1,171 @@
+/*!
+ * finalhandler
+ * Copyright(c) 2014 Douglas Christopher Wilson
+ * MIT Licensed
+ */
+
+/**
+ * Module dependencies.
+ */
+
+var debug = require('debug')('finalhandler')
+var escapeHtml = require('escape-html')
+var http = require('http')
+var onFinished = require('on-finished')
+
+/**
+ * Variables.
+ */
+
+/* istanbul ignore next */
+var defer = typeof setImmediate === 'function'
+  ? setImmediate
+  : function(fn){ process.nextTick(fn.bind.apply(fn, arguments)) }
+var isFinished = onFinished.isFinished
+
+/**
+ * Module exports.
+ */
+
+module.exports = finalhandler
+
+/**
+ * Final handler:
+ *
+ * @param {Request} req
+ * @param {Response} res
+ * @param {Object} [options]
+ * @return {Function}
+ * @api public
+ */
+
+function finalhandler(req, res, options) {
+  options = options || {}
+
+  // get environment
+  var env = options.env || process.env.NODE_ENV || 'development'
+
+  // get error callback
+  var onerror = options.onerror
+
+  return function (err) {
+    var msg
+
+    // ignore 404 on in-flight response
+    if (!err && res._header) {
+      debug('cannot 404 after headers sent')
+      return
+    }
+
+    // unhandled error
+    if (err) {
+      // default status code to 500
+      if (!res.statusCode || res.statusCode < 400) {
+        res.statusCode = 500
+      }
+
+      // respect err.status
+      if (err.status) {
+        res.statusCode = err.status
+      }
+
+      // production gets a basic error message
+      var msg = env === 'production'
+        ? http.STATUS_CODES[res.statusCode]
+        : err.stack || err.toString()
+      msg = escapeHtml(msg)
+        .replace(/\n/g, '<br>')
+        .replace(/  /g, '  ') + '\n'
+    } else {
+      res.statusCode = 404
+      msg = 'Cannot ' + escapeHtml(req.method) + ' ' + escapeHtml(req.originalUrl || req.url) + '\n'
+    }
+
+    debug('default %s', res.statusCode)
+
+    // schedule onerror callback
+    if (err && onerror) {
+      defer(onerror, err, req, res)
+    }
+
+    // cannot actually respond
+    if (res._header) {
+      return req.socket.destroy()
+    }
+
+    send(req, res, res.statusCode, msg)
+  }
+}
+
+/**
+ * Send response.
+ *
+ * @param {IncomingMessage} req
+ * @param {OutgoingMessage} res
+ * @param {number} status
+ * @param {string} body
+ * @api private
+ */
+
+function send(req, res, status, body) {
+  function write() {
+    res.statusCode = status
+
+    // security header for content sniffing
+    res.setHeader('X-Content-Type-Options', 'nosniff')
+
+    // standard headers
+    res.setHeader('Content-Type', 'text/html; charset=utf-8')
+    res.setHeader('Content-Length', Buffer.byteLength(body, 'utf8'))
+
+    if (req.method === 'HEAD') {
+      res.end()
+      return
+    }
+
+    res.end(body, 'utf8')
+  }
+
+  if (isFinished(req)) {
+    write()
+    return
+  }
+
+  // unpipe everything from the request
+  unpipe(req)
+
+  // flush the request
+  onFinished(req, write)
+  req.resume()
+}
+
+/**
+ * Unpipe everything from a stream.
+ *
+ * @param {Object} stream
+ * @api private
+ */
+
+/* istanbul ignore next: implementation differs between versions */
+function unpipe(stream) {
+  if (typeof stream.unpipe === 'function') {
+    // new-style
+    stream.unpipe()
+    return
+  }
+
+  // Node.js 0.8 hack
+  var listener
+  var listeners = stream.listeners('close')
+
+  for (var i = 0; i < listeners.length; i++) {
+    listener = listeners[i]
+
+    if (listener.name !== 'cleanup' && listener.name !== 'onclose') {
+      continue
+    }
+
+    // invoke the listener
+    listener.call(stream)
+  }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..12b87d9
--- /dev/null
+++ b/package.json
@@ -0,0 +1,33 @@
+{
+  "name": "finalhandler",
+  "description": "Node.js final http responder",
+  "version": "0.3.0",
+  "author": "Douglas Christopher Wilson <doug at somethingdoug.com>",
+  "license": "MIT",
+  "repository": "pillarjs/finalhandler",
+  "dependencies": {
+    "debug": "~2.0.0",
+    "escape-html": "1.0.1",
+    "on-finished": "~2.1.0"
+  },
+  "devDependencies": {
+    "istanbul": "0.3.2",
+    "mocha": "~1.21.4",
+    "readable-stream": "~1.0.27",
+    "should": "~4.0.1",
+    "supertest": "~0.13.0"
+  },
+  "files": [
+    "LICENSE",
+    "HISTORY.md",
+    "index.js"
+  ],
+  "engines": {
+    "node": ">= 0.8"
+  },
+  "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/"
+  }
+}
diff --git a/test/test.js b/test/test.js
new file mode 100644
index 0000000..1e6d61d
--- /dev/null
+++ b/test/test.js
@@ -0,0 +1,275 @@
+
+var finalhandler = require('..')
+var http = require('http')
+var request = require('supertest')
+var should = require('should')
+var stream = require('readable-stream')
+var util = require('util')
+
+describe('finalhandler(req, res)', function () {
+  describe('status code', function () {
+    it('should 404 on no error', function (done) {
+      var server = createServer()
+      request(server)
+      .get('/')
+      .expect(404, done)
+    })
+
+    it('should 500 on error', function (done) {
+      var server = createServer(new Error())
+      request(server)
+      .get('/')
+      .expect(500, done)
+    })
+
+    it('should use err.status', function (done) {
+      var err = new Error()
+      err.status = 400
+      var server = createServer(err)
+      request(server)
+      .get('/')
+      .expect(400, done)
+    })
+  })
+
+  describe('404 response', function () {
+    it('include method and path', function (done) {
+      var server = createServer()
+      request(server)
+      .get('/foo')
+      .expect(404, 'Cannot GET /foo\n', done)
+    })
+
+    it('should handle HEAD', function (done) {
+      var server = createServer()
+      request(server)
+      .head('/foo')
+      .expect(404, '', done)
+    })
+
+    it('should include security header', function (done) {
+      var server = createServer()
+      request(server)
+      .get('/foo')
+      .expect('X-Content-Type-Options', 'nosniff')
+      .expect(404, done)
+    })
+
+    it('should not hang/error if there is a request body', function (done) {
+      var buf = new Buffer(1024 * 16)
+      var server = createServer()
+      var test = request(server).post('/foo')
+      buf.fill('.')
+      test.write(buf)
+      test.write(buf)
+      test.write(buf)
+      test.expect(404, done)
+    })
+  })
+
+  describe('error response', function () {
+    it('should include error stack', function (done) {
+      var server = createServer(new Error('boom!'))
+      request(server)
+      .get('/foo')
+      .expect(500, /^Error: boom!<br>    at/, done)
+    })
+
+    it('should handle HEAD', function (done) {
+      var server = createServer()
+      request(server)
+      .head('/foo')
+      .expect(404, '', done)
+    })
+
+    it('should include security header', function (done) {
+      var server = createServer(new Error('boom!'))
+      request(server)
+      .get('/foo')
+      .expect('X-Content-Type-Options', 'nosniff')
+      .expect(500, done)
+    })
+
+    it('should handle non-error-objects', function (done) {
+      var server = createServer('lame string')
+      request(server)
+      .get('/foo')
+      .expect(500, 'lame string\n', done)
+    })
+
+    it('should send staus code name when production', function (done) {
+      var err = new Error('boom!')
+      err.status = 501
+      var server = createServer(err, {env: 'production'})
+      request(server)
+      .get('/foo')
+      .expect(501, 'Not Implemented\n', done)
+    })
+
+    describe('when there is a request body', function () {
+      it('should not hang/error when unread', function (done) {
+        var buf = new Buffer(1024 * 16)
+        var server = createServer(new Error('boom!'))
+        var test = request(server).post('/foo')
+        buf.fill('.')
+        test.write(buf)
+        test.write(buf)
+        test.write(buf)
+        test.expect(500, done)
+      })
+
+      it('should not hang/error when actively piped', function (done) {
+        var buf = new Buffer(1024 * 16)
+        var server = createServer(function (req, res, next) {
+          req.pipe(stream)
+          process.nextTick(function () {
+            next(new Error('boom!'))
+          })
+        })
+        var stream = createSlowWriteStream()
+        var test = request(server).post('/foo')
+        buf.fill('.')
+        test.write(buf)
+        test.write(buf)
+        test.write(buf)
+        test.expect(500, done)
+      })
+
+      it('should not hang/error when read', function (done) {
+        var buf = new Buffer(1024 * 16)
+        var server = createServer(function (req, res, next) {
+          // read off the request
+          req.once('end', function () {
+            next(new Error('boom!'))
+          })
+          req.resume()
+        })
+        var test = request(server).post('/foo')
+        buf.fill('.')
+        test.write(buf)
+        test.write(buf)
+        test.write(buf)
+        test.expect(500, done)
+      })
+    })
+
+    describe('when res.statusCode set', function () {
+      it('should keep when > 400', function (done) {
+        var server = http.createServer(function (req, res) {
+          var done = finalhandler(req, res)
+          res.statusCode = 503
+          done(new Error('oops'))
+        })
+
+        request(server)
+        .get('/foo')
+        .expect(503, done)
+      })
+
+      it('should override with err.status', function (done) {
+        var server = http.createServer(function (req, res) {
+          var done = finalhandler(req, res)
+          var err = new Error('oops')
+          res.statusCode = 503
+          err.status = 414
+          done(err)
+        })
+
+        request(server)
+        .get('/foo')
+        .expect(414, done)
+      })
+    })
+
+    describe('when res.statusCode undefined', function () {
+      it('should set to 500', function (done) {
+        var server = http.createServer(function (req, res) {
+          var done = finalhandler(req, res)
+          res.statusCode = undefined
+          done(new Error('oops'))
+        })
+
+        request(server)
+        .get('/foo')
+        .expect(500, done)
+      })
+    })
+  })
+
+  describe('request started', function () {
+    it('should not respond', function (done) {
+      var server = http.createServer(function (req, res) {
+        var done = finalhandler(req, res)
+        res.statusCode = 301
+        res.write('0')
+        process.nextTick(function () {
+          done()
+          res.end('1')
+        })
+      })
+
+      request(server)
+      .get('/foo')
+      .expect(301, '01', done)
+    })
+
+    it('should terminate on error', function (done) {
+      var server = http.createServer(function (req, res) {
+        var done = finalhandler(req, res)
+        res.statusCode = 301
+        res.write('0')
+        process.nextTick(function () {
+          done(new Error('boom!'))
+          res.end('1')
+        })
+      })
+
+      request(server)
+      .get('/foo')
+      .expect(301, '0', done)
+    })
+  })
+
+  describe('onerror', function () {
+    it('should be invoked when error', function (done) {
+      var err = new Error('boom!')
+      var error
+      var log = function (e) { error = e }
+      var server = createServer(err, {onerror: log})
+
+      request(server)
+      .get('/')
+      .end(function () {
+        should(error).equal(err)
+        done()
+      })
+    })
+  })
+})
+
+function createServer(err, opts) {
+  return http.createServer(function (req, res) {
+    var done = finalhandler(req, res, opts)
+
+    if (typeof err === 'function') {
+      err(req, res, done)
+      return
+    }
+
+    done(err)
+  })
+}
+
+function createSlowWriteStream() {
+  return new SlowWriteStream()
+}
+
+function SlowWriteStream() {
+  stream.Writable.call(this)
+}
+
+util.inherits(SlowWriteStream, stream.Writable)
+
+SlowWriteStream.prototype._write = function _write(chunk, encoding, callback) {
+  setTimeout(callback, 1000)
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-finalhandler.git



More information about the Pkg-javascript-commits mailing list