diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index 010bec55451c83..a159b88ae84d50 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -62,7 +62,7 @@ const { } = require('timers'); const { ShutdownWrap, WriteWrap } = process.binding('stream_wrap'); -const { constants } = binding; +const { constants, nameForErrorCode } = binding; const NETServer = net.Server; const TLSServer = tls.Server; @@ -1841,7 +1841,8 @@ class Http2Stream extends Duplex { // abort and is already covered by aborted event, also allows more // seamless compatibility with http1 if (err == null && code !== NGHTTP2_NO_ERROR && code !== NGHTTP2_CANCEL) - err = new errors.Error('ERR_HTTP2_STREAM_ERROR', code); + err = new errors.Error('ERR_HTTP2_STREAM_ERROR', + nameForErrorCode[code] || code); this[kSession] = undefined; this[kHandle] = undefined; diff --git a/src/node_http2.cc b/src/node_http2.cc index a11a0bc87b82b1..67c5c67982a178 100644 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -2857,29 +2857,39 @@ void Initialize(Local target, session->GetFunction()).FromJust(); Local constants = Object::New(isolate); - NODE_DEFINE_CONSTANT(constants, NGHTTP2_SESSION_SERVER); - NODE_DEFINE_CONSTANT(constants, NGHTTP2_SESSION_CLIENT); - NODE_DEFINE_CONSTANT(constants, NGHTTP2_STREAM_STATE_IDLE); - NODE_DEFINE_CONSTANT(constants, NGHTTP2_STREAM_STATE_OPEN); - NODE_DEFINE_CONSTANT(constants, NGHTTP2_STREAM_STATE_RESERVED_LOCAL); - NODE_DEFINE_CONSTANT(constants, NGHTTP2_STREAM_STATE_RESERVED_REMOTE); - NODE_DEFINE_CONSTANT(constants, NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL); - NODE_DEFINE_CONSTANT(constants, NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE); - NODE_DEFINE_CONSTANT(constants, NGHTTP2_STREAM_STATE_CLOSED); - NODE_DEFINE_CONSTANT(constants, NGHTTP2_NO_ERROR); - NODE_DEFINE_CONSTANT(constants, NGHTTP2_PROTOCOL_ERROR); - NODE_DEFINE_CONSTANT(constants, NGHTTP2_INTERNAL_ERROR); - NODE_DEFINE_CONSTANT(constants, NGHTTP2_FLOW_CONTROL_ERROR); - NODE_DEFINE_CONSTANT(constants, NGHTTP2_SETTINGS_TIMEOUT); - NODE_DEFINE_CONSTANT(constants, NGHTTP2_STREAM_CLOSED); - NODE_DEFINE_CONSTANT(constants, NGHTTP2_FRAME_SIZE_ERROR); - NODE_DEFINE_CONSTANT(constants, NGHTTP2_REFUSED_STREAM); - NODE_DEFINE_CONSTANT(constants, NGHTTP2_CANCEL); - NODE_DEFINE_CONSTANT(constants, NGHTTP2_COMPRESSION_ERROR); - NODE_DEFINE_CONSTANT(constants, NGHTTP2_CONNECT_ERROR); - NODE_DEFINE_CONSTANT(constants, NGHTTP2_ENHANCE_YOUR_CALM); - NODE_DEFINE_CONSTANT(constants, NGHTTP2_INADEQUATE_SECURITY); - NODE_DEFINE_CONSTANT(constants, NGHTTP2_HTTP_1_1_REQUIRED); + Local name_for_error_code = Array::New(isolate); + +#define NODE_NGHTTP2_ERROR_CODES(V) \ + V(NGHTTP2_SESSION_SERVER); \ + V(NGHTTP2_SESSION_CLIENT); \ + V(NGHTTP2_STREAM_STATE_IDLE); \ + V(NGHTTP2_STREAM_STATE_OPEN); \ + V(NGHTTP2_STREAM_STATE_RESERVED_LOCAL); \ + V(NGHTTP2_STREAM_STATE_RESERVED_REMOTE); \ + V(NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL); \ + V(NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE); \ + V(NGHTTP2_STREAM_STATE_CLOSED); \ + V(NGHTTP2_NO_ERROR); \ + V(NGHTTP2_PROTOCOL_ERROR); \ + V(NGHTTP2_INTERNAL_ERROR); \ + V(NGHTTP2_FLOW_CONTROL_ERROR); \ + V(NGHTTP2_SETTINGS_TIMEOUT); \ + V(NGHTTP2_STREAM_CLOSED); \ + V(NGHTTP2_FRAME_SIZE_ERROR); \ + V(NGHTTP2_REFUSED_STREAM); \ + V(NGHTTP2_CANCEL); \ + V(NGHTTP2_COMPRESSION_ERROR); \ + V(NGHTTP2_CONNECT_ERROR); \ + V(NGHTTP2_ENHANCE_YOUR_CALM); \ + V(NGHTTP2_INADEQUATE_SECURITY); \ + V(NGHTTP2_HTTP_1_1_REQUIRED); \ + +#define V(name) \ + NODE_DEFINE_CONSTANT(constants, name); \ + name_for_error_code->Set(static_cast(name), \ + FIXED_ONE_BYTE_STRING(isolate, #name)); + NODE_NGHTTP2_ERROR_CODES(V) +#undef V NODE_DEFINE_HIDDEN_CONSTANT(constants, NGHTTP2_HCAT_REQUEST); NODE_DEFINE_HIDDEN_CONSTANT(constants, NGHTTP2_HCAT_RESPONSE); @@ -2944,6 +2954,9 @@ HTTP_STATUS_CODES(V) target->Set(context, FIXED_ONE_BYTE_STRING(isolate, "constants"), constants).FromJust(); + target->Set(context, + FIXED_ONE_BYTE_STRING(isolate, "nameForErrorCode"), + name_for_error_code).FromJust(); } } // namespace http2 } // namespace node diff --git a/test/common/index.js b/test/common/index.js index 590ffb7b00c2ba..b24d2158e7d089 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -689,6 +689,11 @@ exports.expectsError = function expectsError(fn, settings, exact) { } assert(error instanceof type, `${error.name} is not instance of ${type.name}`); + let typeName = error.constructor.name; + if (typeName === 'NodeError' && type.name !== 'NodeError') { + typeName = Object.getPrototypeOf(error.constructor).name; + } + assert.strictEqual(typeName, type.name); } if ('message' in settings) { const message = settings.message; diff --git a/test/parallel/test-crypto-sign-verify.js b/test/parallel/test-crypto-sign-verify.js index d105b5b2bb27e3..7619d91191617b 100644 --- a/test/parallel/test-crypto-sign-verify.js +++ b/test/parallel/test-crypto-sign-verify.js @@ -36,7 +36,7 @@ common.expectsError( }, ''), { code: 'ERR_INVALID_OPT_VALUE', - type: Error, + type: TypeError, message: 'The value "undefined" is invalid for option "padding"' }); @@ -47,7 +47,7 @@ common.expectsError( }, ''), { code: 'ERR_INVALID_OPT_VALUE', - type: Error, + type: TypeError, message: 'The value "undefined" is invalid for option "saltLength"' }); diff --git a/test/parallel/test-http2-client-http1-server.js b/test/parallel/test-http2-client-http1-server.js index 616427b3904e16..c7535adcef2381 100644 --- a/test/parallel/test-http2-client-http1-server.js +++ b/test/parallel/test-http2-client-http1-server.js @@ -1,4 +1,5 @@ 'use strict'; +// Flags: --expose-internals const common = require('../common'); if (!common.hasCrypto) @@ -6,6 +7,7 @@ if (!common.hasCrypto) const http = require('http'); const http2 = require('http2'); +const { NghttpError } = require('internal/http2/util'); // Creating an http1 server here... const server = http.createServer(common.mustNotCall()); @@ -18,13 +20,14 @@ server.listen(0, common.mustCall(() => { req.on('error', common.expectsError({ code: 'ERR_HTTP2_ERROR', - type: Error, + type: NghttpError, message: 'Protocol error' })); client.on('error', common.expectsError({ code: 'ERR_HTTP2_ERROR', - type: Error, + type: NghttpError, + name: 'Error [ERR_HTTP2_ERROR]', message: 'Protocol error' })); diff --git a/test/parallel/test-http2-client-onconnect-errors.js b/test/parallel/test-http2-client-onconnect-errors.js index af67a0d0ae27db..a75dc590c669a1 100644 --- a/test/parallel/test-http2-client-onconnect-errors.js +++ b/test/parallel/test-http2-client-onconnect-errors.js @@ -1,4 +1,5 @@ 'use strict'; +// Flags: --expose-internals const common = require('../common'); if (!common.hasCrypto) @@ -10,6 +11,7 @@ const { nghttp2ErrorString } = process.binding('http2'); const http2 = require('http2'); +const { NghttpError } = require('internal/http2/util'); // tests error handling within requestOnConnect // - NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE (should emit session error) @@ -51,7 +53,8 @@ const genericTests = Object.getOwnPropertyNames(constants) ngError: constants[key], error: { code: 'ERR_HTTP2_ERROR', - type: Error, + type: NghttpError, + name: 'Error [ERR_HTTP2_ERROR]', message: nghttp2ErrorString(constants[key]) }, type: 'session' diff --git a/test/parallel/test-http2-client-rststream-before-connect.js b/test/parallel/test-http2-client-rststream-before-connect.js index 10d7520ac125fe..57a9580f9407d7 100644 --- a/test/parallel/test-http2-client-rststream-before-connect.js +++ b/test/parallel/test-http2-client-rststream-before-connect.js @@ -16,18 +16,30 @@ server.on('stream', (stream) => { server.listen(0, common.mustCall(() => { const client = h2.connect(`http://localhost:${server.address().port}`); const req = client.request(); - req.close(1); + const closeCode = 1; + + common.expectsError( + () => req.close(2 ** 32), + { + type: RangeError, + code: 'ERR_OUT_OF_RANGE', + message: 'The "code" argument is out of range' + } + ); + assert.strictEqual(req.closed, false); + + req.close(closeCode, common.mustCall()); assert.strictEqual(req.closed, true); // Make sure that destroy is called. req._destroy = common.mustCall(req._destroy.bind(req)); // Second call doesn't do anything. - req.close(8); + req.close(closeCode + 1); req.on('close', common.mustCall((code) => { assert.strictEqual(req.destroyed, true); - assert.strictEqual(code, 1); + assert.strictEqual(code, closeCode); server.close(); client.close(); })); @@ -35,7 +47,7 @@ server.listen(0, common.mustCall(() => { req.on('error', common.expectsError({ code: 'ERR_HTTP2_STREAM_ERROR', type: Error, - message: 'Stream closed with error code 1' + message: 'Stream closed with error code NGHTTP2_PROTOCOL_ERROR' })); req.on('response', common.mustCall()); diff --git a/test/parallel/test-http2-client-stream-destroy-before-connect.js b/test/parallel/test-http2-client-stream-destroy-before-connect.js index a2412b9f1d646a..884a38fac6f71d 100644 --- a/test/parallel/test-http2-client-stream-destroy-before-connect.js +++ b/test/parallel/test-http2-client-stream-destroy-before-connect.js @@ -18,7 +18,8 @@ server.on('stream', (stream) => { // system specific timings. stream.on('error', (err) => { assert.strictEqual(err.code, 'ERR_HTTP2_STREAM_ERROR'); - assert.strictEqual(err.message, 'Stream closed with error code 2'); + assert.strictEqual(err.message, + 'Stream closed with error code NGHTTP2_INTERNAL_ERROR'); }); stream.respond(); stream.end(); diff --git a/test/parallel/test-http2-client-unescaped-path.js b/test/parallel/test-http2-client-unescaped-path.js index 190f8ce75e8917..ff122a02ca1a68 100644 --- a/test/parallel/test-http2-client-unescaped-path.js +++ b/test/parallel/test-http2-client-unescaped-path.js @@ -27,7 +27,7 @@ server.listen(0, common.mustCall(() => { req.on('error', common.expectsError({ code: 'ERR_HTTP2_STREAM_ERROR', type: Error, - message: 'Stream closed with error code 1' + message: 'Stream closed with error code NGHTTP2_PROTOCOL_ERROR' })); req.on('close', common.mustCall(() => countdown.dec())); } diff --git a/test/parallel/test-http2-compat-serverresponse-destroy.js b/test/parallel/test-http2-compat-serverresponse-destroy.js index f890346dbf4303..8ee52a74ab4e81 100644 --- a/test/parallel/test-http2-compat-serverresponse-destroy.js +++ b/test/parallel/test-http2-compat-serverresponse-destroy.js @@ -58,7 +58,7 @@ server.listen(0, common.mustCall(() => { req.on('error', common.expectsError({ code: 'ERR_HTTP2_STREAM_ERROR', type: Error, - message: 'Stream closed with error code 2' + message: 'Stream closed with error code NGHTTP2_INTERNAL_ERROR' })); req.on('close', common.mustCall(() => countdown.dec())); @@ -73,7 +73,7 @@ server.listen(0, common.mustCall(() => { req.on('error', common.expectsError({ code: 'ERR_HTTP2_STREAM_ERROR', type: Error, - message: 'Stream closed with error code 2' + message: 'Stream closed with error code NGHTTP2_INTERNAL_ERROR' })); req.on('close', common.mustCall(() => countdown.dec())); diff --git a/test/parallel/test-http2-info-headers-errors.js b/test/parallel/test-http2-info-headers-errors.js index 1df76334558529..437add098b8a9f 100644 --- a/test/parallel/test-http2-info-headers-errors.js +++ b/test/parallel/test-http2-info-headers-errors.js @@ -1,4 +1,5 @@ 'use strict'; +// Flags: --expose-internals const common = require('../common'); if (!common.hasCrypto) @@ -9,6 +10,7 @@ const { Http2Stream, nghttp2ErrorString } = process.binding('http2'); +const { NghttpError } = require('internal/http2/util'); // tests error handling within additionalHeaders // - every other NGHTTP2 error from binding (should emit stream error) @@ -24,7 +26,8 @@ const genericTests = Object.getOwnPropertyNames(constants) ngError: constants[key], error: { code: 'ERR_HTTP2_ERROR', - type: Error, + type: NghttpError, + name: 'Error [ERR_HTTP2_ERROR]', message: nghttp2ErrorString(constants[key]) }, type: 'stream' @@ -69,7 +72,7 @@ function runTest(test) { req.on('error', common.expectsError({ code: 'ERR_HTTP2_STREAM_ERROR', type: Error, - message: 'Stream closed with error code 2' + message: 'Stream closed with error code NGHTTP2_INTERNAL_ERROR' })); req.on('close', common.mustCall(() => { diff --git a/test/parallel/test-http2-max-concurrent-streams.js b/test/parallel/test-http2-max-concurrent-streams.js index ffc04e98f134b2..b270d6cc6aff31 100644 --- a/test/parallel/test-http2-max-concurrent-streams.js +++ b/test/parallel/test-http2-max-concurrent-streams.js @@ -50,7 +50,7 @@ server.listen(0, common.mustCall(() => { req.on('error', common.expectsError({ code: 'ERR_HTTP2_STREAM_ERROR', type: Error, - message: 'Stream closed with error code 7' + message: 'Stream closed with error code NGHTTP2_REFUSED_STREAM' })); } })); diff --git a/test/parallel/test-http2-misbehaving-flow-control-paused.js b/test/parallel/test-http2-misbehaving-flow-control-paused.js index d69e0fd802979a..60a2cdabf847d9 100644 --- a/test/parallel/test-http2-misbehaving-flow-control-paused.js +++ b/test/parallel/test-http2-misbehaving-flow-control-paused.js @@ -63,7 +63,7 @@ server.on('stream', (stream) => { stream.on('error', common.expectsError({ code: 'ERR_HTTP2_STREAM_ERROR', type: Error, - message: 'Stream closed with error code 3' + message: 'Stream closed with error code NGHTTP2_FLOW_CONTROL_ERROR' })); stream.on('close', common.mustCall(() => { server.close(); diff --git a/test/parallel/test-http2-misbehaving-flow-control.js b/test/parallel/test-http2-misbehaving-flow-control.js index 161a88ea1fb407..f2da0ba56c8e67 100644 --- a/test/parallel/test-http2-misbehaving-flow-control.js +++ b/test/parallel/test-http2-misbehaving-flow-control.js @@ -69,7 +69,7 @@ server.on('stream', (stream) => { stream.on('error', common.expectsError({ code: 'ERR_HTTP2_STREAM_ERROR', type: Error, - message: 'Stream closed with error code 3' + message: 'Stream closed with error code NGHTTP2_FLOW_CONTROL_ERROR' })); stream.on('close', common.mustCall(() => { server.close(common.mustCall()); diff --git a/test/parallel/test-http2-misbehaving-multiplex.js b/test/parallel/test-http2-misbehaving-multiplex.js index 757e66b100e435..96010308515039 100644 --- a/test/parallel/test-http2-misbehaving-multiplex.js +++ b/test/parallel/test-http2-misbehaving-multiplex.js @@ -1,4 +1,5 @@ 'use strict'; +// Flags: --expose-internals const common = require('../common'); @@ -7,6 +8,7 @@ if (!common.hasCrypto) const h2 = require('http2'); const net = require('net'); +const { NghttpError } = require('internal/http2/util'); const h2test = require('../common/http2'); let client; @@ -25,7 +27,7 @@ server.on('stream', common.mustCall((stream) => { server.on('session', common.mustCall((session) => { session.on('error', common.expectsError({ code: 'ERR_HTTP2_ERROR', - type: Error, + type: NghttpError, message: 'Stream was already closed or invalid' })); })); diff --git a/test/parallel/test-http2-misused-pseudoheaders.js b/test/parallel/test-http2-misused-pseudoheaders.js index b47462b14fd0e1..e9253baa74ad1d 100644 --- a/test/parallel/test-http2-misused-pseudoheaders.js +++ b/test/parallel/test-http2-misused-pseudoheaders.js @@ -41,7 +41,7 @@ server.listen(0, common.mustCall(() => { req.on('error', common.expectsError({ code: 'ERR_HTTP2_STREAM_ERROR', type: Error, - message: 'Stream closed with error code 2' + message: 'Stream closed with error code NGHTTP2_INTERNAL_ERROR' })); req.on('response', common.mustCall()); diff --git a/test/parallel/test-http2-multi-content-length.js b/test/parallel/test-http2-multi-content-length.js index 4d18356f127da0..c64b803a0ecca3 100644 --- a/test/parallel/test-http2-multi-content-length.js +++ b/test/parallel/test-http2-multi-content-length.js @@ -58,7 +58,7 @@ server.listen(0, common.mustCall(() => { req.on('error', common.expectsError({ code: 'ERR_HTTP2_STREAM_ERROR', type: Error, - message: 'Stream closed with error code 1' + message: 'Stream closed with error code NGHTTP2_PROTOCOL_ERROR' })); } })); diff --git a/test/parallel/test-http2-options-max-headers-block-length.js b/test/parallel/test-http2-options-max-headers-block-length.js index a728c28c6576d4..0706e6a83e6d48 100644 --- a/test/parallel/test-http2-options-max-headers-block-length.js +++ b/test/parallel/test-http2-options-max-headers-block-length.js @@ -38,6 +38,6 @@ server.listen(0, common.mustCall(() => { req.on('error', common.expectsError({ code: 'ERR_HTTP2_STREAM_ERROR', type: Error, - message: 'Stream closed with error code 7' + message: 'Stream closed with error code NGHTTP2_REFUSED_STREAM' })); })); diff --git a/test/parallel/test-http2-respond-errors.js b/test/parallel/test-http2-respond-errors.js index 2a48456c9394a4..656a27df26edb5 100644 --- a/test/parallel/test-http2-respond-errors.js +++ b/test/parallel/test-http2-respond-errors.js @@ -1,96 +1,85 @@ 'use strict'; +// Flags: --expose-internals const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); const http2 = require('http2'); -const { - constants, - Http2Stream, - nghttp2ErrorString -} = process.binding('http2'); +const { Http2Stream } = process.binding('http2'); + +const types = { + boolean: true, + function: () => {}, + number: 1, + object: {}, + array: [], + null: null, + symbol: Symbol('test') +}; -// tests error handling within respond -// - every other NGHTTP2 error from binding (should emit stream error) +const server = http2.createServer(); -const specificTestKeys = []; +Http2Stream.prototype.respond = () => 1; +server.on('stream', common.mustCall((stream) => { -const specificTests = []; + // Check for all possible TypeError triggers on options.getTrailers + Object.entries(types).forEach(([type, value]) => { + if (type === 'function') { + return; + } -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', + common.expectsError( + () => stream.respond({ + 'content-type': 'text/plain' + }, { + ['getTrailers']: value + }), + { + type: TypeError, + code: 'ERR_INVALID_OPT_VALUE', + message: `The value "${String(value)}" is invalid ` + + 'for option "getTrailers"' + } + ); + }); + + // Send headers + stream.respond({ + 'content-type': 'text/plain' + }, { + ['getTrailers']: () => common.mustCall() + }); + + // Should throw if headers already sent + common.expectsError( + () => stream.respond(), + { 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 -Http2Stream.prototype.respond = () => 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}` + code: 'ERR_HTTP2_HEADERS_SENT', + message: 'Response has already been initiated.' + } ); - 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); - req.on('error', common.expectsError({ - code: 'ERR_HTTP2_STREAM_ERROR', - type: Error, - message: 'Stream closed with error code 2' - })); + // Should throw if stream already destroyed + stream.destroy(); + common.expectsError( + () => stream.respond(), + { + type: Error, + code: 'ERR_HTTP2_INVALID_STREAM', + message: 'The stream has been destroyed' + } + ); +})); - currentError = test; - req.resume(); - req.end(); +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request(); req.on('end', common.mustCall(() => { client.close(); - - if (!tests.length) { - server.close(); - } else { - runTest(tests.shift()); - } + server.close(); })); -} + req.resume(); + req.end(); +})); diff --git a/test/parallel/test-http2-respond-file-fd-invalid.js b/test/parallel/test-http2-respond-file-fd-invalid.js index 5dbb42e4788a7a..21fcf790b449eb 100644 --- a/test/parallel/test-http2-respond-file-fd-invalid.js +++ b/test/parallel/test-http2-respond-file-fd-invalid.js @@ -15,7 +15,7 @@ const { const errorCheck = common.expectsError({ code: 'ERR_HTTP2_STREAM_ERROR', type: Error, - message: `Stream closed with error code ${NGHTTP2_INTERNAL_ERROR}` + message: 'Stream closed with error code NGHTTP2_INTERNAL_ERROR' }, 2); const server = http2.createServer(); diff --git a/test/parallel/test-http2-respond-nghttperrors.js b/test/parallel/test-http2-respond-nghttperrors.js new file mode 100644 index 00000000000000..ad9eee0d59fecc --- /dev/null +++ b/test/parallel/test-http2-respond-nghttperrors.js @@ -0,0 +1,99 @@ +'use strict'; +// Flags: --expose-internals + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const http2 = require('http2'); +const { + constants, + Http2Stream, + nghttp2ErrorString +} = process.binding('http2'); +const { NghttpError } = require('internal/http2/util'); + +// tests error handling within respond +// - every other NGHTTP2 error from binding (should emit stream error) + +const specificTestKeys = []; + +const specificTests = []; + +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: NghttpError, + name: 'Error [ERR_HTTP2_ERROR]', + message: nghttp2ErrorString(constants[key]) + }, + type: 'stream' + })); + + +const tests = specificTests.concat(genericTests); + +let currentError; + +// mock submitResponse because we only care about testing error handling +Http2Stream.prototype.respond = () => 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); + req.on('error', common.expectsError({ + code: 'ERR_HTTP2_STREAM_ERROR', + type: Error, + message: 'Stream closed with error code NGHTTP2_INTERNAL_ERROR' + })); + + currentError = test; + req.resume(); + req.end(); + + req.on('end', common.mustCall(() => { + client.close(); + + if (!tests.length) { + server.close(); + } else { + runTest(tests.shift()); + } + })); +} diff --git a/test/parallel/test-http2-respond-with-fd-errors.js b/test/parallel/test-http2-respond-with-fd-errors.js index 0b215134663bda..0eccd231c63a2e 100644 --- a/test/parallel/test-http2-respond-with-fd-errors.js +++ b/test/parallel/test-http2-respond-with-fd-errors.js @@ -1,4 +1,5 @@ 'use strict'; +// Flags: --expose-internals const common = require('../common'); @@ -14,6 +15,7 @@ const { Http2Stream, nghttp2ErrorString } = process.binding('http2'); +const { NghttpError } = require('internal/http2/util'); // tests error handling within processRespondWithFD // (called by respondWithFD & respondWithFile) @@ -32,7 +34,8 @@ const genericTests = Object.getOwnPropertyNames(constants) ngError: constants[key], error: { code: 'ERR_HTTP2_ERROR', - type: Error, + type: NghttpError, + name: 'Error [ERR_HTTP2_ERROR]', message: nghttp2ErrorString(constants[key]) }, type: 'stream' @@ -85,7 +88,7 @@ function runTest(test) { req.on('error', common.expectsError({ code: 'ERR_HTTP2_STREAM_ERROR', type: Error, - message: 'Stream closed with error code 2' + message: 'Stream closed with error code NGHTTP2_INTERNAL_ERROR' })); currentError = test; diff --git a/test/parallel/test-http2-server-http1-client.js b/test/parallel/test-http2-server-http1-client.js index 34a8f48b5e130d..394993d4d72088 100644 --- a/test/parallel/test-http2-server-http1-client.js +++ b/test/parallel/test-http2-server-http1-client.js @@ -1,4 +1,5 @@ 'use strict'; +// Flags: --expose-internals const common = require('../common'); @@ -7,6 +8,7 @@ if (!common.hasCrypto) const http = require('http'); const http2 = require('http2'); +const { NghttpError } = require('internal/http2/util'); const server = http2.createServer(); server.on('stream', common.mustNotCall()); @@ -14,7 +16,7 @@ server.on('session', common.mustCall((session) => { session.on('close', common.mustCall()); session.on('error', common.expectsError({ code: 'ERR_HTTP2_ERROR', - type: Error, + type: NghttpError, message: 'Received bad client magic byte string' })); })); diff --git a/test/parallel/test-http2-server-push-stream-errors.js b/test/parallel/test-http2-server-push-stream-errors.js index 7eaf4dc94d15e2..a6d2fe127827a8 100644 --- a/test/parallel/test-http2-server-push-stream-errors.js +++ b/test/parallel/test-http2-server-push-stream-errors.js @@ -1,4 +1,5 @@ 'use strict'; +// Flags: --expose-internals const common = require('../common'); if (!common.hasCrypto) @@ -9,6 +10,7 @@ const { Http2Stream, nghttp2ErrorString } = process.binding('http2'); +const { NghttpError } = require('internal/http2/util'); // tests error handling within pushStream // - NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE (should emit session error) @@ -49,7 +51,8 @@ const genericTests = Object.getOwnPropertyNames(constants) ngError: constants[key], error: { code: 'ERR_HTTP2_ERROR', - type: Error, + type: NghttpError, + name: 'Error [ERR_HTTP2_ERROR]', message: nghttp2ErrorString(constants[key]) }, type: 'stream' diff --git a/test/parallel/test-http2-server-rst-stream.js b/test/parallel/test-http2-server-rst-stream.js index 6e4e9ccb88e611..402ff07fa9d0a0 100644 --- a/test/parallel/test-http2-server-rst-stream.js +++ b/test/parallel/test-http2-server-rst-stream.js @@ -18,10 +18,10 @@ const { const tests = [ [NGHTTP2_NO_ERROR, false], [NGHTTP2_NO_ERROR, false], - [NGHTTP2_PROTOCOL_ERROR, true, 1], + [NGHTTP2_PROTOCOL_ERROR, true, 'NGHTTP2_PROTOCOL_ERROR'], [NGHTTP2_CANCEL, false], - [NGHTTP2_REFUSED_STREAM, true, 7], - [NGHTTP2_INTERNAL_ERROR, true, 2] + [NGHTTP2_REFUSED_STREAM, true, 'NGHTTP2_REFUSED_STREAM'], + [NGHTTP2_INTERNAL_ERROR, true, 'NGHTTP2_INTERNAL_ERROR'] ]; const server = http2.createServer(); diff --git a/test/parallel/test-http2-too-large-headers.js b/test/parallel/test-http2-too-large-headers.js index 7a7082736160f8..ad7a24f39b2fa2 100644 --- a/test/parallel/test-http2-too-large-headers.js +++ b/test/parallel/test-http2-too-large-headers.js @@ -20,7 +20,7 @@ server.listen(0, common.mustCall(() => { req.on('error', common.expectsError({ code: 'ERR_HTTP2_STREAM_ERROR', type: Error, - message: 'Stream closed with error code 11' + message: 'Stream closed with error code NGHTTP2_ENHANCE_YOUR_CALM' })); req.on('close', common.mustCall((code) => { assert.strictEqual(code, NGHTTP2_ENHANCE_YOUR_CALM); diff --git a/test/parallel/test-http2-too-many-headers.js b/test/parallel/test-http2-too-many-headers.js index f05511cff657e0..9b57060be695bb 100644 --- a/test/parallel/test-http2-too-many-headers.js +++ b/test/parallel/test-http2-too-many-headers.js @@ -23,7 +23,7 @@ server.listen(0, common.mustCall(() => { req.on('error', common.expectsError({ code: 'ERR_HTTP2_STREAM_ERROR', type: Error, - message: 'Stream closed with error code 11' + message: 'Stream closed with error code NGHTTP2_ENHANCE_YOUR_CALM' })); req.on('close', common.mustCall((code) => { assert.strictEqual(code, NGHTTP2_ENHANCE_YOUR_CALM); diff --git a/test/parallel/test-internal-errors.js b/test/parallel/test-internal-errors.js index f5876b7f568dd9..5cdeaf5ee51058 100644 --- a/test/parallel/test-internal-errors.js +++ b/test/parallel/test-internal-errors.js @@ -175,6 +175,7 @@ common.expectsError(() => { message: /^Error for testing 2/ }); }, { code: 'ERR_ASSERTION', + type: assert.AssertionError, message: /.+ does not match \S/ }); @@ -225,6 +226,7 @@ common.expectsError( () => errors.message('ERR_INVALID_URL_SCHEME', [[]]), { code: 'ERR_ASSERTION', + type: assert.AssertionError, message: /^At least one expected value needs to be specified$/ }); @@ -239,6 +241,7 @@ common.expectsError( () => errors.message('ERR_MISSING_ARGS'), { code: 'ERR_ASSERTION', + type: assert.AssertionError, message: /^At least one arg needs to be specified$/ }); diff --git a/test/parallel/test-ttywrap-invalid-fd.js b/test/parallel/test-ttywrap-invalid-fd.js index f54adb1ddc8737..a9def448fd6747 100644 --- a/test/parallel/test-ttywrap-invalid-fd.js +++ b/test/parallel/test-ttywrap-invalid-fd.js @@ -1,4 +1,6 @@ 'use strict'; +// Flags: --expose-internals + const common = require('../common'); const assert = require('assert'); const fs = require('fs'); diff --git a/test/sequential/test-http2-max-session-memory.js b/test/sequential/test-http2-max-session-memory.js index e16000d1261ab0..d6d3bf935db801 100644 --- a/test/sequential/test-http2-max-session-memory.js +++ b/test/sequential/test-http2-max-session-memory.js @@ -30,7 +30,7 @@ server.listen(0, common.mustCall(() => { req.on('error', common.expectsError({ code: 'ERR_HTTP2_STREAM_ERROR', type: Error, - message: 'Stream closed with error code 11' + message: 'Stream closed with error code NGHTTP2_ENHANCE_YOUR_CALM' })); req.on('close', common.mustCall(() => { server.close();