Skip to content

Commit fa87548

Browse files
author
Forrest L Norvell
committed
src: continuation-local-storage-glue no longer necessary
Instead this module now depends on async-listener, a polyfill intended to replicate the addAsyncListener API Trevor Norris is presently experimenting with adding to Node core. As it's a pure polyfill, CLS can just load it if necessary and include all of the CLS functionality directly in the module.
1 parent e4b0f5b commit fa87548

14 files changed

+1443
-7
lines changed

context.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
var assert = require('assert');
44

5+
// load polyfill if native support is unavailable
6+
if (!process.addAsyncListener) require('async-listener');
7+
58
var namespaces = process.namespaces = Object.create(null);
69

710
function Namespace (name) {
@@ -84,6 +87,22 @@ Namespace.prototype.exit = function (context) {
8487
};
8588

8689
module.exports = {
87-
createNamespace : function (name) { return new Namespace(name); },
90+
createNamespace : function (name) {
91+
var namespace = new Namespace(name);
92+
process.addAsyncListener(
93+
function () {
94+
return namespace.active;
95+
},
96+
{
97+
before: function (context) {
98+
namespace.enter(context);
99+
},
100+
after: function (context) {
101+
namespace.exit(context);
102+
}
103+
}
104+
);
105+
return namespace;
106+
},
88107
getNamespace : getNamespace
89108
};

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,8 @@
2929
"license": "BSD",
3030
"devDependencies": {
3131
"tap": "~0.4.2"
32+
},
33+
"optionalDependencies": {
34+
"async-listener": "0.0.2"
3235
}
3336
}

test/async-context.tap.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
'use strict';
2+
3+
var tap = require('tap')
4+
, test = tap.test
5+
, createNamespace = require('../context.js').createNamespace
6+
;
7+
8+
test("asynchronously propagating state with local-context-domains", function (t) {
9+
t.plan(2);
10+
11+
var namespace = createNamespace('namespace');
12+
t.ok(process.namespaces.namespace, "namespace has been created");
13+
14+
namespace.set('test', 1337);
15+
t.equal(namespace.get('test'), 1337, "namespace is working");
16+
});

test/bind.tap.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ var test = tap.test;
66
var EventEmitter = require('events').EventEmitter;
77

88
// module under test
9-
var context = require('../context');
9+
var context = require('../context.js');
1010

1111
// multiple contexts in use
1212
var tracer = context.createNamespace('tracer');

test/crypto.tap.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
'use strict';
2+
3+
var tap = require('tap')
4+
, test = tap.test
5+
, createNamespace = require('../context.js').createNamespace
6+
;
7+
8+
var crypto;
9+
try { crypto = require('crypto'); }
10+
catch (err) {}
11+
if (crypto) {
12+
13+
test("continuation-local state with crypto.randomBytes", function (t) {
14+
t.plan(1);
15+
16+
var namespace = createNamespace('namespace');
17+
namespace.set('test', 0xabad1dea);
18+
19+
t.test("deflate", function (t) {
20+
namespace.run(function () {
21+
namespace.set('test', 42);
22+
crypto.randomBytes(100, function (err, bytes) {
23+
if (err) throw err;
24+
t.equal(namespace.get('test'), 42, "mutated state was preserved");
25+
t.end();
26+
});
27+
});
28+
});
29+
});
30+
31+
test("continuation-local state with crypto.pseudoRandomBytes", function (t) {
32+
t.plan(1);
33+
34+
var namespace = createNamespace('namespace');
35+
namespace.set('test', 0xabad1dea);
36+
37+
t.test("deflate", function (t) {
38+
namespace.run(function () {
39+
namespace.set('test', 42);
40+
crypto.pseudoRandomBytes(100, function (err, bytes) {
41+
if (err) throw err;
42+
t.equal(namespace.get('test'), 42, "mutated state was preserved");
43+
t.end();
44+
});
45+
});
46+
});
47+
});
48+
49+
test("continuation-local state with crypto.pbkdf2", function (t) {
50+
t.plan(1);
51+
52+
var namespace = createNamespace('namespace');
53+
namespace.set('test', 0xabad1dea);
54+
55+
t.test("deflate", function (t) {
56+
namespace.run(function () {
57+
namespace.set('test', 42);
58+
crypto.pbkdf2("s3cr3tz", "451243", 10, 40, function (err, key) {
59+
if (err) throw err;
60+
t.equal(namespace.get('test'), 42, "mutated state was preserved");
61+
t.end();
62+
});
63+
});
64+
});
65+
});
66+
67+
}
68+

