Skip to content

Commit

Permalink
async_wrap: add asyncReset to TLSWrap
Browse files Browse the repository at this point in the history
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 <amwebdk@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
  • Loading branch information
refack committed May 20, 2017
1 parent 4a7b7e8 commit 6bfdeed
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/tls_wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,7 @@ void TLSWrap::Initialize(Local<Object> target,
t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "TLSWrap"));

env->SetProtoMethod(t, "getAsyncId", AsyncWrap::GetAsyncId);
env->SetProtoMethod(t, "asyncReset", AsyncWrap::AsyncReset);
env->SetProtoMethod(t, "receive", Receive);
env->SetProtoMethod(t, "start", Start);
env->SetProtoMethod(t, "setVerifyMode", SetVerifyMode);
Expand Down
53 changes: 53 additions & 0 deletions test/parallel/test-async-wrap-GH13045.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
'use strict';
const common = require('../common');

// Refs: https://github.com/nodejs/node/issues/13045
// An HTTP Agent reuses a TLSSocket, and makes a failed call to `asyncReset`.

const assert = require('assert');
const https = require('https');
const fs = require('fs');

const serverOptions = {
key: fs.readFileSync(`${common.fixturesDir}/keys/agent1-key.pem`),
cert: fs.readFileSync(`${common.fixturesDir}/keys/agent1-cert.pem`),
ca: fs.readFileSync(`${common.fixturesDir}/keys/ca1-cert.pem`)
};

const server = https.createServer(serverOptions, common.mustCall((req, res) => {
res.end('hello world\n');
}, 2));

server.listen(0, common.mustCall(function() {
const port = this.address().port;
const clientOptions = {
agent: new https.Agent({
keepAlive: true,
rejectUnauthorized: false
}),
port: port
};

const req = https.get(clientOptions, common.mustCall((res) => {
assert.strictEqual(res.statusCode, 200);
res.on('error', (err) => assert.fail(err));
res.socket.on('error', (err) => assert.fail(err));
res.resume();
// drain the socket and wait for it to be free to reuse
res.socket.once('free', () => {
// This is the pain point. Internally the Agent will call
// `socket._handle.asyncReset()` and if the _handle does not implement
// `asyncReset` this will throw TypeError
const req2 = https.get(clientOptions, common.mustCall((res2) => {
assert.strictEqual(res.statusCode, 200);
res2.on('error', (err) => assert.fail(err));
res2.socket.on('error', (err) => assert.fail(err));
// this should be the end of the test
res2.destroy();
server.close();
}));
req2.on('error', (err) => assert.fail(err));
});
}));
req.on('error', (err) => assert.fail(err));
}));

0 comments on commit 6bfdeed

Please sign in to comment.