aboutsummaryrefslogtreecommitdiff
path: root/test/parallel
diff options
context:
space:
mode:
authorAnatoli Papirovski <apapirovski@mac.com>2017-09-30 10:06:21 -0400
committerRuben Bridgewater <ruben@bridgewater.de>2017-10-01 20:14:10 -0300
commitccd3afc84303247b32c011fea7630d150f07849d (patch)
treef010f7b450075412a57e74c29f05a23e15d7d60e /test/parallel
parentdcd890a1355c8b4165d631155048051ddd2d83f3 (diff)
downloadandroid-node-v8-ccd3afc84303247b32c011fea7630d150f07849d.tar.gz
android-node-v8-ccd3afc84303247b32c011fea7630d150f07849d.tar.bz2
android-node-v8-ccd3afc84303247b32c011fea7630d150f07849d.zip
http2: adjust error emit in core, add tests
Use the ability of nextTick and setImmediate to pass arguments instead of creating closures or binding. Add tests that cover the vast majority of error emits. PR-URL: https://github.com/nodejs/node/pull/15586 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Diffstat (limited to 'test/parallel')
-rw-r--r--test/parallel/test-http2-info-headers-errors.js105
-rw-r--r--test/parallel/test-http2-info-headers.js9
-rw-r--r--test/parallel/test-http2-priority-errors.js109
-rw-r--r--test/parallel/test-http2-respond-errors.js104
-rw-r--r--test/parallel/test-http2-respond-file-errors.js31
-rw-r--r--test/parallel/test-http2-respond-file-fd-errors.js40
-rw-r--r--test/parallel/test-http2-respond-with-fd-errors.js109
-rw-r--r--test/parallel/test-http2-rststream-errors.js108
-rw-r--r--test/parallel/test-http2-server-push-stream-errors.js1
-rw-r--r--test/parallel/test-http2-shutdown-errors.js75
10 files changed, 681 insertions, 10 deletions
diff --git a/test/parallel/test-http2-info-headers-errors.js b/test/parallel/test-http2-info-headers-errors.js
new file mode 100644
index 0000000000..5e1c2d1fad
--- /dev/null
+++ b/test/parallel/test-http2-info-headers-errors.js
@@ -0,0 +1,105 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+const http2 = require('http2');
+const {
+ constants,
+ Http2Session,
+ nghttp2ErrorString
+} = process.binding('http2');
+
+// tests error handling within additionalHeaders
+// - NGHTTP2_ERR_NOMEM (should emit session error)
+// - every other NGHTTP2 error from binding (should emit stream error)
+
+const specificTestKeys = [
+ 'NGHTTP2_ERR_NOMEM'
+];
+
+const specificTests = [
+ {
+ ngError: constants.NGHTTP2_ERR_NOMEM,
+ error: {
+ code: 'ERR_OUTOFMEMORY',
+ type: Error,
+ message: 'Out of memory'
+ },
+ type: 'session'
+ }
+];
+
+const genericTests = Object.getOwnPropertyNames(constants)
+ .filter((key) => (
+ key.indexOf('NGHTTP2_ERR') === 0 && specificTestKeys.indexOf(key) < 0
+ ))
+ .map((key) => ({
+ ngError: constants[key],
+ error: {
+ code: 'ERR_HTTP2_ERROR',
+ type: Error,
+ message: nghttp2ErrorString(constants[key])
+ },
+ type: 'stream'
+ }));
+
+
+const tests = specificTests.concat(genericTests);
+
+let currentError;
+
+// mock sendHeaders because we only care about testing error handling
+Http2Session.prototype.sendHeaders = () => currentError.ngError;
+
+const server = http2.createServer();
+server.on('stream', common.mustCall((stream, headers) => {
+ const errorMustCall = common.expectsError(currentError.error);
+ const errorMustNotCall = common.mustNotCall(
+ `${currentError.error.code} should emit on ${currentError.type}`
+ );
+
+ if (currentError.type === 'stream') {
+ stream.session.on('error', errorMustNotCall);
+ stream.on('error', errorMustCall);
+ stream.on('error', common.mustCall(() => {
+ stream.respond();
+ stream.end();
+ }));
+ } else {
+ stream.session.once('error', errorMustCall);
+ stream.on('error', errorMustNotCall);
+ }
+
+ stream.additionalHeaders({ ':status': 100 });
+}, tests.length));
+
+server.listen(0, common.mustCall(() => runTest(tests.shift())));
+
+function runTest(test) {
+ const port = server.address().port;
+ const url = `http://localhost:${port}`;
+ const headers = {
+ ':path': '/',
+ ':method': 'POST',
+ ':scheme': 'http',
+ ':authority': `localhost:${port}`
+ };
+
+ const client = http2.connect(url);
+ const req = client.request(headers);
+
+ currentError = test;
+ req.resume();
+ req.end();
+
+ req.on('end', common.mustCall(() => {
+ client.destroy();
+
+ if (!tests.length) {
+ server.close();
+ } else {
+ runTest(tests.shift());
+ }
+ }));
+}
diff --git a/test/parallel/test-http2-info-headers.js b/test/parallel/test-http2-info-headers.js
index 1ef6f244e2..609f56e8b8 100644
--- a/test/parallel/test-http2-info-headers.js
+++ b/test/parallel/test-http2-info-headers.js
@@ -32,6 +32,15 @@ function onStream(stream, headers, flags) {
message: status101regex
}));
+ common.expectsError(
+ () => stream.additionalHeaders({ ':method': 'POST' }),
+ {
+ code: 'ERR_HTTP2_INVALID_PSEUDOHEADER',
+ type: Error,
+ message: '":method" is an invalid pseudoheader or is used incorrectly'
+ }
+ );
+
// Can send more than one
stream.additionalHeaders({ ':status': 100 });
stream.additionalHeaders({ ':status': 100 });
diff --git a/test/parallel/test-http2-priority-errors.js b/test/parallel/test-http2-priority-errors.js
new file mode 100644
index 0000000000..d29d2f72fa
--- /dev/null
+++ b/test/parallel/test-http2-priority-errors.js
@@ -0,0 +1,109 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+const http2 = require('http2');
+const {
+ constants,
+ Http2Session,
+ nghttp2ErrorString
+} = process.binding('http2');
+
+// tests error handling within priority
+// - NGHTTP2_ERR_NOMEM (should emit session error)
+// - every other NGHTTP2 error from binding (should emit stream error)
+
+const specificTestKeys = [
+ 'NGHTTP2_ERR_NOMEM'
+];
+
+const specificTests = [
+ {
+ ngError: constants.NGHTTP2_ERR_NOMEM,
+ error: {
+ code: 'ERR_OUTOFMEMORY',
+ type: Error,
+ message: 'Out of memory'
+ },
+ type: 'session'
+ }
+];
+
+const genericTests = Object.getOwnPropertyNames(constants)
+ .filter((key) => (
+ key.indexOf('NGHTTP2_ERR') === 0 && specificTestKeys.indexOf(key) < 0
+ ))
+ .map((key) => ({
+ ngError: constants[key],
+ error: {
+ code: 'ERR_HTTP2_ERROR',
+ type: Error,
+ message: nghttp2ErrorString(constants[key])
+ },
+ type: 'stream'
+ }));
+
+
+const tests = specificTests.concat(genericTests);
+
+let currentError;
+
+// mock submitPriority because we only care about testing error handling
+Http2Session.prototype.submitPriority = () => currentError.ngError;
+
+const server = http2.createServer();
+server.on('stream', common.mustCall((stream, headers) => {
+ const errorMustCall = common.expectsError(currentError.error);
+ const errorMustNotCall = common.mustNotCall(
+ `${currentError.error.code} should emit on ${currentError.type}`
+ );
+
+ if (currentError.type === 'stream') {
+ stream.session.on('error', errorMustNotCall);
+ stream.on('error', errorMustCall);
+ stream.on('error', common.mustCall(() => {
+ stream.respond();
+ stream.end();
+ }));
+ } else {
+ stream.session.once('error', errorMustCall);
+ stream.on('error', errorMustNotCall);
+ }
+
+ stream.priority({
+ parent: 0,
+ weight: 1,
+ exclusive: false
+ });
+}, tests.length));
+
+server.listen(0, common.mustCall(() => runTest(tests.shift())));
+
+function runTest(test) {
+ const port = server.address().port;
+ const url = `http://localhost:${port}`;
+ const headers = {
+ ':path': '/',
+ ':method': 'POST',
+ ':scheme': 'http',
+ ':authority': `localhost:${port}`
+ };
+
+ const client = http2.connect(url);
+ const req = client.request(headers);
+
+ currentError = test;
+ req.resume();
+ req.end();
+
+ req.on('end', common.mustCall(() => {
+ client.destroy();
+
+ if (!tests.length) {
+ server.close();
+ } else {
+ runTest(tests.shift());
+ }
+ }));
+}
diff --git a/test/parallel/test-http2-respond-errors.js b/test/parallel/test-http2-respond-errors.js
new file mode 100644
index 0000000000..4e2c39178e
--- /dev/null
+++ b/test/parallel/test-http2-respond-errors.js
@@ -0,0 +1,104 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+const http2 = require('http2');
+const {
+ constants,
+ Http2Session,
+ nghttp2ErrorString
+} = process.binding('http2');
+
+// tests error handling within respond
+// - NGHTTP2_ERR_NOMEM (should emit session error)
+// - every other NGHTTP2 error from binding (should emit stream error)
+
+const specificTestKeys = [
+ 'NGHTTP2_ERR_NOMEM'
+];
+
+const specificTests = [
+ {
+ ngError: constants.NGHTTP2_ERR_NOMEM,
+ error: {
+ code: 'ERR_OUTOFMEMORY',
+ type: Error,
+ message: 'Out of memory'
+ },
+ type: 'session'
+ }
+];
+
+const genericTests = Object.getOwnPropertyNames(constants)
+ .filter((key) => (
+ key.indexOf('NGHTTP2_ERR') === 0 && specificTestKeys.indexOf(key) < 0
+ ))
+ .map((key) => ({
+ ngError: constants[key],
+ error: {
+ code: 'ERR_HTTP2_ERROR',
+ type: Error,
+ message: nghttp2ErrorString(constants[key])
+ },
+ type: 'stream'
+ }));
+
+
+const tests = specificTests.concat(genericTests);
+
+let currentError;
+
+// mock submitResponse because we only care about testing error handling
+Http2Session.prototype.submitResponse = () => currentError.ngError;
+
+const server = http2.createServer();
+server.on('stream', common.mustCall((stream, headers) => {
+ const errorMustCall = common.expectsError(currentError.error);
+ const errorMustNotCall = common.mustNotCall(
+ `${currentError.error.code} should emit on ${currentError.type}`
+ );
+
+ if (currentError.type === 'stream') {
+ stream.session.on('error', errorMustNotCall);
+ stream.on('error', errorMustCall);
+ stream.on('error', common.mustCall(() => {
+ stream.destroy();
+ }));
+ } else {
+ stream.session.once('error', errorMustCall);
+ stream.on('error', errorMustNotCall);
+ }
+
+ stream.respond();
+}, tests.length));
+
+server.listen(0, common.mustCall(() => runTest(tests.shift())));
+
+function runTest(test) {
+ const port = server.address().port;
+ const url = `http://localhost:${port}`;
+ const headers = {
+ ':path': '/',
+ ':method': 'POST',
+ ':scheme': 'http',
+ ':authority': `localhost:${port}`
+ };
+
+ const client = http2.connect(url);
+ const req = client.request(headers);
+
+ currentError = test;
+ req.resume();
+ req.end();
+
+ req.on('end', common.mustCall(() => {
+ client.destroy();
+
+ if (!tests.length) {
+ server.close();
+ } else {
+ runTest(tests.shift());
+ }
+ }));
+}
diff --git a/test/parallel/test-http2-respond-file-errors.js b/test/parallel/test-http2-respond-file-errors.js
index d5f071b376..ac2e8f3498 100644
--- a/test/parallel/test-http2-respond-file-errors.js
+++ b/test/parallel/test-http2-respond-file-errors.js
@@ -6,6 +6,11 @@ if (!common.hasCrypto)
const http2 = require('http2');
const path = require('path');
+const {
+ HTTP2_HEADER_CONTENT_TYPE,
+ HTTP2_HEADER_METHOD
+} = http2.constants;
+
const optionsWithTypeError = {
offset: 'number',
length: 'number',
@@ -54,7 +59,7 @@ server.on('stream', common.mustCall((stream) => {
// Should throw if :status 204, 205 or 304
[204, 205, 304].forEach((status) => common.expectsError(
() => stream.respondWithFile(fname, {
- [http2.constants.HTTP2_HEADER_CONTENT_TYPE]: 'text/plain',
+ [HTTP2_HEADER_CONTENT_TYPE]: 'text/plain',
':status': status,
}),
{
@@ -63,13 +68,31 @@ server.on('stream', common.mustCall((stream) => {
}
));
+ // should emit an error on the stream if headers aren't valid
+ stream.respondWithFile(fname, {
+ [HTTP2_HEADER_METHOD]: 'POST'
+ }, {
+ statCheck: common.mustCall(() => {
+ // give time to the current test case to finish
+ process.nextTick(continueTest, stream);
+ return true;
+ })
+ });
+ stream.once('error', common.expectsError({
+ code: 'ERR_HTTP2_INVALID_PSEUDOHEADER',
+ type: Error,
+ message: '":method" is an invalid pseudoheader or is used incorrectly'
+ }));
+}));
+
+function continueTest(stream) {
// Should throw if headers already sent
stream.respond({
':status': 200,
});
common.expectsError(
() => stream.respondWithFile(fname, {
- [http2.constants.HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
+ [HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
}),
{
code: 'ERR_HTTP2_HEADERS_SENT',
@@ -81,14 +104,14 @@ server.on('stream', common.mustCall((stream) => {
stream.destroy();
common.expectsError(
() => stream.respondWithFile(fname, {
- [http2.constants.HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
+ [HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
}),
{
code: 'ERR_HTTP2_INVALID_STREAM',
message: 'The stream has been destroyed'
}
);
-}));
+}
server.listen(0, common.mustCall(() => {
const client = http2.connect(`http://localhost:${server.address().port}`);
diff --git a/test/parallel/test-http2-respond-file-fd-errors.js b/test/parallel/test-http2-respond-file-fd-errors.js
index faf37f3373..95c4b5fea6 100644
--- a/test/parallel/test-http2-respond-file-fd-errors.js
+++ b/test/parallel/test-http2-respond-file-fd-errors.js
@@ -7,6 +7,11 @@ const http2 = require('http2');
const path = require('path');
const fs = require('fs');
+const {
+ HTTP2_HEADER_CONTENT_TYPE,
+ HTTP2_HEADER_METHOD
+} = http2.constants;
+
const optionsWithTypeError = {
offset: 'number',
length: 'number',
@@ -38,7 +43,7 @@ server.on('stream', common.mustCall((stream) => {
common.expectsError(
() => stream.respondWithFD(types[type], {
- [http2.constants.HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
+ [HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
}),
{
type: TypeError,
@@ -57,7 +62,7 @@ server.on('stream', common.mustCall((stream) => {
common.expectsError(
() => stream.respondWithFD(fd, {
- [http2.constants.HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
+ [HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
}, {
[option]: types[type]
}),
@@ -74,25 +79,49 @@ server.on('stream', common.mustCall((stream) => {
// Should throw if :status 204, 205 or 304
[204, 205, 304].forEach((status) => common.expectsError(
() => stream.respondWithFD(fd, {
- [http2.constants.HTTP2_HEADER_CONTENT_TYPE]: 'text/plain',
+ [HTTP2_HEADER_CONTENT_TYPE]: 'text/plain',
':status': status,
}),
{
code: 'ERR_HTTP2_PAYLOAD_FORBIDDEN',
+ type: Error,
message: `Responses with ${status} status must not have a payload`
}
));
+ // should emit an error on the stream if headers aren't valid
+ stream.respondWithFD(fd, {
+ [HTTP2_HEADER_METHOD]: 'POST'
+ }, {
+ statCheck() {
+ return true;
+ }
+ });
+ stream.once('error', common.expectsError({
+ code: 'ERR_HTTP2_INVALID_PSEUDOHEADER',
+ type: Error,
+ message: '":method" is an invalid pseudoheader or is used incorrectly'
+ }));
+ stream.respondWithFD(fd, {
+ [HTTP2_HEADER_METHOD]: 'POST'
+ });
+ stream.once('error', common.expectsError({
+ code: 'ERR_HTTP2_INVALID_PSEUDOHEADER',
+ type: Error,
+ message: '":method" is an invalid pseudoheader or is used incorrectly'
+ }));
+
// Should throw if headers already sent
stream.respond({
':status': 200,
});
common.expectsError(
() => stream.respondWithFD(fd, {
- [http2.constants.HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
+ [HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
}),
{
code: 'ERR_HTTP2_HEADERS_SENT',
+ type: Error,
message: 'Response has already been initiated.'
}
);
@@ -101,10 +130,11 @@ server.on('stream', common.mustCall((stream) => {
stream.destroy();
common.expectsError(
() => stream.respondWithFD(fd, {
- [http2.constants.HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
+ [HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
}),
{
code: 'ERR_HTTP2_INVALID_STREAM',
+ type: Error,
message: 'The stream has been destroyed'
}
);
diff --git a/test/parallel/test-http2-respond-with-fd-errors.js b/test/parallel/test-http2-respond-with-fd-errors.js
new file mode 100644
index 0000000000..8ec0d9bf71
--- /dev/null
+++ b/test/parallel/test-http2-respond-with-fd-errors.js
@@ -0,0 +1,109 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+const http2 = require('http2');
+const path = require('path');
+
+const {
+ constants,
+ Http2Session,
+ nghttp2ErrorString
+} = process.binding('http2');
+
+// tests error handling within processRespondWithFD
+// (called by respondWithFD & respondWithFile)
+// - NGHTTP2_ERR_NOMEM (should emit session error)
+// - every other NGHTTP2 error from binding (should emit stream error)
+
+const fname = path.resolve(common.fixturesDir, 'elipses.txt');
+
+const specificTestKeys = [
+ 'NGHTTP2_ERR_NOMEM'
+];
+
+const specificTests = [
+ {
+ ngError: constants.NGHTTP2_ERR_NOMEM,
+ error: {
+ code: 'ERR_OUTOFMEMORY',
+ type: Error,
+ message: 'Out of memory'
+ },
+ type: 'session'
+ }
+];
+
+const genericTests = Object.getOwnPropertyNames(constants)
+ .filter((key) => (
+ key.indexOf('NGHTTP2_ERR') === 0 && specificTestKeys.indexOf(key) < 0
+ ))
+ .map((key) => ({
+ ngError: constants[key],
+ error: {
+ code: 'ERR_HTTP2_ERROR',
+ type: Error,
+ message: nghttp2ErrorString(constants[key])
+ },
+ type: 'stream'
+ }));
+
+
+const tests = specificTests.concat(genericTests);
+
+let currentError;
+
+// mock submitFile because we only care about testing error handling
+Http2Session.prototype.submitFile = () => currentError.ngError;
+
+const server = http2.createServer();
+server.on('stream', common.mustCall((stream, headers) => {
+ const errorMustCall = common.expectsError(currentError.error);
+ const errorMustNotCall = common.mustNotCall(
+ `${currentError.error.code} should emit on ${currentError.type}`
+ );
+
+ if (currentError.type === 'stream') {
+ stream.session.on('error', errorMustNotCall);
+ stream.on('error', errorMustCall);
+ stream.on('error', common.mustCall(() => {
+ stream.destroy();
+ }));
+ } else {
+ stream.session.once('error', errorMustCall);
+ stream.on('error', errorMustNotCall);
+ }
+
+ stream.respondWithFile(fname);
+}, tests.length));
+
+server.listen(0, common.mustCall(() => runTest(tests.shift())));
+
+function runTest(test) {
+ const port = server.address().port;
+ const url = `http://localhost:${port}`;
+ const headers = {
+ ':path': '/',
+ ':method': 'POST',
+ ':scheme': 'http',
+ ':authority': `localhost:${port}`
+ };
+
+ const client = http2.connect(url);
+ const req = client.request(headers);
+
+ currentError = test;
+ req.resume();
+ req.end();
+
+ req.on('end', common.mustCall(() => {
+ client.destroy();
+
+ if (!tests.length) {
+ server.close();
+ } else {
+ runTest(tests.shift());
+ }
+ }));
+}
diff --git a/test/parallel/test-http2-rststream-errors.js b/test/parallel/test-http2-rststream-errors.js
new file mode 100644
index 0000000000..58d4440f2e
--- /dev/null
+++ b/test/parallel/test-http2-rststream-errors.js
@@ -0,0 +1,108 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+const http2 = require('http2');
+const {
+ constants,
+ Http2Session,
+ nghttp2ErrorString
+} = process.binding('http2');
+
+// tests error handling within rstStream
+// - NGHTTP2_ERR_NOMEM (should emit session error)
+// - every other NGHTTP2 error from binding (should emit stream error)
+
+const specificTestKeys = [
+ 'NGHTTP2_ERR_NOMEM'
+];
+
+const specificTests = [
+ {
+ ngError: constants.NGHTTP2_ERR_NOMEM,
+ error: {
+ code: 'ERR_OUTOFMEMORY',
+ type: Error,
+ message: 'Out of memory'
+ },
+ type: 'session'
+ }
+];
+
+const genericTests = Object.getOwnPropertyNames(constants)
+ .filter((key) => (
+ key.indexOf('NGHTTP2_ERR') === 0 && specificTestKeys.indexOf(key) < 0
+ ))
+ .map((key) => ({
+ ngError: constants[key],
+ error: {
+ code: 'ERR_HTTP2_ERROR',
+ type: Error,
+ message: nghttp2ErrorString(constants[key])
+ },
+ type: 'stream'
+ }));
+
+
+const tests = specificTests.concat(genericTests);
+
+let currentError;
+
+// mock submitRstStream because we only care about testing error handling
+Http2Session.prototype.submitRstStream = () => currentError.ngError;
+
+const server = http2.createServer();
+server.on('stream', common.mustCall((stream, headers) => {
+ const errorMustCall = common.expectsError(currentError.error);
+ const errorMustNotCall = common.mustNotCall(
+ `${currentError.error.code} should emit on ${currentError.type}`
+ );
+
+ if (currentError.type === 'stream') {
+ stream.session.on('error', errorMustNotCall);
+ stream.on('error', errorMustCall);
+ stream.on('error', common.mustCall(() => {
+ stream.session.destroy();
+ }));
+ } else {
+ stream.session.once('error', errorMustCall);
+ stream.on('error', errorMustNotCall);
+ }
+
+ stream.rstStream();
+}, tests.length));
+
+server.listen(0, common.mustCall(() => runTest(tests.shift())));
+
+function runTest(test) {
+ const port = server.address().port;
+ const url = `http://localhost:${port}`;
+ const headers = {
+ ':path': '/',
+ ':method': 'POST',
+ ':scheme': 'http',
+ ':authority': `localhost:${port}`
+ };
+
+ const client = http2.connect(url);
+ const req = client.request(headers);
+
+ currentError = test;
+ req.resume();
+ req.end();
+
+ if (currentError.type === 'stream') {
+ req.on('error', common.mustCall());
+ }
+
+ req.on('end', common.mustCall(() => {
+ client.destroy();
+
+ if (!tests.length) {
+ server.close();
+ } else {
+ runTest(tests.shift());
+ }
+ }));
+}
diff --git a/test/parallel/test-http2-server-push-stream-errors.js b/test/parallel/test-http2-server-push-stream-errors.js
index 42d58c2ca1..777b20eb3f 100644
--- a/test/parallel/test-http2-server-push-stream-errors.js
+++ b/test/parallel/test-http2-server-push-stream-errors.js
@@ -81,7 +81,6 @@ server.on('stream', common.mustCall((stream, headers) => {
const errorMustNotCall = common.mustNotCall(
`${currentError.error.code} should emit on ${currentError.type}`
);
- console.log(currentError);
if (currentError.type === 'stream') {
stream.session.on('error', errorMustNotCall);
diff --git a/test/parallel/test-http2-shutdown-errors.js b/test/parallel/test-http2-shutdown-errors.js
new file mode 100644
index 0000000000..99ae791767
--- /dev/null
+++ b/test/parallel/test-http2-shutdown-errors.js
@@ -0,0 +1,75 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+const http2 = require('http2');
+const {
+ constants,
+ Http2Session,
+ nghttp2ErrorString
+} = process.binding('http2');
+
+// tests error handling within shutdown
+// - should emit ERR_HTTP2_ERROR on session for all errors
+
+const tests = Object.getOwnPropertyNames(constants)
+ .filter((key) => (
+ key.indexOf('NGHTTP2_ERR') === 0
+ ))
+ .map((key) => ({
+ ngError: constants[key],
+ error: {
+ code: 'ERR_HTTP2_ERROR',
+ type: Error,
+ message: nghttp2ErrorString(constants[key])
+ }
+ }));
+
+let currentError;
+
+// mock submitGoaway because we only care about testing error handling
+Http2Session.prototype.submitGoaway = () => currentError.ngError;
+
+const server = http2.createServer();
+server.on('stream', common.mustCall((stream, headers) => {
+ const errorMustCall = common.expectsError(currentError.error);
+ const errorMustNotCall = common.mustNotCall(
+ `${currentError.error.code} should emit on session`
+ );
+
+ stream.session.once('error', errorMustCall);
+ stream.on('error', errorMustNotCall);
+
+ stream.session.shutdown();
+}, tests.length));
+
+server.listen(0, common.mustCall(() => runTest(tests.shift())));
+
+function runTest(test) {
+ const port = server.address().port;
+ const url = `http://localhost:${port}`;
+ const headers = {
+ ':path': '/',
+ ':method': 'POST',
+ ':scheme': 'http',
+ ':authority': `localhost:${port}`
+ };
+
+ const client = http2.connect(url);
+ const req = client.request(headers);
+
+ currentError = test;
+ req.resume();
+ req.end();
+
+ req.on('end', common.mustCall(() => {
+ client.destroy();
+
+ if (!tests.length) {
+ server.close();
+ } else {
+ runTest(tests.shift());
+ }
+ }));
+}