-
Notifications
You must be signed in to change notification settings - Fork 29.6k
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
http2: support generic Duplex
streams
#16269
Conversation
CI: https://ci.nodejs.org/job/node-test-commit/13238/ /cc @nodejs/http2 |
a0b1a2e
to
fe69dca
Compare
lib/internal/http2/core.js
Outdated
constructor(type, options, socket) { | ||
super(); | ||
|
||
if (!(socket instanceof net.Socket)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm... I'd almost prefer that this would check to see if the socket
has an ._external
... that is, rather than checking if it is a net.Socket
, that it would check if it is a StreamBase
somehow. Yes, generally it should always be socket but...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, I've purposefully avoided using instanceof
in the constructor here to avoid the performance hit. It would be good if we could find a way of avoiding adding it here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fwiw, for tls we also check instanceof net.Socket
, although I think I generally agree
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m going to sleep soon but feel free to push to this branch :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm working on a few other items so cannot break away to push on this branch.
One thing to keep in mind is that if this mechanism is only going to be used for the client side, the instanceof
check can be moved into the ClientHttp2Session
constructor rather than the base Http2Session
in order to keep the performance hit isolated just to the client side of things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually... scratch that... I see you're using this for server side also... hmmm let me stew on it a bit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I’ve switched to checking for the presence of socket._handle._externalStream
default: | ||
throw new errors.Error('ERR_HTTP2_UNSUPPORTED_PROTOCOL', protocol); | ||
if (typeof options.createConnection === 'function') { | ||
socket = options.createConnection(authority, options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should likely type check the return value
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think all we could do here is doing some duck-type checking, e.g. verifying that .write()
and .on()
are present…
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need to? This is very much opt-in... What does http
do for createConnection
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn’t do any explicit typechecking either, yes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok. I won't block on that. I much prefer the APIs to be stricter here and not gate based on what we do in http1 but I can live with this.
@@ -724,7 +729,8 @@ class Http2Session extends EventEmitter { | |||
this[kSocket] = socket; | |||
|
|||
// Do not use nagle's algorithm | |||
socket.setNoDelay(); | |||
if (typeof socket.setNoDelay === 'function') | |||
socket.setNoDelay(); | |||
|
|||
// Disable TLS renegotiation on the socket |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bit further down in this code there is a check for socket.connecting
that explicitly defers actions until the socket is connected. With this change, the assumption would be that the non-socket Stream passed in is immediately available for use, which is a perfectly fine assumption to make, IMHO, but I want to make sure that is actually a safe assumption here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m not sure what the alternative would be. We could document socket.connecting
/socket.on('connect')
as part of the implicit API here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would certainly be an option, but given that we're wrapping the custom socket, the likelihood of it even being an issue is pretty low. Let's go with this for now :-)
clientSide.end(); | ||
})); | ||
req.end(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be good to include a limited selection of additional variations of other http2 tests, especially those that deal with multiplexing and flow-control just to make sure there are no hidden gotchas. It should be fine, but over-abundance of caution and all that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don’t have that much of an overview over the HTTP/2 tests, it would be good if you could give pointers as to which test files I should be looking at.
(But yes, this should be fine anyway – this doesn’t really deal with anything below the
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can do this separately, actually. Don't worry about it in this PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good work. Does this affects benchmarks anyhow?
Can you add a unit test for sendFile as well?
I will run them but I’d go with “no” – there really shouldn’t be any measurable perf impact if you don’t use this feature (and even if you do, it’s only during session construction).
That sounds reasonable, yes. Done! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 👍 Just very minor nits.
constructor(type, options, socket) { | ||
super(); | ||
|
||
if (!socket._handle || !socket._handle._externalStream) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this is super nit picky but could we compare to undefined
? Anytime we're dealing with an object, because of the existence of document.all
(at least as far as V8 is concerned), these truthy/falsey checks are unusually expensive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some things in Node’s source explicitly set ._handle
on sockets to null
(e.g. after disconnecting)… so I’d like to be consistent with TLS here, just to be on the safe side.
these truthy/falsey checks are unusually expensive.
I doubt this makes a noticeable difference compared to the overall cost of setting up the HTTP2 session objects anyway, V8 seems to optimize a function containing only this line reasonable well to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For some reason I thought we were unsetting _handle
but if we do null
it then this is fine.
const MakeDuplexPair = require('../common/duplexpair'); | ||
|
||
{ | ||
const testData = '<h1>Hello World</h1>'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I think this is unused in this test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh. Yup, done!
|
||
req.setEncoding('utf8'); | ||
let data = ''; | ||
req.on('data', (chunk) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: ideally a mustCall?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just accumulating the data, and that’s checked in an assert
(inside a mustCall
) anyway
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mustCall's on data events can be problematic. If anything, a mustCallAtLeast
would be ok.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, this was strictly about the consistency of these calls. In other tests with this PR, data events have mustCall
. It sets a good example for newer contributors when there's consistency to where we mustCall
and where we don't. Or we could leave a comment explaining why not.
When I started writing tests and contributing to node I had a bit of confusion around this because it wasn't 100% consistent across tests.
But as I mentioned, this is just a very minor nit. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fwiw, the other call is somewhat intentionally checking that the data is sent through in a single chunk :) I’ve added 2a666de1940b as a comment for that
test/common/README.md
Outdated
@@ -8,6 +8,7 @@ This directory contains modules used to test the Node.js implementation. | |||
* [Common module API](#common-module-api) | |||
* [Countdown module](#countdown-module) | |||
* [DNS module](#dns-module) | |||
* [Duplex pair helper](#duplex-pair-module) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: should this be Duplex pair Module
to match heading below? Edit: Or I guess, to nit pick myself, Duplex pair module
to match the rest of the list...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want this changed for consistency, I’d go with Duplex pair helper
everywhere. Does that seem okay?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That seems good 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, done!
5bb1fa5
to
621d2d7
Compare
Fresh CI: https://ci.nodejs.org/job/node-test-commit/13370/ This should be ready. |
Allow `JSStream` instances to be used more flexibly by explicitly enabling calls that have no JS stack below them. PR-URL: nodejs#16269 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Remove unused methods for reading data from `JSStream` and add those required for emitting data or an EOF event to the JS side, in essentially the same way that `LibuvStreamWrap` does it. PR-URL: nodejs#16269 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Add a utility for adding simple, streams-API based duplex pairs. PR-URL: nodejs#16269 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Support generic `Duplex` streams through using `StreamWrap` on the server and client sides, and adding a `createConnection` method option similar to what the HTTP/1 API provides. Since HTTP2 is, as a protocol, independent of its underlying transport layer, Node.js should not enforce any restrictions on what streams its internals may use. Ref: nodejs#16256 PR-URL: nodejs#16269 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: James M Snell <jasnell@gmail.com>
2a666de
to
966e621
Compare
Add a utility for adding simple, streams-API based duplex pairs. PR-URL: nodejs#16269 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Landed in ba4a0a6...ab16eec |
Allow `JSStream` instances to be used more flexibly by explicitly enabling calls that have no JS stack below them. PR-URL: #16269 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Remove unused methods for reading data from `JSStream` and add those required for emitting data or an EOF event to the JS side, in essentially the same way that `LibuvStreamWrap` does it. PR-URL: #16269 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Add a utility for adding simple, streams-API based duplex pairs. PR-URL: #16269 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Support generic `Duplex` streams through using `StreamWrap` on the server and client sides, and adding a `createConnection` method option similar to what the HTTP/1 API provides. Since HTTP2 is, as a protocol, independent of its underlying transport layer, Node.js should not enforce any restrictions on what streams its internals may use. Ref: #16256 PR-URL: #16269 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Allow `JSStream` instances to be used more flexibly by explicitly enabling calls that have no JS stack below them. PR-URL: #16269 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Remove unused methods for reading data from `JSStream` and add those required for emitting data or an EOF event to the JS side, in essentially the same way that `LibuvStreamWrap` does it. PR-URL: #16269 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Add a utility for adding simple, streams-API based duplex pairs. PR-URL: #16269 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Support generic `Duplex` streams through using `StreamWrap` on the server and client sides, and adding a `createConnection` method option similar to what the HTTP/1 API provides. Since HTTP2 is, as a protocol, independent of its underlying transport layer, Node.js should not enforce any restrictions on what streams its internals may use. Ref: #16256 PR-URL: #16269 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Notable Changes: * crypto: - expose ECDH class #8188 * http2: - http2 is now exposed by defualt without the need for a flag #15685 - a new environment varible NODE\_NO\_HTTP2 has been added to allow userland http2 to be required #15685 - support has been added for generic `Duplex` streams #16269 * module: - resolve and instantiate loader pipeline hooks have been added to the ESM lifecycle #15445 * zlib: - CVE-2017-14919 - In zlib v1.2.9, a change was made that causes an error to be raised when a raw deflate stream is initialized with windowBits set to 8. On some versions this crashes Node and you cannot recover from it, while on some versions it throws an exception. Node.js will now gracefully set windowBits to 9 replicating the legacy behavior to avoid a DOS vector. https://github.com/nodejs-private/node-private/pull/95 PR-URL: https://github.com/nodejs-private/node-private/pull/98
Allow `JSStream` instances to be used more flexibly by explicitly enabling calls that have no JS stack below them. PR-URL: nodejs/node#16269 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Remove unused methods for reading data from `JSStream` and add those required for emitting data or an EOF event to the JS side, in essentially the same way that `LibuvStreamWrap` does it. PR-URL: nodejs/node#16269 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Add a utility for adding simple, streams-API based duplex pairs. PR-URL: nodejs/node#16269 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Support generic `Duplex` streams through using `StreamWrap` on the server and client sides, and adding a `createConnection` method option similar to what the HTTP/1 API provides. Since HTTP2 is, as a protocol, independent of its underlying transport layer, Node.js should not enforce any restrictions on what streams its internals may use. Ref: nodejs/node#16256 PR-URL: nodejs/node#16269 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Notable Changes: * crypto: - expose ECDH class nodejs/node#8188 * http2: - http2 is now exposed by defualt without the need for a flag nodejs/node#15685 - a new environment varible NODE\_NO\_HTTP2 has been added to allow userland http2 to be required nodejs/node#15685 - support has been added for generic `Duplex` streams nodejs/node#16269 * module: - resolve and instantiate loader pipeline hooks have been added to the ESM lifecycle nodejs/node#15445 * zlib: - CVE-2017-14919 - In zlib v1.2.9, a change was made that causes an error to be raised when a raw deflate stream is initialized with windowBits set to 8. On some versions this crashes Node and you cannot recover from it, while on some versions it throws an exception. Node.js will now gracefully set windowBits to 9 replicating the legacy behavior to avoid a DOS vector. nodejs-private/node-private#95 PR-URL: nodejs-private/node-private#98
Allow `JSStream` instances to be used more flexibly by explicitly enabling calls that have no JS stack below them. PR-URL: nodejs/node#16269 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Remove unused methods for reading data from `JSStream` and add those required for emitting data or an EOF event to the JS side, in essentially the same way that `LibuvStreamWrap` does it. PR-URL: nodejs/node#16269 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Add a utility for adding simple, streams-API based duplex pairs. PR-URL: nodejs/node#16269 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Support generic `Duplex` streams through using `StreamWrap` on the server and client sides, and adding a `createConnection` method option similar to what the HTTP/1 API provides. Since HTTP2 is, as a protocol, independent of its underlying transport layer, Node.js should not enforce any restrictions on what streams its internals may use. Ref: nodejs/node#16256 PR-URL: nodejs/node#16269 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Notable Changes: * crypto: - expose ECDH class nodejs/node#8188 * http2: - http2 is now exposed by defualt without the need for a flag nodejs/node#15685 - a new environment varible NODE\_NO\_HTTP2 has been added to allow userland http2 to be required nodejs/node#15685 - support has been added for generic `Duplex` streams nodejs/node#16269 * module: - resolve and instantiate loader pipeline hooks have been added to the ESM lifecycle nodejs/node#15445 * zlib: - CVE-2017-14919 - In zlib v1.2.9, a change was made that causes an error to be raised when a raw deflate stream is initialized with windowBits set to 8. On some versions this crashes Node and you cannot recover from it, while on some versions it throws an exception. Node.js will now gracefully set windowBits to 9 replicating the legacy behavior to avoid a DOS vector. nodejs-private/node-private#95 PR-URL: nodejs-private/node-private#98
Add a utility for adding simple, streams-API based duplex pairs. PR-URL: #16269 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Add a utility for adding simple, streams-API based duplex pairs. PR-URL: #16269 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Add a utility for adding simple, streams-API based duplex pairs. PR-URL: #16269 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Add a utility for adding simple, streams-API based duplex pairs. PR-URL: #16269 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Commits:
src: allow top-level calls into JSStream
Allow
JSStream
instances to be used more flexibly by explicitlyenabling calls that have no JS stack below them.
src: turn JS stream into a full duplex
Remove unused methods for reading data from
JSStream
and addthose required for emitting data or an EOF event to the JS side,
in essentially the same way that
LibuvStreamWrap
does it.http2: support generic
Duplex
streamsSupport generic
Duplex
streams through usingStreamWrap
on the server and client sides, and adding a
createConnection
method option similar to what the HTTP/1 API provides.
Since HTTP2 is, as a protocol, independent of its underlying transport
layer, Node.js should not enforce any restrictions on what streams
its internals may use.
Ref: #16256
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passesAffected core subsystem(s)
http2