-
Notifications
You must be signed in to change notification settings - Fork 30k
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: empty DATA frame used instead of END_STREAM on last DATA frame #33891
Comments
Here's what the output looks like:
The problem lies in two |
@clshortfuse So … I can give you some context on this :) Yes, We have worked around this problem at least once, in zlib, where we look up streams internals to figure out whether the chunk is final or not: Lines 397 to 401 in 5536044
Without that, I think it’s generally not possible to tell the following patterns apart from inside the stream: stream.write(data, () => stream.end()); // vs.
stream.end(data); So, my understanding is that, in |
In But, there is a problem. Line 590 in 5536044
The path of least resistance seems to queue up the buffers and use https://github.com/nodejs/node/blob/master/src/node_http2.cc#L2191 The problem is the next interaction nearly immediately tells I encountered this "was end called?"/"is this the final chunk?" issue, personally while trying to transparently calculate |
So this works: [kWriteGeneric](writev, data, encoding, cb) {
// When the Http2Stream is first created, it is corked until the
// handle and the stream ID is assigned. However, if the user calls
// uncork() before that happens, the Duplex will attempt to pass
// writes through. Those need to be queued up here.
if (this.pending) {
this.once(
'ready',
this[kWriteGeneric].bind(this, writev, data, encoding, cb)
);
return;
}
// If the stream has been destroyed, there's nothing else we can do
// because the handle has been destroyed. This should only be an
// issue if a write occurs before the 'ready' event in the case where
// the duplex is uncorked before the stream is ready to go. In that
// case, drop the data on the floor. An error should have already been
// emitted.
if (this.destroyed)
return;
this[kUpdateTimer]();
if (!this.headersSent)
this[kProceed]();
let req;
let waitingForWriteCallback = true;
let waitingForEndCheck = true;
let writeCallbackErr;
let endCheckCallbackErr;
const done = () => {
if (waitingForEndCheck || waitingForWriteCallback) return;
const err = writeCallbackErr || endCheckCallbackErr;
if (err) {
// writeGeneric does not destroy on error and we cannot enable autoDestroy,
// so make sure to destroy on error.
this.destroy(err);
}
cb(err);
}
const writeCallback = (err) => {
waitingForWriteCallback = false;
writeCallbackErr = err;
done();
};
const endCheckCallback = (err) => {
waitingForEndCheck = false;
endCheckCallbackErr = err;
done()
}
process.nextTick(() => {
if (!this._writableState.ending) return endCheckCallback();
if (this._writableState.buffered.length) return endCheckCallback();
const handle = this[kHandle];
if (!handle) return endCheckCallback();
debugStreamObj(this, 'fast shutting down');
const req = new ShutdownWrap();
req.oncomplete = afterShutdown;
req.callback = endCheckCallback;
req.handle = handle;
const err = handle.shutdown(req);
if (err === 1) // synchronous finish
return afterShutdown.call(req, 0);
});
if (writev)
req = writevGeneric(this, data, writeCallback);
else
req = writeGeneric(this, data, encoding, writeCallback);
trackWriteState(this, req.bytes);
} With code injection here: node/lib/internal/http2/core.js Line 1981 in 272b46e
Basically, after sending a
|
Adds support for reading from a stream where the final frame is a non-empty DATA frame with the END_STREAM flag set, instead of hanging waiting for another frame. When writing to a stream, uses a END_STREAM flag on final DATA frame instead of adding an empty DATA frame. BREAKING: http2 client now expects servers to properly support END_STREAM flag Fixes: nodejs#31309 Fixes: nodejs#33891 Refs: https://nghttp2.org/documentation/types.html#c.nghttp2_on_data_chunk_recv_callback
Adds support for reading from a stream where the final frame is a non-empty DATA frame with the END_STREAM flag set, instead of hanging waiting for another frame. When writing to a stream, uses a END_STREAM flag on final DATA frame instead of adding an empty DATA frame. BREAKING: http2 client now expects servers to properly support END_STREAM flag Fixes: nodejs#31309 Fixes: nodejs#33891 Refs: https://nghttp2.org/documentation/types.html#c.nghttp2_on_data_chunk_recv_callback PR-URL: nodejs#33875 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com>
Adds support for reading from a stream where the final frame is a non-empty DATA frame with the END_STREAM flag set, instead of hanging waiting for another frame. When writing to a stream, uses a END_STREAM flag on final DATA frame instead of adding an empty DATA frame. BREAKING: http2 client now expects servers to properly support END_STREAM flag Fixes: nodejs#31309 Fixes: nodejs#33891 Refs: https://nghttp2.org/documentation/types.html#c.nghttp2_on_data_chunk_recv_callback
Adds support for reading from a stream where the final frame is a non-empty DATA frame with the END_STREAM flag set, instead of hanging waiting for another frame. When writing to a stream, uses a END_STREAM flag on final DATA frame instead of adding an empty DATA frame. BREAKING: http2 client now expects servers to properly support END_STREAM flag Fixes: #31309 Fixes: #33891 Refs: https://nghttp2.org/documentation/types.html#c.nghttp2_on_data_chunk_recv_callback PR-URL: #33875 Backport-PR-URL: #34838 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com>
Adds support for reading from a stream where the final frame is a non-empty DATA frame with the END_STREAM flag set, instead of hanging waiting for another frame. When writing to a stream, uses a END_STREAM flag on final DATA frame instead of adding an empty DATA frame. BREAKING: http2 client now expects servers to properly support END_STREAM flag Fixes: nodejs#31309 Fixes: nodejs#33891 Refs: https://nghttp2.org/documentation/types.html#c.nghttp2_on_data_chunk_recv_callback PR-URL: nodejs#33875 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com>
Adds support for reading from a stream where the final frame is a non-empty DATA frame with the END_STREAM flag set, instead of hanging waiting for another frame. When writing to a stream, uses a END_STREAM flag on final DATA frame instead of adding an empty DATA frame. BREAKING: http2 client now expects servers to properly support END_STREAM flag Fixes: #31309 Fixes: #33891 Refs: https://nghttp2.org/documentation/types.html#c.nghttp2_on_data_chunk_recv_callback Backport-PR-URL: #34845 PR-URL: #33875 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com>
Adds support for reading from a stream where the final frame is a non-empty DATA frame with the END_STREAM flag set, instead of hanging waiting for another frame. When writing to a stream, uses a END_STREAM flag on final DATA frame instead of adding an empty DATA frame. BREAKING: http2 client now expects servers to properly support END_STREAM flag Fixes: #31309 Fixes: #33891 Refs: https://nghttp2.org/documentation/types.html#c.nghttp2_on_data_chunk_recv_callback Backport-PR-URL: #34845 PR-URL: #33875 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com>
What steps will reproduce the bug?
Http2Stream.end('data');
How often does it reproduce? Is there a required condition?
It occurs as long as there are no trailers (which means always on
ClientHttp2Stream
)What is the expected behavior?
The
DATA
frame with the last piece of data should have theEND_STREAM
flag.What do you see instead?
node/src/node_http2.cc
Lines 2319 to 2326 in f46ca0f
The
NGHTTP2_DATA_FLAG_EOF
is only set if the outbound stream is no long writable (eg: on shutdown), but not if the internal (Writable
) stream has ended.Additional information
This is how
curl
, Chrome, and Safari send DATA frames, wherein the last DATA frame contains theEND_STREAM
flag, instead of sending an empty frame. It makes it more difficult to test http2 if nodeJS applies different logic. Performance-wise, it's a wasted packet.The text was updated successfully, but these errors were encountered: