Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

async_wrap: add asyncReset to TLSWrap #13092

Merged
merged 0 commits into from
May 20, 2017
Merged

Conversation

refack
Copy link
Contributor

@refack refack commented May 18, 2017

When using an Agent for HTTPS, TLSSockets are reused and need to
have the ability to asyncReset from JS.

Fixes: #13045
(Not a complete solution. Does not solve custom Agent use case)

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • commit message follows commit guidelines
Affected core subsystem(s)

async_wrap

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. tls Issues and PRs related to the tls subsystem. labels May 18, 2017
@refack refack self-assigned this May 18, 2017
@refack
Copy link
Contributor Author

refack commented May 18, 2017

CI: https://ci.nodejs.org/job/node-test-commit/9967/
(just cause my computer takes so long to build...)

@mscdex mscdex added the wip Issues and PRs that are still a work in progress. label May 18, 2017
@refack refack changed the title [WIP] test-balloon: investigate #13045 async_wrap: add asyncReset to TLSWrap May 18, 2017
@refack refack removed the wip Issues and PRs that are still a work in progress. label May 18, 2017
@refack
Copy link
Contributor Author

refack commented May 18, 2017

@refack refack requested a review from trevnorris May 18, 2017 10:18
@refack
Copy link
Contributor Author

refack commented May 18, 2017


