Skip to content

Commit 6bfdeed

Browse files
committed
async_wrap: add asyncReset to TLSWrap
When using an Agent for HTTPS, `TLSSocket`s are reused and need to have the ability to `asyncReset` from JS. PR-URL: #13092 Fixes: #13045 Reviewed-By: Andreas Madsen <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Matteo Collina <[email protected]>
1 parent 4a7b7e8 commit 6bfdeed

File tree

2 files changed

+54
-0
lines changed

2 files changed

+54
-0
lines changed

src/tls_wrap.cc

+1
Original file line numberDiff line numberDiff line change
@@ -940,6 +940,7 @@ void TLSWrap::Initialize(Local<Object> target,
940940
t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "TLSWrap"));
941941

942942
env->SetProtoMethod(t, "getAsyncId", AsyncWrap::GetAsyncId);
943+
env->SetProtoMethod(t, "asyncReset", AsyncWrap::AsyncReset);
943944
env->SetProtoMethod(t, "receive", Receive);
944945
env->SetProtoMethod(t, "start", Start);
945946
env->SetProtoMethod(t, "setVerifyMode", SetVerifyMode);
+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
'use strict';
2+
const common = require('../common');
3+
4+
// Refs: https://github.com/nodejs/node/issues/13045
5+
// An HTTP Agent reuses a TLSSocket, and makes a failed call to `asyncReset`.
6+
7+
const assert = require('assert');
8+
const https = require('https');
9+
const fs = require('fs');
10+
11+
const serverOptions = {
12+
key: fs.readFileSync(`${common.fixturesDir}/keys/agent1-key.pem`),
13+
cert: fs.readFileSync(`${common.fixturesDir}/keys/agent1-cert.pem`),
14+
ca: fs.readFileSync(`${common.fixturesDir}/keys/ca1-cert.pem`)
15+
};
16+
17+
const server = https.createServer(serverOptions, common.mustCall((req, res) => {
18+
res.end('hello world\n');
19+
}, 2));
20+
21+
server.listen(0, common.mustCall(function() {
22+
const port = this.address().port;
23+
const clientOptions = {
24+
agent: new https.Agent({
25+
keepAlive: true,
26+
rejectUnauthorized: false
27+
}),
28+
port: port
29+
};
30+
31+
const req = https.get(clientOptions, common.mustCall((res) => {
32+
assert.strictEqual(res.statusCode, 200);
33+
res.on('error', (err) => assert.fail(err));
34+
res.socket.on('error', (err) => assert.fail(err));
35+
res.resume();
36+
// drain the socket and wait for it to be free to reuse
37+
res.socket.once('free', () => {
38+
// This is the pain point. Internally the Agent will call
39+
// `socket._handle.asyncReset()` and if the _handle does not implement
40+
// `asyncReset` this will throw TypeError
41+
const req2 = https.get(clientOptions, common.mustCall((res2) => {
42+
assert.strictEqual(res.statusCode, 200);
43+
res2.on('error', (err) => assert.fail(err));
44+
res2.socket.on('error', (err) => assert.fail(err));
45+
// this should be the end of the test
46+
res2.destroy();
47+
server.close();
48+
}));
49+
req2.on('error', (err) => assert.fail(err));
50+
});
51+
}));
52+
req.on('error', (err) => assert.fail(err));
53+
}));

0 commit comments

Comments
 (0)