Skip to content

Commit 4caf536

Browse files
trivikrtargos
authored andcommitted
test: http2 stream.respond() error checks
Backport-PR-URL: #19579 PR-URL: #18861 Reviewed-By: James M Snell <[email protected]>
1 parent ca97be5 commit 4caf536

File tree

2 files changed

+166
-81
lines changed

2 files changed

+166
-81
lines changed

test/parallel/test-http2-respond-errors.js

+67-81
Original file line numberDiff line numberDiff line change
@@ -5,95 +5,81 @@ const common = require('../common');
55
if (!common.hasCrypto)
66
common.skip('missing crypto');
77
const http2 = require('http2');
8-
const {
9-
constants,
10-
Http2Stream,
11-
nghttp2ErrorString
12-
} = process.binding('http2');
13-
const { NghttpError } = require('internal/http2/util');
14-
15-
// tests error handling within respond
16-
// - every other NGHTTP2 error from binding (should emit stream error)
17-
18-
const specificTestKeys = [];
19-
20-
const specificTests = [];
21-
22-
const genericTests = Object.getOwnPropertyNames(constants)
23-
.filter((key) => (
24-
key.indexOf('NGHTTP2_ERR') === 0 && specificTestKeys.indexOf(key) < 0
25-
))
26-
.map((key) => ({
27-
ngError: constants[key],
28-
error: {
29-
code: 'ERR_HTTP2_ERROR',
30-
type: NghttpError,
31-
name: 'Error [ERR_HTTP2_ERROR]',
32-
message: nghttp2ErrorString(constants[key])
33-
},
34-
type: 'stream'
35-
}));
36-
37-
38-
const tests = specificTests.concat(genericTests);
39-
40-
let currentError;
41-
42-
// mock submitResponse because we only care about testing error handling
43-
Http2Stream.prototype.respond = () => currentError.ngError;
8+
const { Http2Stream } = process.binding('http2');
9+
10+
const types = {
11+
boolean: true,
12+
function: () => {},
13+
number: 1,
14+
object: {},
15+
array: [],
16+
null: null,
17+
symbol: Symbol('test')
18+
};
4419

