├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── index.js ├── lib └── connect-domain.js ├── package.json └── test └── domain.test.js /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | coverage.html 3 | *.seed 4 | *.log 5 | *.csv 6 | *.dat 7 | *.out 8 | *.pid 9 | *.gz 10 | 11 | pids 12 | logs 13 | results 14 | 15 | node_modules 16 | npm-debug.log -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .git* 2 | test/ 3 | lib-cov/ 4 | coverage.html 5 | .travis.yml 6 | Makefile -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.9 4 | - 0.8 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2012 Vadim M. Baryshev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TESTS = test/*.test.js 2 | REPORTER = spec 3 | TIMEOUT = 1000 4 | JSCOVERAGE = ./node_modules/jscover/bin/jscover 5 | 6 | install: 7 | @npm install 8 | 9 | test: install 10 | @NODE_ENV=test ./node_modules/mocha/bin/mocha \ 11 | --reporter $(REPORTER) \ 12 | --timeout $(TIMEOUT) \ 13 | $(TESTS) 14 | 15 | test-cov: lib-cov 16 | @CONNECT_DOMAIN_COV=1 $(MAKE) test 17 | @CONNECT_DOMAIN_COV=1 $(MAKE) test REPORTER=html-cov > coverage.html 18 | 19 | lib-cov: install 20 | @rm -rf $@ 21 | @$(JSCOVERAGE) lib $@ 22 | 23 | .PHONY: test-cov test lib-cov 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | Asynchronous error handler for Connect 4 | 5 | # Installation 6 | 7 | npm install connect-domain 8 | 9 | # Usage 10 | 11 | ```js 12 | var 13 | connect = require('connect'), 14 | connectDomain = require('connect-domain'); 15 | 16 | var app = connect() 17 | .use(connectDomain()) 18 | .use(function(req, res){ 19 | if (Math.random() > 0.5) { 20 | throw new Error('Simple error'); 21 | } 22 | setTimeout(function() { 23 | if (Math.random() > 0.5) { 24 | throw new Error('Asynchronous error from timeout'); 25 | } else { 26 | res.end('Hello from Connect!'); 27 | } 28 | }, 1000); 29 | }) 30 | .use(function(err, req, res, next) { 31 | res.end(err.message); 32 | }); 33 | 34 | app.listen(3000); 35 | ``` -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = process.env.CONNECT_DOMAIN_COV ? require('./lib-cov/connect-domain') : require('./lib/connect-domain'); 2 | -------------------------------------------------------------------------------- /lib/connect-domain.js: -------------------------------------------------------------------------------- 1 | var domain = require('domain'); 2 | 3 | module.exports = function () { 4 | return function domainMiddleware(req, res, next) { 5 | var reqDomain = domain.create(); 6 | 7 | res.on('close', function () { 8 | reqDomain.dispose(); 9 | }); 10 | 11 | res.on('finish', function () { 12 | reqDomain.dispose(); 13 | }); 14 | 15 | reqDomain.on('error', function (err) { 16 | // Once a domain is disposed, further errors from the emitters in that set will be ignored. 17 | reqDomain.dispose(); 18 | next(err); 19 | }); 20 | 21 | reqDomain.run(next); 22 | }; 23 | }; 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "connect-domain", 3 | "version" : "0.5.0", 4 | "description" : "Asynchronous error handler for Connect", 5 | "keywords": [ "domain", "error", "connect" ], 6 | "author" : "Vadim M. Baryshev ", 7 | "main" : "index", 8 | "repository": { 9 | "type": "git", 10 | "url": "git://github.com/baryshev/connect-domain.git" 11 | }, 12 | "devDependencies": { 13 | "should": "*", 14 | "connect": "*", 15 | "jscover": "*", 16 | "supertest": "*", 17 | "mocha": "*" 18 | }, 19 | "engines" : { "node": ">= 0.8.0" } 20 | } 21 | -------------------------------------------------------------------------------- /test/domain.test.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * connect-domain - test/domian.test.js 3 | * Copyright(c) 2012 fengmk2 4 | * MIT Licensed 5 | */ 6 | 7 | "use strict"; 8 | 9 | /** 10 | * Module dependencies. 11 | */ 12 | 13 | var connect = require('connect'); 14 | var connectDomain = require('../'); 15 | var should = require('should'); 16 | var request = require('supertest'); 17 | 18 | describe('domain.test.js', function () { 19 | var normalHandler = function normalHandler(req, res, next) { 20 | if (req.url === '/sync_error') { 21 | throw new Error('sync_error'); 22 | } 23 | if (req.url === '/async_error') { 24 | process.nextTick(function () { 25 | ff.foo(); 26 | }); 27 | return; 28 | } 29 | if (req.url === '/async_error_twice') { 30 | setTimeout(function () { 31 | ff.foo(); 32 | }, 100); 33 | setTimeout(function () { 34 | bar.bar(); 35 | }, 210); 36 | return; 37 | } 38 | res.end(req.url); 39 | }; 40 | 41 | var errorHandler = function errorHandler(err, req, res, next) { 42 | res.statusCode = 500; 43 | res.end(err.message); 44 | }; 45 | 46 | var app = connect() 47 | .use(connectDomain()) 48 | .use(normalHandler) 49 | .use(errorHandler); 50 | 51 | it('should GET / status 200', function (done) { 52 | request(app) 53 | .get('/') 54 | .expect(200, done); 55 | }); 56 | 57 | it('should GET /sync_error status 500', function (done) { 58 | request(app) 59 | .get('/sync_error') 60 | .expect('sync_error') 61 | .expect(500, done); 62 | }); 63 | 64 | describe('hack for async error', function () { 65 | // Because `domain` will still throw `uncaughtException`, we need to hack for `mocha` test. 66 | // https://github.com/joyent/node/issues/4375 67 | // https://gist.github.com/4179636 68 | var mochaHandler; 69 | before(function () { 70 | mochaHandler = process.listeners('uncaughtException').pop(); 71 | }); 72 | after(function (done) { 73 | // ...but be sure to re-enable mocha's error handler 74 | process.on('uncaughtException', mochaHandler); 75 | setTimeout(done, 500); 76 | }); 77 | 78 | it('should GET /async_error status 500', function (done) { 79 | request(app) 80 | .get('/async_error') 81 | .expect('ff is not defined') 82 | .expect(500, done); 83 | }); 84 | 85 | it('should GET /async_error_twice status 500', function (done) { 86 | request(app) 87 | .get('/async_error_twice') 88 | .expect('ff is not defined') 89 | .expect(500, done); 90 | }); 91 | }); 92 | 93 | }); --------------------------------------------------------------------------------