Skip to content

Commit f6c5188

Browse files
apapirovskiMylesBorins
authored andcommitted
http2: correct emit error in onConnect, full tests
Correct requestOnConnect to emit session error on NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE rather than stream error. Add full test suite for the error handling within requestOnConnect. PR-URL: #15080 Refs: #14985 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Matteo Collina <[email protected]>
1 parent fd51cb8 commit f6c5188

File tree

2 files changed

+125
-1
lines changed

2 files changed

+125
-1
lines changed

lib/internal/http2/core.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ function requestOnConnect(headers, options) {
462462
break;
463463
case NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE:
464464
err = new errors.Error('ERR_HTTP2_OUT_OF_STREAMS');
465-
process.nextTick(() => this.emit('error', err));
465+
process.nextTick(() => session.emit('error', err));
466466
break;
467467
case NGHTTP2_ERR_INVALID_ARGUMENT:
468468
err = new errors.Error('ERR_HTTP2_STREAM_SELF_DEPENDENCY');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// Flags: --expose-http2
2+
'use strict';
3+
4+
const {
5+
constants,
6+
Http2Session,
7+
nghttp2ErrorString
8+
} = process.binding('http2');
9+
const common = require('../common');
10+
if (!common.hasCrypto)
11+
common.skip('missing crypto');
12+
const http2 = require('http2');
13+
14+
// tests error handling within requestOnConnect
15+
// - NGHTTP2_ERR_NOMEM (should emit session error)
16+
// - NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE (should emit session error)
17+
// - NGHTTP2_ERR_INVALID_ARGUMENT (should emit stream error)
18+
// - every other NGHTTP2 error from binding (should emit session error)
19+
20+
const specificTestKeys = [
21+
'NGHTTP2_ERR_NOMEM',
22+
'NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE',
23+
'NGHTTP2_ERR_INVALID_ARGUMENT'
24+
];
25+
26+
const specificTests = [
27+
{
28+
ngError: constants.NGHTTP2_ERR_NOMEM,
29+
error: {
30+
code: 'ERR_OUTOFMEMORY',
31+
type: Error,
32+
message: 'Out of memory'
33+
},
34+
type: 'session'
35+
},
36+
{
37+
ngError: constants.NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE,
38+
error: {
39+
code: 'ERR_HTTP2_OUT_OF_STREAMS',
40+
type: Error,
41+
message: 'No stream ID is available because ' +
42+
'maximum stream ID has been reached'
43+
},
44+
type: 'session'
45+
},
46+
{
47+
ngError: constants.NGHTTP2_ERR_INVALID_ARGUMENT,
48+
error: {
49+
code: 'ERR_HTTP2_STREAM_SELF_DEPENDENCY',
50+
type: Error,
51+
message: 'A stream cannot depend on itself'
52+
},
53+
type: 'stream'
54+
},
55+
];
56+
57+
const genericTests = Object.getOwnPropertyNames(constants)
58+
.filter((key) => (
59+
key.indexOf('NGHTTP2_ERR') === 0 && specificTestKeys.indexOf(key) < 0
60+
))
61+
.map((key) => ({
62+
ngError: constants[key],
63+
error: {
64+
code: 'ERR_HTTP2_ERROR',
65+
type: Error,
66+
message: nghttp2ErrorString(constants[key])
67+
},
68+
type: 'session'
69+
}));
70+
71+
const tests = specificTests.concat(genericTests);
72+
73+
let currentError;
74+
75+
// mock submitRequest because we only care about testing error handling
76+
Http2Session.prototype.submitRequest = () => currentError;
77+
78+
const server = http2.createServer(common.mustNotCall());
79+
80+
server.listen(0, common.mustCall(() => runTest(tests.shift())));
81+
82+
function runTest(test) {
83+
const port = server.address().port;
84+
const url = `http://localhost:${port}`;
85+
const headers = {
86+
':path': '/',
87+
':method': 'POST',
88+
':scheme': 'http',
89+
':authority': `localhost:${port}`
90+
};
91+
92+
const client = http2.connect(url);
93+
const req = client.request(headers);
94+
95+
currentError = test.ngError;
96+
req.resume();
97+
req.end();
98+
99+
const errorMustCall = common.expectsError(test.error);
100+
const errorMustNotCall = common.mustNotCall(
101+
`${test.error.code} should emit on ${test.type}`
102+
);
103+
104+
if (test.type === 'stream') {
105+
client.on('error', errorMustNotCall);
106+
req.on('error', errorMustCall);
107+
req.on('error', common.mustCall(() => {
108+
client.destroy();
109+
}));
110+
} else {
111+
client.on('error', errorMustCall);
112+
req.on('error', errorMustNotCall);
113+
}
114+
115+
req.on('end', common.mustCall(() => {
116+
client.destroy();
117+
118+
if (!tests.length) {
119+
server.close();
120+
} else {
121+
runTest(tests.shift());
122+
}
123+
}));
124+
}

0 commit comments

Comments
 (0)