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