mirror of https://github.com/lukechilds/node.git
Browse Source
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>v9.x-staging
Anatoli Papirovski
7 years ago
committed by
Ruben Bridgewater
11 changed files with 788 additions and 110 deletions
@ -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()); |
||||
|
} |
||||
|
})); |
||||
|
} |
@ -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()); |
||||
|
} |
||||
|
})); |
||||
|
} |
@ -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()); |
||||
|
} |
||||
|
})); |
||||
|
} |
@ -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()); |
||||
|
} |
||||
|
})); |
||||
|
} |
@ -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()); |
||||
|
} |
||||
|
})); |
||||
|
} |
@ -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()); |
||||
|
} |
||||
|
})); |
||||
|
} |
Loading…
Reference in new issue