test/dns.tap.js

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
'use strict';
2+
3+
var dns = require('dns')
4+
, tap = require('tap')
5+
, test = tap.test
6+
, createNamespace = require('../context.js').createNamespace
7+
;
8+
9+
test("continuation-local state with MakeCallback and DNS module", function (t) {
10+
t.plan(11);
11+
12+
var namespace = createNamespace('dns');
13+
namespace.set('test', 0xabad1dea);
14+
15+
t.test("dns.lookup", function (t) {
16+
namespace.run(function () {
17+
namespace.set('test', 808);
18+
t.equal(namespace.get('test'), 808, "state has been mutated");
19+
20+
dns.lookup('www.newrelic.com', 4, function (err, addresses) {
21+
t.notOk(err, "lookup succeeded");
22+
t.ok(addresses.length > 0, "some results were found");
23+
24+
t.equal(namespace.get('test'), 808,
25+
"mutated state has persisted to dns.lookup's callback");
26+
27+
t.end();
28+
});
29+
});
30+
});
31+
32+
t.test("dns.resolve", function (t) {
33+
namespace.run(function () {
34+
namespace.set('test', 909);
35+
t.equal(namespace.get('test'), 909, "state has been mutated");
36+
37+
dns.resolve('newrelic.com', 'NS', function (err, addresses) {
38+
t.notOk(err, "lookup succeeded");
39+
t.ok(addresses.length > 0, "some results were found");
40+
41+
t.equal(namespace.get('test'), 909,
42+
"mutated state has persisted to dns.resolve's callback");
43+
44+
t.end();
45+
});
46+
});
47+
});
48+
49+
t.test("dns.resolve4", function (t) {
50+
namespace.run(function () {
51+
namespace.set('test', 303);
52+
t.equal(namespace.get('test'), 303, "state has been mutated");
53+
54+
dns.resolve4('www.newrelic.com', function (err, addresses) {
55+
t.notOk(err, "lookup succeeded");
56+
t.ok(addresses.length > 0, "some results were found");
57+
58+
t.equal(namespace.get('test'), 303,
59+
"mutated state has persisted to dns.resolve4's callback");
60+
61+
t.end();
62+
});
63+
});
64+
});
65+
66+
t.test("dns.resolve6", function (t) {
67+
namespace.run(function () {
68+
namespace.set('test', 101);
69+
t.equal(namespace.get('test'), 101, "state has been mutated");
70+
71+
dns.resolve6('google.com', function (err, addresses) {
72+
t.notOk(err, "lookup succeeded");
73+
t.ok(addresses.length > 0, "some results were found");
74+
75+
t.equal(namespace.get('test'), 101,
76+
"mutated state has persisted to dns.resolve6's callback");
77+
78+
t.end();
79+
});
80+
});
81+
});
82+
83+
t.test("dns.resolveCname", function (t) {
84+
namespace.run(function () {
85+
namespace.set('test', 212);
86+
t.equal(namespace.get('test'), 212, "state has been mutated");
87+
88+
dns.resolveCname('mail.newrelic.com', function (err, addresses) {
89+
t.notOk(err, "lookup succeeded");
90+
t.ok(addresses.length > 0, "some results were found");
91+
92+
t.equal(namespace.get('test'), 212,
93+
"mutated state has persisted to dns.resolveCname's callback");
94+
95+
t.end();
96+
});
97+
});
98+
});
99+
100+
t.test("dns.resolveMx", function (t) {
101+
namespace.run(function () {
102+
namespace.set('test', 707);
103+
t.equal(namespace.get('test'), 707, "state has been mutated");
104+
105+
dns.resolveMx('newrelic.com', function (err, addresses) {
106+
t.notOk(err, "lookup succeeded");
107+
t.ok(addresses.length > 0, "some results were found");
108+
109+
t.equal(namespace.get('test'), 707,
110+
"mutated state has persisted to dns.resolveMx's callback");
111+
112+
t.end();
113+
});
114+
});
115+
});
116+
117+
t.test("dns.resolveNs", function (t) {
118+
namespace.run(function () {
119+
namespace.set('test', 717);
120+
t.equal(namespace.get('test'), 717, "state has been mutated");
121+
122+
dns.resolveNs('newrelic.com', function (err, addresses) {
123+
t.notOk(err, "lookup succeeded");
124+
t.ok(addresses.length > 0, "some results were found");
125+
126+
t.equal(namespace.get('test'), 717,
127+
"mutated state has persisted to dns.resolveNs's callback");
128+
129+
t.end();
130+
});
131+
});
132+
});
133+
134+
t.test("dns.resolveTxt", function (t) {
135+
namespace.run(function () {
136+
namespace.set('test', 2020);
137+
t.equal(namespace.get('test'), 2020, "state has been mutated");
138+
139+
dns.resolveTxt('newrelic.com', function (err, addresses) {
140+
t.notOk(err, "lookup succeeded");
141+
t.ok(addresses.length > 0, "some results were found");
142+
143+
t.equal(namespace.get('test'), 2020,
144+
"mutated state has persisted to dns.resolveTxt's callback");
145+
146+
t.end();
147+
});
148+
});
149+
});
150+
151+
t.test("dns.resolveSrv", function (t) {
152+
namespace.run(function () {
153+
namespace.set('test', 9000);
154+
t.equal(namespace.get('test'), 9000, "state has been mutated");
155+
156+
dns.resolveSrv('_xmpp-server._tcp.google.com', function (err, addresses) {
157+
t.notOk(err, "lookup succeeded");
158+
t.ok(addresses.length > 0, "some results were found");
159+
160+
t.equal(namespace.get('test'), 9000,
161+
"mutated state has persisted to dns.resolveSrv's callback");
162+
163+
t.end();
164+
});
165+
});
166+
});
167+
168+
t.test("dns.resolveNaptr", function (t) {
169+
// dns.resolveNaptr only in Node > 0.9.x
170+
if (!dns.resolveNaptr) return t.end();
171+
172+
namespace.run(function () {
173+
namespace.set('test', 'Polysix');
174+
t.equal(namespace.get('test'), 'Polysix', "state has been mutated");
175+
176+
dns.resolveNaptr('columbia.edu', function (err, addresses) {
177+
t.notOk(err, "lookup succeeded");
178+
t.ok(addresses.length > 0, "some results were found");
179+
180+
t.equal(namespace.get('test'), 'Polysix',
181+
"mutated state has persisted to dns.resolveNaptr's callback");
182+
183+
t.end();
184+
});
185+
});
186+
});
187+
188+
t.test("dns.reverse", function (t) {
189+
namespace.run(function () {
190+
namespace.set('test', 1000);
191+
t.equal(namespace.get('test'), 1000, "state has been mutated");
192+
193+
dns.reverse('204.93.223.144', function (err, addresses) {
194+
t.notOk(err, "lookup succeeded");
195+
t.ok(addresses.length > 0, "some results were found");
196+
197+
t.equal(namespace.get('test'), 1000,
198+
"mutated state has persisted to dns.reverse's callback");
199+
200+
t.end();
201+
});
202+
});
203+
});
204+
});