const req = https.request(clientOptions, common.mustCall((res) => {
assert.strictEqual(res.statusCode, 200);
res.on('error', common.mustNotCall());
Copy link
Member

@AndreasMadsen AndreasMadsen May 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to add this listener? If there is an error in the future I think it would be better to see that error than an AssertionError('must not throw') message.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 and put assert.ifError?
Ok.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will just throw by default, which will cause the test to fail.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But then you don't get which line... And since there are two almost identical calls it's a PITA (just been there while writing this test)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. I think the ifError approach is fine then.

@refack
Copy link
Contributor Author

refack commented May 18, 2017

Copy link
Member

@AndreasMadsen AndreasMadsen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Contributor

@cjihrig cjihrig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of nits with the test, but mostly LGTM

'use strict';
const common = require('../common');

// checks for the issue in https://github.com/nodejs/node/issues/13045
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

checks for the issue in -> Refs:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack


const req = https.request(clientOptions, common.mustCall((res) => {
assert.strictEqual(res.statusCode, 200);
res.on('error', assert.ifError);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you replace the uses of assert.ifError() with common.mustNotCall(). You can provide a custom message with the latter if you'd like.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #13092 (comment)
@AndreasMadsen suggested that we might want to see the actual error.
(Maybe we need mustNotErr like assert.doesNotThrow)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, you might want to use assert.fail() instead. ifError() implies that there might not be an error, but in this case, we know that there is one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a factory method mustNotErr that calls assert.fail so the fail point will appear in the stack

try {
req2 = https.request(clientOptions);
} catch (e) {
assert.ifError(e);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this redundant?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a sense yes, I just wanted to be explicit about the pain point.
I could replace this with a comment.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't you drop the try...catch completely, and if it happens to throw, it will fail the test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack. Replaced with a comment

'use strict';
const common = require('../common');

// Checks for the issue in Refs.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry if my previous comment was unclear. I just meant to write this as:

Refs: #13045

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack

const fs = require('fs');

// arrow factory so fail point will appear in the stack
function mustNotErr() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why you need this. You can just pass assert.fail (without the parens) as the handler, everywhere you use mustNotErr().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried it, then there is no frame for the line with the failing assert (Actually the factory is not a solution I need explicit (err) => assert.fail(err))

Generated error by setting port: port + 1
with (err) => assert.fail(err)

assert.js:92
  throw new AssertionError({
  ^

AssertionError [ERR_ASSERTION]: Error: write EPROTO 101057795:error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol:openssl\ssl\s23_clnt.c:794:

    at ClientRequest.req.on (D:\code\node-cur\test\parallel\test-async-wrap-GH13045.js:63:35)
    at emitOne (events.js:115:13)
    at ClientRequest.emit (events.js:210:7)
    at TLSSocket.socketErrorListener (_http_client.js:397:9)
    at emitOne (events.js:115:13)
    at TLSSocket.emit (events.js:210:7)
    at onwriteError (_stream_writable.js:359:10)
    at onwrite (_stream_writable.js:377:5)
    at fireErrorCallbacks (net.js:522:13)
    at TLSSocket.Socket._destroy (net.js:563:3)

with just assert.fail

assert.js:92
  throw new AssertionError({
  ^

AssertionError [ERR_ASSERTION]: Error: write EPROTO 101057795:error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol:openssl\ssl\s23_clnt.c:794:

    at emitOne (events.js:115:13)
    at ClientRequest.emit (events.js:210:7)
    at TLSSocket.socketErrorListener (_http_client.js:397:9)
    at emitOne (events.js:115:13)
    at TLSSocket.emit (events.js:210:7)
    at onwriteError (_stream_writable.js:359:10)
    at onwrite (_stream_writable.js:377:5)
    at fireErrorCallbacks (net.js:522:13)
    at TLSSocket.Socket._destroy (net.js:563:3)
    at WriteWrap.afterWrite [as oncomplete] (net.js:857:10)

@Fishrock123
Copy link
Contributor

Fishrock123 commented May 18, 2017

Sounds correct to me.

@mcollina
Copy link
Member

Can we get a CITGM run? that has been the main blocker for me.

@addaleax
Copy link
Member

});
}));

req.on('error', (err) => assert.fail(err));
Copy link
Contributor

@mscdex mscdex May 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this more or less equivalent to just not adding an 'error' listener? Similarly with the 'error' handlers above.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case, I'm -0 on it.

// 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.request(clientOptions);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add the response handler as a second argument here for consistency?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto about switching to https.get().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ack, ack.

ca: fs.readFileSync(`${common.fixturesDir}/keys/ca1-cert.pem`)
};

const server = https.createServer(serverOptions, (req, res) => {
Copy link
Contributor

@mscdex mscdex May 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

common.mustCall((req, res) => { ... }, 2) ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ack.

agent: new https.Agent({
keepAlive: true,
maxSockets: 50,
rejectUnauthorized: false,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these last two options necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just last one: AssertionError [ERR_ASSERTION]: Error: self signed certificate in certificate chain

method: 'GET'
};

const req = https.request(clientOptions, common.mustCall((res) => {
Copy link
Contributor

@mscdex mscdex May 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think method: 'GET' could be dropped and .request() changed to .get() to avoid the need to req.end().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ack

}),
hostname: common.localhostIPv4,
port: port,
path: '/',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't needed, it's even the default.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ack

Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@refack
Copy link
Contributor Author

refack commented May 19, 2017

@refack
Copy link
Contributor Author

refack commented May 19, 2017

Pre land CI:https://ci.nodejs.org/job/node-test-commit/10006/

Ping @trevnorris any comments? I would like to land this in a few hours.

@refack
Copy link
Contributor Author

refack commented May 20, 2017

Landed in 6bfdeed

@refack refack merged commit 6bfdeed into nodejs:master May 20, 2017
@refack
Copy link
Contributor Author

refack commented May 20, 2017

@refack refack deleted the async-wrap-13045 branch May 20, 2017 03:33
@AndreasMadsen
Copy link
Member

@refack Thanks for taking care of this.

@mcollina
Copy link
Member

Thank you @refack. 🎉

@MylesBorins
Copy link
Contributor

This should likely be included in a larger async_wrap backport if it were to happen

@MylesBorins MylesBorins added the baking-for-lts PRs that need to wait before landing in a LTS release. label Aug 14, 2017
@MylesBorins MylesBorins removed the baking-for-lts PRs that need to wait before landing in a LTS release. label Aug 17, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ Issues and PRs that require attention from people who are familiar with C++. tls Issues and PRs related to the tls subsystem.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

test-npm failing on master after introduction of initial async hooks implementation
9 participants