[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