Skip to content

Commit 9b3271b

Browse files
committed
test: add ALS test using http agent keep alive
Add a tests 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 9b3271b

File tree

1 file changed

+82
-0
lines changed

1 file changed

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

0 commit comments

Comments
 (0)