summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/api/http2.md11
-rw-r--r--lib/internal/http2/core.js9
-rw-r--r--test/parallel/test-http2-endafterheaders.js50
3 files changed, 69 insertions, 1 deletions
diff --git a/doc/api/http2.md b/doc/api/http2.md
index 7aec57e111..feeb66aa88 100644
--- a/doc/api/http2.md
+++ b/doc/api/http2.md
@@ -958,6 +958,17 @@ added: v8.4.0
Set to `true` if the `Http2Stream` instance has been destroyed and is no longer
usable.
+#### http2stream.endAfterHeaders
+<!-- YAML
+added: REPLACEME
+-->
+
+* {boolean}
+
+Set the `true` if the `END_STREAM` flag was set in the request or response
+HEADERS frame received, indicating that no additional data should be received
+and the readable side of the `Http2Stream` will be closed.
+
#### http2stream.pending
<!-- YAML
added: v9.4.0
diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js
index f2a298b6e5..0b9be932af 100644
--- a/lib/internal/http2/core.js
+++ b/lib/internal/http2/core.js
@@ -276,6 +276,8 @@ function onSessionHeaders(handle, id, cat, flags, headers) {
} else {
stream = new ClientHttp2Stream(session, handle, id, opts);
}
+ if (endOfStream)
+ stream[kState].endAfterHeaders = true;
process.nextTick(emit, session, 'stream', stream, obj, flags, headers);
} else {
let event;
@@ -1548,7 +1550,8 @@ class Http2Stream extends Duplex {
flags: STREAM_FLAGS_PENDING,
rstCode: NGHTTP2_NO_ERROR,
writeQueueSize: 0,
- trailersReady: false
+ trailersReady: false,
+ endAfterHeaders: false
};
this.on('pause', streamOnPause);
@@ -1594,6 +1597,10 @@ class Http2Stream extends Duplex {
return `Http2Stream ${util.format(obj)}`;
}
+ get endAfterHeaders() {
+ return this[kState].endAfterHeaders;
+ }
+
get sentHeaders() {
return this[kSentHeaders];
}
diff --git a/test/parallel/test-http2-endafterheaders.js b/test/parallel/test-http2-endafterheaders.js
new file mode 100644
index 0000000000..429ffc3188
--- /dev/null
+++ b/test/parallel/test-http2-endafterheaders.js
@@ -0,0 +1,50 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+const assert = require('assert');
+const http2 = require('http2');
+const Countdown = require('../common/countdown');
+
+const server = http2.createServer();
+server.on('stream', common.mustCall((stream, headers) => {
+ const check = headers[':method'] === 'GET' ? true : false;
+ assert.strictEqual(stream.endAfterHeaders, check);
+ stream.on('data', common.mustNotCall());
+ stream.on('end', common.mustCall());
+ stream.respond();
+ stream.end('ok');
+}, 2));
+
+const countdown = new Countdown(2, () => server.close());
+
+server.listen(0, common.mustCall(() => {
+ {
+ const client = http2.connect(`http://localhost:${server.address().port}`);
+ const req = client.request();
+
+ req.resume();
+ req.on('response', common.mustCall(() => {
+ assert.strictEqual(req.endAfterHeaders, false);
+ }));
+ req.on('end', common.mustCall(() => {
+ client.close();
+ countdown.dec();
+ }));
+ }
+ {
+ const client = http2.connect(`http://localhost:${server.address().port}`);
+ const req = client.request({ ':method': 'POST' });
+
+ req.resume();
+ req.end();
+ req.on('response', common.mustCall(() => {
+ assert.strictEqual(req.endAfterHeaders, false);
+ }));
+ req.on('end', common.mustCall(() => {
+ client.close();
+ countdown.dec();
+ }));
+ }
+}));