I have something similar to this:
var domain = require ("domain");
var http = require ("http");
var d = domain.create ();
d.on ("error", function (error){
console.error (error);
//res.writeHead (500)
//res.end ()
});
d.run (function (){
http.createServer (function (req, res){
null.error
}).listen (1337, "localhost");
});
All my server runs inside a domain. If an error occurs during a request I want to send to the user a 500 error but inside the error handler I can't access to the response object.
My first attempt is to use an explicit domain for each request as the docs says:
var d = domain.create ();
d.on ("error", function (error){
//res.writeHead (500);
//res.end ()
console.error (error);
});
d.run (function (){
http.createServer (function (req, res){
var dreq = domain.create ();
dreq.add (req)
dreq.add (res)
dreq.on ("error", function (error){
res.writeHead (500);
res.end ()
console.error (error);
});
null.error
}).listen (1337, "localhost");
});
The dreq error handler is never called because req and res never emit, the error occurs in execution-time, it's a TypeError.
My second attempt is to save a reference to the res object using a closure, for example an event emitter:
var domain = require ("domain");
var http = require ("http");
var events = require ("events");
var emitter = new events.EventEmitter ();
var d = domain.create ();
d.on ("error", function (error){
emitter.emit ("error", error);
});
d.run (function (){
http.createServer (function (req, res){
emitter.once ("error", function (error){
res.writeHead (500);
res.end ();
console.error (error);
});
null.error
}).listen (1337, "localhost");
});
This works, the error is catched in the error handler and then emitted so I can use the response object to send a message to the user. If you have a very little knowledge on the node.js basics you'll see that this workaround is completely bugged. If there are 5 user requests on the event loop and 1 of them does a task that throws an error, the emitter will execute all the error handlers and the 5 users wil be notified with a 500 error when only 1 of them should receive a 500 error.
My last attempt is to create sub-contexts for each request. The docs says nothing about this pattern. This is a bit confusing to me:
In order to prevent excessive memory usage, Domain objects themselves are not implicitly added as children of the active domain. If they were, then it would be too easy to prevent request and response objects from being properly garbage collected.
If you want to nest Domain objects as children of a parent Domain, then you must explicitly add them, and then dispose of them later.
var domain = require ("domain");
var http = require ("http");
var d = domain.create ();
d.on ("error", function (error){
console.error (error);
});
d.run (function (){
http.createServer (function (req, res){
var dreq = domain.create ();
dreq.on ("error", function (error){
res.writeHead (500);
res.end ()
console.error (error);
dreq.dispose ();
});
dreq.run (function (){
null.error
//...
//dreq.dispose ();
});
}).listen (1337, "localhost");
});
Am I introducing memory leaks?
The easy solution would be to assign a unique ID to each request and save them in an object or an array and when the emitter emits find the handler that must be executed given the unique ID. Totally inacceptable.
If I can't save a reference to the res object how can I send an error to the user inside the domain error handler?
You're last posted solution is actually what you should do. This is not the same thing as the type of nesting that the docs advise against. All you're doing is wrapping the potentially (or, in this case, definitely) erroring code in the domain so that when it does error that gets handled by the domain.
This solution works fine for me:
var http = require('http');
var domain = require('domain');
var server = http.createServer (function (req, res) {
var reqDomain = domain.create();
reqDomain.on('error', function (error) {
res.writeHead(500);
res.end('error 500');
console.error(error);
});
res.on('close', function () {
reqDomain.dispose();
});
reqDomain.run(function () {
// Request handler code here
null.error
});
}).listen(3000);
server.on('error', function (error) {
console.error('Server error: ' + error);
});
This solution is the winner:
var domain = require ("domain");
var http = require ("http");
var d = domain.create ();
d.on ("error", function (error){
console.error (error);
});
d.run (function (){
http.createServer (function (req, res){
var dreq = domain.create ();
dreq.add (req);
dreq.add (res);
dreq.on ("error", function (error){
res.on ("close", function (){
dreq.exit ();
});
res.writeHead (500);
res.end ()
console.error (error);
dreq.exit();
});
dreq.run (function (){
null.error
});
}).listen (1337, "localhost");
});