From e91a1a3d5190103da0a59fa57c384ef77132b2b3 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Tue, 16 May 2017 16:53:20 -0700 Subject: [PATCH] http2: improve header single value checking PR-URL: https://github.com/nodejs/http2/pull/129 Reviewed-By: Matteo Collina --- lib/internal/errors.js | 6 +- lib/internal/http2/compat.js | 3 - lib/internal/http2/util.js | 139 ++++++++++--- src/node_http2.cc | 58 +++++- src/node_http2.h | 9 +- test/parallel/test-http2-binding.js | 9 +- ...est-http2-compat-serverresponse-headers.js | 3 - test/parallel/test-http2-util-headers-list.js | 194 +++++++++++++++--- 8 files changed, 345 insertions(+), 76 deletions(-) diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 9b8ae18e15..aa0ecaf10c 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -116,6 +116,8 @@ E('ERR_CONSOLE_WRITABLE_STREAM', (name) => `Console expects a writable stream instance for ${name}`); E('ERR_HTTP2_HEADER_REQUIRED', (name) => `The ${name} header is required`); +E('ERR_HTTP2_HEADER_SINGLE_VALUE', + (name) => `Header field "${name}" must have only a single value`); E('ERR_HTTP2_HEADERS_SENT', 'Response has already been initiated.'); E('ERR_HTTP2_HEADERS_AFTER_RESPOND', 'Cannot specify additional headers after response initiated'); @@ -123,8 +125,8 @@ E('ERR_HTTP2_INVALID_CONNECTION_HEADERS', 'HTTP/1 Connection specific headers are forbidden'); E('ERR_HTTP2_INVALID_INFO_STATUS', 'Invalid informational status code'); -E('ERR_HTTP2_PSEUDO_HEADERS_SINGLE_VALUE', - 'HTTP/2 pseudo-headers must have a single value'); +E('ERR_HTTP2_INVALID_PSEUDOHEADER', + (name) => `"${name}" is not a valid HTTP/2 pseudoheader`); E('ERR_HTTP2_SOCKET_BOUND', 'The socket is already bound to an Http2Session'); E('ERR_HTTP2_STATUS_INVALID', diff --git a/lib/internal/http2/compat.js b/lib/internal/http2/compat.js index 9b77ba3f1a..28bffa652d 100644 --- a/lib/internal/http2/compat.js +++ b/lib/internal/http2/compat.js @@ -3,7 +3,6 @@ const Stream = require('stream'); const Readable = Stream.Readable; const binding = process.binding('http2'); -const { isIllegalConnectionSpecificHeader } = require('internal/http2/util'); const constants = binding.constants; const kFinish = Symbol('finish'); @@ -24,8 +23,6 @@ let statusMessageWarned = false; function assertValidHeader(name, value) { if (isPseudoHeader(name)) throw new Error('Cannot set HTTP/2 pseudo-headers'); - if (isIllegalConnectionSpecificHeader(name, value)) - throw new Error('Connection-specific HTTP/1 headers are not permitted'); if (value === undefined || value === null) throw new TypeError('Value must not be undefined or null'); } diff --git a/lib/internal/http2/util.js b/lib/internal/http2/util.js index 9441f3e461..5762c66d90 100644 --- a/lib/internal/http2/util.js +++ b/lib/internal/http2/util.js @@ -3,6 +3,89 @@ const binding = process.binding('http2'); const errors = require('internal/errors'); +const { + HTTP2_HEADER_STATUS, + HTTP2_HEADER_METHOD, + HTTP2_HEADER_AUTHORITY, + HTTP2_HEADER_SCHEME, + HTTP2_HEADER_PATH, + HTTP2_HEADER_AGE, + HTTP2_HEADER_AUTHORIZATION, + HTTP2_HEADER_CONTENT_ENCODING, + HTTP2_HEADER_CONTENT_LANGUAGE, + HTTP2_HEADER_CONTENT_LENGTH, + HTTP2_HEADER_CONTENT_LOCATION, + HTTP2_HEADER_CONTENT_MD5, + HTTP2_HEADER_CONTENT_RANGE, + HTTP2_HEADER_CONTENT_TYPE, + HTTP2_HEADER_DATE, + HTTP2_HEADER_ETAG, + HTTP2_HEADER_EXPIRES, + HTTP2_HEADER_FROM, + HTTP2_HEADER_IF_MATCH, + HTTP2_HEADER_IF_NONE_MATCH, + HTTP2_HEADER_IF_MODIFIED_SINCE, + HTTP2_HEADER_IF_RANGE, + HTTP2_HEADER_IF_UNMODIFIED_SINCE, + HTTP2_HEADER_LAST_MODIFIED, + HTTP2_HEADER_MAX_FORWARDS, + HTTP2_HEADER_PROXY_AUTHORIZATION, + HTTP2_HEADER_RANGE, + HTTP2_HEADER_REFERER, + HTTP2_HEADER_RETRY_AFTER, + HTTP2_HEADER_USER_AGENT, + + HTTP2_HEADER_CONNECTION, + HTTP2_HEADER_UPGRADE, + HTTP2_HEADER_HTTP2_SETTINGS, + HTTP2_HEADER_TE, + HTTP2_HEADER_TRANSFER_ENCODING, + HTTP2_HEADER_HOST, + HTTP2_HEADER_KEEP_ALIVE, + HTTP2_HEADER_PROXY_CONNECTION +} = binding.constants; + +const kValidPseudoHeaders = new Set([ + HTTP2_HEADER_STATUS, + HTTP2_HEADER_METHOD, + HTTP2_HEADER_AUTHORITY, + HTTP2_HEADER_SCHEME, + HTTP2_HEADER_PATH +]); + +const kSingleValueHeaders = new Set([ + HTTP2_HEADER_STATUS, + HTTP2_HEADER_METHOD, + HTTP2_HEADER_AUTHORITY, + HTTP2_HEADER_SCHEME, + HTTP2_HEADER_PATH, + HTTP2_HEADER_AGE, + HTTP2_HEADER_AUTHORIZATION, + HTTP2_HEADER_CONTENT_ENCODING, + HTTP2_HEADER_CONTENT_LANGUAGE, + HTTP2_HEADER_CONTENT_LENGTH, + HTTP2_HEADER_CONTENT_LOCATION, + HTTP2_HEADER_CONTENT_MD5, + HTTP2_HEADER_CONTENT_RANGE, + HTTP2_HEADER_CONTENT_TYPE, + HTTP2_HEADER_DATE, + HTTP2_HEADER_ETAG, + HTTP2_HEADER_EXPIRES, + HTTP2_HEADER_FROM, + HTTP2_HEADER_IF_MATCH, + HTTP2_HEADER_IF_MODIFIED_SINCE, + HTTP2_HEADER_IF_NONE_MATCH, + HTTP2_HEADER_IF_RANGE, + HTTP2_HEADER_IF_UNMODIFIED_SINCE, + HTTP2_HEADER_LAST_MODIFIED, + HTTP2_HEADER_MAX_FORWARDS, + HTTP2_HEADER_PROXY_AUTHORIZATION, + HTTP2_HEADER_RANGE, + HTTP2_HEADER_REFERER, + HTTP2_HEADER_RETRY_AFTER, + HTTP2_HEADER_USER_AGENT +]); + // The following ArrayBuffer instances are used to share memory more efficiently // with the native binding side for a number of methods. These are not intended // to be used directly by users in any way. The ArrayBuffers are created on @@ -126,28 +209,22 @@ function getStreamState(session, stream) { function isIllegalConnectionSpecificHeader(name, value) { switch (name) { - case 'connection': - case 'http2-settings': - case 'keep-alive': - case 'proxy-connection': - case 'transfer-encoding': - case 'upgrade': + case HTTP2_HEADER_CONNECTION: + case HTTP2_HEADER_UPGRADE: + case HTTP2_HEADER_HOST: + case HTTP2_HEADER_HTTP2_SETTINGS: + case HTTP2_HEADER_KEEP_ALIVE: + case HTTP2_HEADER_PROXY_CONNECTION: + case HTTP2_HEADER_TRANSFER_ENCODING: return true; - case 'te': - return value !== 'trailers'; + case HTTP2_HEADER_TE: + const val = Array.isArray(value) ? value.join(', ') : value; + return val !== 'trailers'; default: return false; } } -function assertIllegalConnectionSpecificHeader(name, value, ctor) { - if (isIllegalConnectionSpecificHeader(name, value)) { - var err = new errors.Error('ERR_HTTP2_INVALID_CONNECTION_HEADERS'); - Error.captureStackTrace(err, ctor); - throw err; - } -} - function mapToHeaders(map) { var ret = []; var keys = Object.keys(map); @@ -158,26 +235,33 @@ function mapToHeaders(map) { if (typeof key === 'symbol' || value === undefined || !key) continue; key = String(key).toLowerCase(); - var isArray = Array.isArray(value); - if (key[0] === ':') { - if (isArray) { - if (value.length > 1) - throw new errors.Error('ERR_HTTP2_PSEUDO_HEADERS_SINGLE_VALUE'); - value = value[0]; + const isArray = Array.isArray(value); + if (isArray) { + switch (value.length) { + case 0: + continue; + case 1: + value = String(value[0]); + break; + default: + if (kSingleValueHeaders.has(key)) + throw new errors.Error('ERR_HTTP2_HEADER_SINGLE_VALUE', key); } - val = String(value); - assertIllegalConnectionSpecificHeader(key, val, mapToHeaders); - ret.unshift([key, val]); + } + if (key[0] === ':') { + if (!kValidPseudoHeaders.has(key)) + throw new errors.Error('ERR_HTTP2_INVALID_PSEUDOHEADER', key); + ret.unshift([key, String(value)]); } else { + if (isIllegalConnectionSpecificHeader(key, value)) + throw new errors.Error('ERR_HTTP2_INVALID_CONNECTION_HEADERS'); if (isArray) { for (var k = 0; k < value.length; k++) { val = String(value[k]); - assertIllegalConnectionSpecificHeader(key, val, mapToHeaders); ret.push([key, val]); } } else { val = String(value); - assertIllegalConnectionSpecificHeader(key, val, mapToHeaders); ret.push([key, val]); } } @@ -214,7 +298,6 @@ function assertIsObject(value, name, types) { module.exports = { assertIsObject, - isIllegalConnectionSpecificHeader, emitErrorIfNecessary, getDefaultSettings, getSessionState, diff --git a/src/node_http2.cc b/src/node_http2.cc index 87e3bf8738..9168deaf5f 100644 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -713,6 +713,10 @@ static bool CheckHeaderAllowsMultiple(nghttp2_vec* name) { break; case 4: switch (name->base[3]) { + case 'e': + if (memcmp(name->base, "dat", 3) == 0) + return false; + break; case 'g': if (memcmp(name->base, "eta", 3) == 0) return false; @@ -726,6 +730,10 @@ static bool CheckHeaderAllowsMultiple(nghttp2_vec* name) { return false; break; } + case 5: + if (memcmp(name->base, "range", 5) == 0) + return false; + break; case 6: if (memcmp(name->base, "server", 6) == 0) return false; @@ -743,16 +751,36 @@ static bool CheckHeaderAllowsMultiple(nghttp2_vec* name) { } break; case 8: - if (memcmp(name->base, "location", 8) == 0) - return false; + switch (name->base[7]) { + case 'e': + if (memcmp(name->base, "if-rang", 7) == 0) + return false; + break; + case 'h': + if (memcmp(name->base, "if-matc", 7) == 0) + return false; + break; + case 'n': + if (memcmp(name->base, "locatio", 7) == 0) + return false; + break; + } break; case 10: if (memcmp(name->base, "user-agent", 10) == 0) return false; break; case 11: - if (memcmp(name->base, "retry-after", 11) == 0) - return false; + switch (name->base[10]) { + case '5': + if (memcmp(name->base, "content-md", 10) == 0) + return false; + break; + case 'r': + if (memcmp(name->base, "retry-afte", 10) == 0) + return false; + break; + } break; case 12: switch (name->base[11]) { @@ -772,6 +800,10 @@ static bool CheckHeaderAllowsMultiple(nghttp2_vec* name) { if (memcmp(name->base, "last-modifie", 12) == 0) return false; break; + case 'h': + if (memcmp(name->base, "if-none-matc", 12) == 0) + return false; + break; case 'n': if (memcmp(name->base, "authorizatio", 12) == 0) return false; @@ -782,6 +814,22 @@ static bool CheckHeaderAllowsMultiple(nghttp2_vec* name) { if (memcmp(name->base, "content-length", 14) == 0) return false; break; + case 16: + switch (name->base[15]) { + case 'e': + if (memcmp(name->base, "content-languag", 15) == 0) + return false; + break; + case 'g': + if (memcmp(name->base, "content-encodin", 15) == 0) + return false; + break; + case 'n': + if (memcmp(name->base, "content-locatio", 15) == 0) + return false; + break; + } + break; case 17: if (memcmp(name->base, "if-modified-since", 17) == 0) return false; @@ -793,7 +841,7 @@ static bool CheckHeaderAllowsMultiple(nghttp2_vec* name) { return false; break; case 'n': - if (memcmp(name->base, "proxy-authenticatio", 18) == 0) + if (memcmp(name->base, "proxy-authorizatio", 18) == 0) return false; break; } diff --git a/src/node_http2.h b/src/node_http2.h index a3e44b76bf..5ea38d8cfb 100644 --- a/src/node_http2.h +++ b/src/node_http2.h @@ -31,11 +31,13 @@ using v8::MaybeLocal; V(ALLOW, "allow") \ V(AUTHORIZATION, "authorization") \ V(CACHE_CONTROL, "cache-control") \ + V(CONNECTION, "connection") \ V(CONTENT_DISPOSITION, "content-disposition") \ V(CONTENT_ENCODING, "content-encoding") \ V(CONTENT_LANGUAGE, "content-language") \ V(CONTENT_LENGTH, "content-length") \ V(CONTENT_LOCATION, "content-location") \ + V(CONTENT_MD5, "content-md5") \ V(CONTENT_RANGE, "content-range") \ V(CONTENT_TYPE, "content-type") \ V(COOKIE, "cookie") \ @@ -65,10 +67,15 @@ using v8::MaybeLocal; V(SET_COOKIE, "set-cookie") \ V(STRICT_TRANSPORT_SECURITY, "strict-transport-security") \ V(TRANSFER_ENCODING, "transfer-encoding") \ + V(TE, "te") \ + V(UPGRADE, "upgrade") \ V(USER_AGENT, "user-agent") \ V(VARY, "vary") \ V(VIA, "via") \ - V(WWW_AUTHENTICATE, "www-authenticate") + V(WWW_AUTHENTICATE, "www-authenticate") \ + V(HTTP2_SETTINGS, "http2-settings") \ + V(KEEP_ALIVE, "keep-alive") \ + V(PROXY_CONNECTION, "proxy-connection") enum http_known_headers { HTTP_KNOWN_HEADER_MIN, diff --git a/test/parallel/test-http2-binding.js b/test/parallel/test-http2-binding.js index f1285f3294..ecf81b10af 100644 --- a/test/parallel/test-http2-binding.js +++ b/test/parallel/test-http2-binding.js @@ -126,6 +126,7 @@ const expectedHeaderNames = { HTTP2_HEADER_CONTENT_RANGE: 'content-range', HTTP2_HEADER_CONTENT_TYPE: 'content-type', HTTP2_HEADER_COOKIE: 'cookie', + HTTP2_HEADER_CONNECTION: 'connection', HTTP2_HEADER_ETAG: 'etag', HTTP2_HEADER_EXPECT: 'expect', HTTP2_HEADER_EXPIRES: 'expires', @@ -143,6 +144,7 @@ const expectedHeaderNames = { HTTP2_HEADER_PREFER: 'prefer', HTTP2_HEADER_PROXY_AUTHENTICATE: 'proxy-authenticate', HTTP2_HEADER_PROXY_AUTHORIZATION: 'proxy-authorization', + HTTP2_HEADER_PROXY_CONNECTION: 'proxy-connection', HTTP2_HEADER_RANGE: 'range', HTTP2_HEADER_REFERER: 'referer', HTTP2_HEADER_REFRESH: 'refresh', @@ -154,7 +156,12 @@ const expectedHeaderNames = { HTTP2_HEADER_USER_AGENT: 'user-agent', HTTP2_HEADER_VARY: 'vary', HTTP2_HEADER_VIA: 'via', - HTTP2_HEADER_WWW_AUTHENTICATE: 'www-authenticate' + HTTP2_HEADER_WWW_AUTHENTICATE: 'www-authenticate', + HTTP2_HEADER_KEEP_ALIVE: 'keep-alive', + HTTP2_HEADER_CONTENT_MD5: 'content-md5', + HTTP2_HEADER_TE: 'te', + HTTP2_HEADER_UPGRADE: 'upgrade', + HTTP2_HEADER_HTTP2_SETTINGS: 'http2-settings' }; const expectedNGConstants = { diff --git a/test/parallel/test-http2-compat-serverresponse-headers.js b/test/parallel/test-http2-compat-serverresponse-headers.js index bdf92f75b9..5e0cc8969e 100644 --- a/test/parallel/test-http2-compat-serverresponse-headers.js +++ b/test/parallel/test-http2-compat-serverresponse-headers.js @@ -42,9 +42,6 @@ server.listen(0, common.mustCall(function() { assert.throws(function() { response.setHeader(':status', 'foobar'); }, Error); - assert.throws(function() { - response.setHeader('connection', 'foobar'); - }, Error); assert.throws(function() { response.setHeader(real, null); }, TypeError); diff --git a/test/parallel/test-http2-util-headers-list.js b/test/parallel/test-http2-util-headers-list.js index c86c72ec65..9d5a08f821 100644 --- a/test/parallel/test-http2-util-headers-list.js +++ b/test/parallel/test-http2-util-headers-list.js @@ -8,12 +8,79 @@ const common = require('../common'); const assert = require('assert'); const { mapToHeaders } = require('internal/http2/util'); +const { + HTTP2_HEADER_STATUS, + HTTP2_HEADER_METHOD, + HTTP2_HEADER_AUTHORITY, + HTTP2_HEADER_SCHEME, + HTTP2_HEADER_PATH, + HTTP2_HEADER_AGE, + HTTP2_HEADER_AUTHORIZATION, + HTTP2_HEADER_CONTENT_ENCODING, + HTTP2_HEADER_CONTENT_LANGUAGE, + HTTP2_HEADER_CONTENT_LENGTH, + HTTP2_HEADER_CONTENT_LOCATION, + HTTP2_HEADER_CONTENT_MD5, + HTTP2_HEADER_CONTENT_RANGE, + HTTP2_HEADER_CONTENT_TYPE, + HTTP2_HEADER_DATE, + HTTP2_HEADER_ETAG, + HTTP2_HEADER_EXPIRES, + HTTP2_HEADER_FROM, + HTTP2_HEADER_IF_MATCH, + HTTP2_HEADER_IF_MODIFIED_SINCE, + HTTP2_HEADER_IF_NONE_MATCH, + HTTP2_HEADER_IF_RANGE, + HTTP2_HEADER_IF_UNMODIFIED_SINCE, + HTTP2_HEADER_LAST_MODIFIED, + HTTP2_HEADER_MAX_FORWARDS, + HTTP2_HEADER_PROXY_AUTHORIZATION, + HTTP2_HEADER_RANGE, + HTTP2_HEADER_REFERER, + HTTP2_HEADER_RETRY_AFTER, + HTTP2_HEADER_USER_AGENT, + + HTTP2_HEADER_ACCEPT_CHARSET, + HTTP2_HEADER_ACCEPT_ENCODING, + HTTP2_HEADER_ACCEPT_LANGUAGE, + HTTP2_HEADER_ACCEPT_RANGES, + HTTP2_HEADER_ACCEPT, + HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, + HTTP2_HEADER_ALLOW, + HTTP2_HEADER_CACHE_CONTROL, + HTTP2_HEADER_CONTENT_DISPOSITION, + HTTP2_HEADER_COOKIE, + HTTP2_HEADER_EXPECT, + HTTP2_HEADER_LINK, + HTTP2_HEADER_LOCATION, + HTTP2_HEADER_PREFER, + HTTP2_HEADER_PROXY_AUTHENTICATE, + HTTP2_HEADER_REFRESH, + HTTP2_HEADER_SERVER, + HTTP2_HEADER_SET_COOKIE, + HTTP2_HEADER_STRICT_TRANSPORT_SECURITY, + HTTP2_HEADER_VARY, + HTTP2_HEADER_VIA, + HTTP2_HEADER_WWW_AUTHENTICATE, + + HTTP2_HEADER_CONNECTION, + HTTP2_HEADER_UPGRADE, + HTTP2_HEADER_HTTP2_SETTINGS, + HTTP2_HEADER_TE, + HTTP2_HEADER_TRANSFER_ENCODING, + HTTP2_HEADER_HOST, + HTTP2_HEADER_KEEP_ALIVE, + HTTP2_HEADER_PROXY_CONNECTION +} = process.binding('http2').constants; + { const headers = { 'abc': 1, ':status': 200, ':path': 'abc', - 'xyz': [1, '2', { toString() { return '3'; } }, 4] + 'xyz': [1, '2', { toString() { return '3'; } }, 4], + 'foo': [], + 'BAR': [1] }; assert.deepStrictEqual(mapToHeaders(headers), [ @@ -23,7 +90,8 @@ const { mapToHeaders } = require('internal/http2/util'); [ 'xyz', '1' ], [ 'xyz', '2' ], [ 'xyz', '3' ], - [ 'xyz', '4' ] + [ 'xyz', '4' ], + [ 'bar', '1' ] ]); } @@ -31,7 +99,8 @@ const { mapToHeaders } = require('internal/http2/util'); const headers = { 'abc': 1, ':path': 'abc', - ':status': 200, + ':status': [200], + ':authority': [], 'xyz': [1, 2, 3, 4] }; @@ -86,39 +155,98 @@ const { mapToHeaders } = require('internal/http2/util'); ]); } -assert.throws(() => mapToHeaders({':status': [1, 2, 3]}), - common.expectsError({ - code: 'ERR_HTTP2_PSEUDO_HEADERS_SINGLE_VALUE', - message: /^HTTP\/2 pseudo-headers must have a single value$/ - })); +// The following are not allowed to have multiple values +[ + HTTP2_HEADER_STATUS, + HTTP2_HEADER_METHOD, + HTTP2_HEADER_AUTHORITY, + HTTP2_HEADER_SCHEME, + HTTP2_HEADER_PATH, + HTTP2_HEADER_AGE, + HTTP2_HEADER_AUTHORIZATION, + HTTP2_HEADER_CONTENT_ENCODING, + HTTP2_HEADER_CONTENT_LANGUAGE, + HTTP2_HEADER_CONTENT_LENGTH, + HTTP2_HEADER_CONTENT_LOCATION, + HTTP2_HEADER_CONTENT_MD5, + HTTP2_HEADER_CONTENT_RANGE, + HTTP2_HEADER_CONTENT_TYPE, + HTTP2_HEADER_DATE, + HTTP2_HEADER_ETAG, + HTTP2_HEADER_EXPIRES, + HTTP2_HEADER_FROM, + HTTP2_HEADER_IF_MATCH, + HTTP2_HEADER_IF_MODIFIED_SINCE, + HTTP2_HEADER_IF_NONE_MATCH, + HTTP2_HEADER_IF_RANGE, + HTTP2_HEADER_IF_UNMODIFIED_SINCE, + HTTP2_HEADER_LAST_MODIFIED, + HTTP2_HEADER_MAX_FORWARDS, + HTTP2_HEADER_PROXY_AUTHORIZATION, + HTTP2_HEADER_RANGE, + HTTP2_HEADER_REFERER, + HTTP2_HEADER_RETRY_AFTER, + HTTP2_HEADER_USER_AGENT +].forEach((name) => { + const msg = `Header field "${name}" must have only a single value`; + assert.throws(() => mapToHeaders({[name]: [1, 2, 3]}), + common.expectsError({ + code: 'ERR_HTTP2_HEADER_SINGLE_VALUE', + message: msg + })); +}); -assert.throws(() => mapToHeaders({':path': [1, 2, 3]}), - common.expectsError({ - code: 'ERR_HTTP2_PSEUDO_HEADERS_SINGLE_VALUE', - message: /^HTTP\/2 pseudo-headers must have a single value$/ - })); +[ + HTTP2_HEADER_ACCEPT_CHARSET, + HTTP2_HEADER_ACCEPT_ENCODING, + HTTP2_HEADER_ACCEPT_LANGUAGE, + HTTP2_HEADER_ACCEPT_RANGES, + HTTP2_HEADER_ACCEPT, + HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, + HTTP2_HEADER_ALLOW, + HTTP2_HEADER_CACHE_CONTROL, + HTTP2_HEADER_CONTENT_DISPOSITION, + HTTP2_HEADER_COOKIE, + HTTP2_HEADER_EXPECT, + HTTP2_HEADER_LINK, + HTTP2_HEADER_LOCATION, + HTTP2_HEADER_PREFER, + HTTP2_HEADER_PROXY_AUTHENTICATE, + HTTP2_HEADER_REFRESH, + HTTP2_HEADER_SERVER, + HTTP2_HEADER_SET_COOKIE, + HTTP2_HEADER_STRICT_TRANSPORT_SECURITY, + HTTP2_HEADER_VARY, + HTTP2_HEADER_VIA, + HTTP2_HEADER_WWW_AUTHENTICATE +].forEach((name) => { + assert.doesNotThrow(() => mapToHeaders({[name]: [1, 2, 3]}), name); +}); const regex = /^HTTP\/1 Connection specific headers are forbidden$/; -['connection', - 'upgrade', - 'http2-settings', - 'te', - 'transfer-encoding', - 'proxy-connection', - 'keep-alive', - 'Connection', - 'Upgrade', - 'HTTP2-Settings', - 'TE', - 'Transfer-Encoding', - 'Proxy-Connection', - 'Keep-Alive'].forEach((i) => { - assert.throws(() => mapToHeaders({[i]: 'abc'}), - common.expectsError({ - code: 'ERR_HTTP2_INVALID_CONNECTION_HEADERS', - message: regex - }), i); - }); +[ + HTTP2_HEADER_CONNECTION, + HTTP2_HEADER_UPGRADE, + HTTP2_HEADER_HTTP2_SETTINGS, + HTTP2_HEADER_TE, + HTTP2_HEADER_TRANSFER_ENCODING, + HTTP2_HEADER_HOST, + HTTP2_HEADER_PROXY_CONNECTION, + HTTP2_HEADER_KEEP_ALIVE, + 'Connection', + 'Upgrade', + 'HTTP2-Settings', + 'TE', + 'Transfer-Encoding', + 'Proxy-Connection', + 'Keep-Alive' +].forEach((name) => { + assert.throws(() => mapToHeaders({[name]: 'abc'}), + common.expectsError({ + code: 'ERR_HTTP2_INVALID_CONNECTION_HEADERS', + message: regex + })); +}); assert.doesNotThrow(() => mapToHeaders({ te: 'trailers' }));