Skip to content

Commit 887ae7a

Browse files
committed
test: add ALS test using http agent keep alive
Add a test to verify AsyncLocalStore functionality for HTTP using a keep alive agent. AsyncLocalStore moves away from using async_hooks therefore relying on async_hooks tests alone is not longer valid.
1 parent 1b5b12c commit 887ae7a

File tree

1 file changed

+83
-0
lines changed

1 file changed

+83
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
'use strict';
2+
const common = require('../common');
3+
const assert = require('node:assert');
4+
const { AsyncLocalStorage } = require('node:async_hooks');
5+
const http = require('node:http');
6+
7+
// Similar as test-async-hooks-http-agent added via
8+
// https://github.com/nodejs/node/issues/13325 but verifies
9+
// AsyncLocalStorage functionality instead async_hooks
10+
11+
const cls = new AsyncLocalStorage();
12+
13+
// Make sure a single socket is transparently reused for 2 requests.
14+
const agent = new http.Agent({
15+
keepAlive: true,
16+
keepAliveMsecs: Infinity,
17+
maxSockets: 1
18+
});
19+
20+
const server = http.createServer(common.mustCall((req, res) => {
21+
req.once('data', common.mustCallAtLeast(() => {
22+
res.writeHead(200, { 'Content-Type': 'text/plain' });
23+
res.write('foo');
24+
}));
25+
req.on('end', common.mustCall(() => {
26+
res.end('bar');
27+
}));
28+
}, 2)).listen(0, common.mustCall(() => {
29+
const port = server.address().port;
30+
const payload = 'hello world';
31+
32+
// First request. This is useless except for adding a socket to the
33+
// agent’s pool for reuse.
34+
cls.run('first', common.mustCall(() => {
35+
assert.strictEqual(cls.getStore(), 'first');
36+
const r1 = http.request({
37+
agent, port, method: 'POST'
38+
}, common.mustCall((res) => {
39+
assert.strictEqual(cls.getStore(), 'first');
40+
res.on('data', common.mustCallAtLeast(() => {
41+
assert.strictEqual(cls.getStore(), 'first');
42+
}));
43+
res.on('end', common.mustCall(() => {
44+
assert.strictEqual(cls.getStore(), 'first');
45+
// setImmediate() to give the agent time to register the freed socket.
46+
setImmediate(common.mustCall(() => {
47+
assert.strictEqual(cls.getStore(), 'first');
48+
49+
cls.run('second', common.mustCall(() => {
50+
// Second request. To re-create the exact conditions from the
51+
// referenced issue, we use a POST request without chunked encoding
52+
// (hence the Content-Length header) and call .end() after the
53+
// response header has already been received.
54+
const r2 = http.request({
55+
agent, port, method: 'POST', headers: {
56+
'Content-Length': payload.length
57+
}
58+
}, common.mustCall((res) => {
59+
assert.strictEqual(cls.getStore(), 'second');
60+
// Empty payload, to hit the “right” code path.
61+
r2.end('');
62+
63+
res.on('data', common.mustCallAtLeast(() => {
64+
assert.strictEqual(cls.getStore(), 'second');
65+
}));
66+
res.on('end', common.mustCall(() => {
67+
assert.strictEqual(cls.getStore(), 'second');
68+
// Clean up to let the event loop stop.
69+
server.close();
70+
agent.destroy();
71+
}));
72+
}));
73+
74+
// Schedule a payload to be written immediately, but do not end the
75+
// request just yet.
76+
r2.write(payload);
77+
}));
78+
}));
79+
}));
80+
}));
81+
r1.end(payload);
82+
}));
83+
}));

0 commit comments

Comments
 (0)