4520
const server = http2.createServer();
46-
server.on('stream', common.mustCall((stream, headers) => {
47-
const errorMustCall = common.expectsError(currentError.error);
48-
const errorMustNotCall = common.mustNotCall(
49-
`${currentError.error.code} should emit on ${currentError.type}`
50-
);
51-
52-
if (currentError.type === 'stream') {
53-
stream.session.on('error', errorMustNotCall);
54-
stream.on('error', errorMustCall);
55-
stream.on('error', common.mustCall(() => {
56-
stream.destroy();
57-
}));
58-
} else {
59-
stream.session.once('error', errorMustCall);
60-
stream.on('error', errorMustNotCall);
61-
}
6221

63-
stream.respond();
64-
}, tests.length));
22+
Http2Stream.prototype.respond = () => 1;
23+
server.on('stream', common.mustCall((stream) => {
6524

66-
server.listen(0, common.mustCall(() => runTest(tests.shift())));
25+
// Check for all possible TypeError triggers on options.getTrailers
26+
Object.entries(types).forEach(([type, value]) => {
27+
if (type === 'function') {
28+
return;
29+
}
6730

68-
function runTest(test) {
69-
const port = server.address().port;
70-
const url = `http://localhost:${port}`;
71-
const headers = {
72-
':path': '/',
73-
':method': 'POST',
74-
':scheme': 'http',
75-
':authority': `localhost:${port}`
76-
};
31+
common.expectsError(
32+
() => stream.respond({
33+
'content-type': 'text/plain'
34+
}, {
35+
['getTrailers']: value
36+
}),
37+
{
38+
type: TypeError,
39+
code: 'ERR_INVALID_OPT_VALUE',
40+
message: `The value "${String(value)}" is invalid ` +
41+
'for option "getTrailers"'
42+
}
43+
);
44+
});
45+
46+
// Send headers
47+
stream.respond({
48+
'content-type': 'text/plain'
49+
}, {
50+
['getTrailers']: () => common.mustCall()
51+
});
52+
53+
// Should throw if headers already sent
54+
common.expectsError(
55+
() => stream.respond(),
56+
{
57+
type: Error,
58+
code: 'ERR_HTTP2_HEADERS_SENT',
59+
message: 'Response has already been initiated.'
60+
}
61+
);
7762

78-
const client = http2.connect(url);
79-
const req = client.request(headers);
80-
req.on('error', common.expectsError({
81-
code: 'ERR_HTTP2_STREAM_ERROR',
82-
type: Error,
83-
message: 'Stream closed with error code 2'
84-
}));
63+
// Should throw if stream already destroyed
64+
stream.destroy();
65+
common.expectsError(
66+
() => stream.respond(),
67+
{
68+
type: Error,
69+
code: 'ERR_HTTP2_INVALID_STREAM',
70+
message: 'The stream has been destroyed'
71+
}
72+
);
73+
}));
8574

86-
currentError = test;
87-
req.resume();
88-
req.end();
75+
server.listen(0, common.mustCall(() => {
76+
const client = http2.connect(`http://localhost:${server.address().port}`);
77+
const req = client.request();
8978

9079
req.on('end', common.mustCall(() => {
9180
client.close();
92-
93-
if (!tests.length) {
94-
server.close();
95-
} else {
96-
runTest(tests.shift());
97-
}
81+
server.close();
9882
}));
99-
}
83+
req.resume();
84+
req.end();
85+
}));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
'use strict';
2+
// Flags: --expose-internals
3+
4+
const common = require('../common');
5+
if (!common.hasCrypto)
6+
common.skip('missing crypto');
7+
const http2 = require('http2');
8+
const {
9+
constants,
10+
Http2Stream,
11+
nghttp2ErrorString
12+
} = process.binding('http2');
13+
const { NghttpError } = require('internal/http2/util');
14+
15+
// tests error handling within respond
16+
// - every other NGHTTP2 error from binding (should emit stream error)
17+
18+
const specificTestKeys = [];
19+
20+
const specificTests = [];
21+
22+
const genericTests = Object.getOwnPropertyNames(constants)
23+
.filter((key) => (
24+
key.indexOf('NGHTTP2_ERR') === 0 && specificTestKeys.indexOf(key) < 0
25+
))
26+
.map((key) => ({
27+
ngError: constants[key],
28+
error: {
29+
code: 'ERR_HTTP2_ERROR',
30+
type: NghttpError,
31+
name: 'Error [ERR_HTTP2_ERROR]',
32+
message: nghttp2ErrorString(constants[key])
33+
},
34+
type: 'stream'
35+
}));
36+
37+
38+
const tests = specificTests.concat(genericTests);
39+
40+
let currentError;
41+
42+
// mock submitResponse because we only care about testing error handling
43+
Http2Stream.prototype.respond = () => currentError.ngError;
44+
45+
const server = http2.createServer();
46+
server.on('stream', common.mustCall((stream, headers) => {
47+
const errorMustCall = common.expectsError(currentError.error);
48+
const errorMustNotCall = common.mustNotCall(
49+
`${currentError.error.code} should emit on ${currentError.type}`
50+
);
51+
52+
if (currentError.type === 'stream') {
53+
stream.session.on('error', errorMustNotCall);
54+
stream.on('error', errorMustCall);
55+
stream.on('error', common.mustCall(() => {
56+
stream.destroy();
57+
}));
58+
} else {
59+
stream.session.once('error', errorMustCall);
60+
stream.on('error', errorMustNotCall);
61+
}
62+
63+
stream.respond();
64+
}, tests.length));
65+
66+
server.listen(0, common.mustCall(() => runTest(tests.shift())));
67+
68+
function runTest(test) {
69+
const port = server.address().port;
70+
const url = `http://localhost:${port}`;
71+
const headers = {
72+
':path': '/',
73+
':method': 'POST',
74+
':scheme': 'http',
75+
':authority': `localhost:${port}`
76+
};
77+
78+
const client = http2.connect(url);
79+
const req = client.request(headers);
80+
req.on('error', common.expectsError({
81+
code: 'ERR_HTTP2_STREAM_ERROR',
82+
type: Error,
83+
message: 'Stream closed with error code 2'
84+
}));
85+
86+
currentError = test;
87+
req.resume();
88+
req.end();
89+
90+
req.on('end', common.mustCall(() => {
91+
client.close();
92+
93+
if (!tests.length) {
94+
server.close();
95+
} else {
96+
runTest(tests.shift());
97+
}
98+
}));
99+
}

0 commit comments

Comments
 (0)