|
| 1 | +'use strict'; |
| 2 | +const common = require('../common'); |
| 3 | +if (!common.hasCrypto) { common.skip('missing crypto'); } |
| 4 | +const assert = require('assert'); |
| 5 | +const http2 = require('http2'); |
| 6 | + |
| 7 | +// Integration tests for the http2.connect() `httpValidation` option. |
| 8 | +// See also test-http2-util-headers-list.js for the per-value validation logic. |
| 9 | + |
| 10 | +const server = http2.createServer(); |
| 11 | +// Consume any stream that is actually created. Control characters and |
| 12 | +// duplicate pseudo-headers may be rejected by nghttp2 asynchronously, so |
| 13 | +// swallow any stream errors rather than letting them become uncaught. |
| 14 | +server.on('stream', (stream) => { |
| 15 | + stream.on('error', () => {}); |
| 16 | +}); |
| 17 | + |
| 18 | +server.listen(0, common.mustCall(() => { |
| 19 | + const origin = `http://localhost:${server.address().port}`; |
| 20 | + |
| 21 | + function swallow(stream) { |
| 22 | + stream.on('error', () => {}); |
| 23 | + stream.destroy(); |
| 24 | + return stream; |
| 25 | + } |
| 26 | + |
| 27 | + // -- strict (default) -- |
| 28 | + { |
| 29 | + const session = http2.connect(origin); |
| 30 | + session.on('error', () => {}); |
| 31 | + // Non-latin1 characters are rejected by default. |
| 32 | + assert.throws(() => session.request({ 'x-bad-char': 'oʊmɪɡə' }), { |
| 33 | + code: 'ERR_HTTP2_INVALID_HEADER_VALUE', |
| 34 | + }); |
| 35 | + // Control characters are rejected by default per the HTTP RFCs. |
| 36 | + assert.throws(() => session.request({ 'x-control': 'value\x01here' }), { |
| 37 | + code: 'ERR_HTTP2_INVALID_HEADER_VALUE', |
| 38 | + }); |
| 39 | + // strictSingleValueFields is enabled by default. |
| 40 | + assert.throws(() => session.request([ |
| 41 | + [':method', 'GET'], |
| 42 | + [':method', 'POST'], |
| 43 | + ]), { |
| 44 | + code: 'ERR_HTTP2_HEADER_SINGLE_VALUE', |
| 45 | + }); |
| 46 | + // The :path pseudo-header must not contain unescaped characters. |
| 47 | + assert.throws(() => session.request({ ':path': 'bad\u0100path' }), { |
| 48 | + code: 'ERR_UNESCAPED_CHARACTERS', |
| 49 | + }); |
| 50 | + session.close(); |
| 51 | + } |
| 52 | + |
| 53 | + // -- relaxed -- |
| 54 | + { |
| 55 | + const session = http2.connect(origin, { httpValidation: 'relaxed' }); |
| 56 | + session.on('error', () => {}); |
| 57 | + // Control characters permitted by the Fetch specification are allowed. |
| 58 | + swallow(session.request({ 'x-control': 'value\x01here' })); |
| 59 | + // relaxed/insecure mode subsumes and disables strictSingleValueFields. |
| 60 | + swallow(session.request([ |
| 61 | + [':method', 'GET'], |
| 62 | + [':method', 'POST'], |
| 63 | + ])); |
| 64 | + // Non-latin1 characters are still rejected in relaxed mode. |
| 65 | + assert.throws(() => session.request({ 'x-bad-char': 'oʊmɪɡə' }), { |
| 66 | + code: 'ERR_HTTP2_INVALID_HEADER_VALUE', |
| 67 | + }); |
| 68 | + // NUL is always rejected. |
| 69 | + assert.throws(() => session.request({ 'x-nul': 'value\x00here' }), { |
| 70 | + code: 'ERR_HTTP2_INVALID_HEADER_VALUE', |
| 71 | + }); |
| 72 | + // The :path pseudo-header check is always enforced. |
| 73 | + assert.throws(() => session.request({ ':path': 'bad\u0100path' }), { |
| 74 | + code: 'ERR_UNESCAPED_CHARACTERS', |
| 75 | + }); |
| 76 | + session.close(); |
| 77 | + } |
| 78 | + |
| 79 | + // -- insecure -- |
| 80 | + { |
| 81 | + const session = http2.connect(origin, { httpValidation: 'insecure' }); |
| 82 | + session.on('error', () => {}); |
| 83 | + // All header value validation is skipped, including newlines. |
| 84 | + swallow(session.request({ 'x-newline': 'value\nhere' })); |
| 85 | + // strictSingleValueFields is disabled in insecure mode too. |
| 86 | + swallow(session.request([ |
| 87 | + [':method', 'GET'], |
| 88 | + [':method', 'POST'], |
| 89 | + ])); |
| 90 | + // The :path pseudo-header check is still enforced. |
| 91 | + assert.throws(() => session.request({ ':path': 'bad\u0100path' }), { |
| 92 | + code: 'ERR_UNESCAPED_CHARACTERS', |
| 93 | + }); |
| 94 | + session.close(); |
| 95 | + } |
| 96 | + |
| 97 | + // -- invalid option value -- |
| 98 | + { |
| 99 | + assert.throws(() => http2.connect(origin, { httpValidation: 'bogus' }), { |
| 100 | + code: 'ERR_INVALID_ARG_VALUE', |
| 101 | + }); |
| 102 | + } |
| 103 | + |
| 104 | + server.close(); |
| 105 | +})); |
0 commit comments