test/error-handling.tap.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
'use strict';
2+
3+
var domain = require('domain')
4+
, test = require('tap').test
5+
, cls = require('../context.js')
6+
;
7+
8+
test("continuation-local storage glue with a throw in the continuation chain",
9+
function (t) {
10+
var namespace = cls.createNamespace('test');
11+
namespace.run(function () {
12+
var d = domain.create();
13+
namespace.set('outer', true);
14+
15+
d.on('error', function (blerg) {
16+
t.equal(blerg.message, "explicitly nonlocal exit", "got the expected exception");
17+
t.notOk(namespace.get('outer'), "outer context should have been exited by throw");
18+
t.notOk(namespace.get('inner'), "inner context should have been exited by throw");
19+
t.equal(namespace._stack.length, 0, "should be back to global state");
20+
21+
t.end();
22+
});
23+
24+
// tap is only trying to help
25+
process.nextTick(d.bind(function () {
26+
t.ok(namespace.get('outer'), "outer mutation worked");
27+
t.notOk(namespace.get('inner'), "inner mutation hasn't happened yet");
28+
29+
namespace.run(function () {
30+
namespace.set('inner', true);
31+
throw new Error("explicitly nonlocal exit");
32+
});
33+
}));
34+
});
35+
});

0 commit comments

Comments
 (0)