• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Flags: --expose-internals
2'use strict';
3
4// Tests the internal utility functions that are used to prepare headers
5// to pass to the internal binding layer and to build a header object.
6
7const common = require('../common');
8if (!common.hasCrypto)
9  common.skip('missing crypto');
10const assert = require('assert');
11const { mapToHeaders, toHeaderObject } = require('internal/http2/util');
12const { internalBinding } = require('internal/test/binding');
13const {
14  HTTP2_HEADER_STATUS,
15  HTTP2_HEADER_METHOD,
16  HTTP2_HEADER_AUTHORITY,
17  HTTP2_HEADER_SCHEME,
18  HTTP2_HEADER_PATH,
19  HTTP2_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS,
20  HTTP2_HEADER_ACCESS_CONTROL_MAX_AGE,
21  HTTP2_HEADER_ACCESS_CONTROL_REQUEST_METHOD,
22  HTTP2_HEADER_AGE,
23  HTTP2_HEADER_AUTHORIZATION,
24  HTTP2_HEADER_CONTENT_ENCODING,
25  HTTP2_HEADER_CONTENT_LANGUAGE,
26  HTTP2_HEADER_CONTENT_LENGTH,
27  HTTP2_HEADER_CONTENT_LOCATION,
28  HTTP2_HEADER_CONTENT_MD5,
29  HTTP2_HEADER_CONTENT_RANGE,
30  HTTP2_HEADER_CONTENT_TYPE,
31  HTTP2_HEADER_DATE,
32  HTTP2_HEADER_DNT,
33  HTTP2_HEADER_ETAG,
34  HTTP2_HEADER_EXPIRES,
35  HTTP2_HEADER_FROM,
36  HTTP2_HEADER_IF_MATCH,
37  HTTP2_HEADER_IF_MODIFIED_SINCE,
38  HTTP2_HEADER_IF_NONE_MATCH,
39  HTTP2_HEADER_IF_RANGE,
40  HTTP2_HEADER_IF_UNMODIFIED_SINCE,
41  HTTP2_HEADER_LAST_MODIFIED,
42  HTTP2_HEADER_LOCATION,
43  HTTP2_HEADER_MAX_FORWARDS,
44  HTTP2_HEADER_PROXY_AUTHORIZATION,
45  HTTP2_HEADER_RANGE,
46  HTTP2_HEADER_REFERER,
47  HTTP2_HEADER_RETRY_AFTER,
48  HTTP2_HEADER_TK,
49  HTTP2_HEADER_UPGRADE_INSECURE_REQUESTS,
50  HTTP2_HEADER_USER_AGENT,
51  HTTP2_HEADER_X_CONTENT_TYPE_OPTIONS,
52
53  HTTP2_HEADER_ACCEPT_CHARSET,
54  HTTP2_HEADER_ACCEPT_ENCODING,
55  HTTP2_HEADER_ACCEPT_LANGUAGE,
56  HTTP2_HEADER_ACCEPT_RANGES,
57  HTTP2_HEADER_ACCEPT,
58  HTTP2_HEADER_ACCESS_CONTROL_ALLOW_HEADERS,
59  HTTP2_HEADER_ACCESS_CONTROL_ALLOW_METHODS,
60  HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
61  HTTP2_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS,
62  HTTP2_HEADER_ACCESS_CONTROL_REQUEST_HEADERS,
63  HTTP2_HEADER_ALLOW,
64  HTTP2_HEADER_CACHE_CONTROL,
65  HTTP2_HEADER_CONTENT_DISPOSITION,
66  HTTP2_HEADER_COOKIE,
67  HTTP2_HEADER_EXPECT,
68  HTTP2_HEADER_FORWARDED,
69  HTTP2_HEADER_LINK,
70  HTTP2_HEADER_PREFER,
71  HTTP2_HEADER_PROXY_AUTHENTICATE,
72  HTTP2_HEADER_REFRESH,
73  HTTP2_HEADER_SERVER,
74  HTTP2_HEADER_SET_COOKIE,
75  HTTP2_HEADER_STRICT_TRANSPORT_SECURITY,
76  HTTP2_HEADER_TRAILER,
77  HTTP2_HEADER_VARY,
78  HTTP2_HEADER_VIA,
79  HTTP2_HEADER_WARNING,
80  HTTP2_HEADER_WWW_AUTHENTICATE,
81  HTTP2_HEADER_X_FRAME_OPTIONS,
82
83  HTTP2_HEADER_CONNECTION,
84  HTTP2_HEADER_UPGRADE,
85  HTTP2_HEADER_HTTP2_SETTINGS,
86  HTTP2_HEADER_TE,
87  HTTP2_HEADER_TRANSFER_ENCODING,
88  HTTP2_HEADER_HOST,
89  HTTP2_HEADER_KEEP_ALIVE,
90  HTTP2_HEADER_PROXY_CONNECTION
91} = internalBinding('http2').constants;
92
93{
94  const headers = {
95    'abc': 1,
96    ':status': 200,
97    ':path': 'abc',
98    'xyz': [1, '2', { toString() { return '3'; } }, 4],
99    'foo': [],
100    'BAR': [1]
101  };
102
103  assert.deepStrictEqual(
104    mapToHeaders(headers),
105    [ [ ':path', 'abc', ':status', '200', 'abc', '1', 'xyz', '1', 'xyz', '2',
106        'xyz', '3', 'xyz', '4', 'bar', '1', '' ].join('\0'), 8 ]
107  );
108}
109
110{
111  const headers = {
112    'abc': 1,
113    ':path': 'abc',
114    ':status': [200],
115    ':authority': [],
116    'xyz': [1, 2, 3, 4]
117  };
118
119  assert.deepStrictEqual(
120    mapToHeaders(headers),
121    [ [ ':status', '200', ':path', 'abc', 'abc', '1', 'xyz', '1', 'xyz', '2',
122        'xyz', '3', 'xyz', '4', '' ].join('\0'), 7 ]
123  );
124}
125
126{
127  const headers = {
128    'abc': 1,
129    ':path': 'abc',
130    'xyz': [1, 2, 3, 4],
131    '': 1,
132    ':status': 200,
133    [Symbol('test')]: 1 // Symbol keys are ignored
134  };
135
136  assert.deepStrictEqual(
137    mapToHeaders(headers),
138    [ [ ':status', '200', ':path', 'abc', 'abc', '1', 'xyz', '1', 'xyz', '2',
139        'xyz', '3', 'xyz', '4', '' ].join('\0'), 7 ]
140  );
141}
142
143{
144  // Only own properties are used
145  const base = { 'abc': 1 };
146  const headers = Object.create(base);
147  headers[':path'] = 'abc';
148  headers.xyz = [1, 2, 3, 4];
149  headers.foo = [];
150  headers[':status'] = 200;
151
152  assert.deepStrictEqual(
153    mapToHeaders(headers),
154    [ [ ':status', '200', ':path', 'abc', 'xyz', '1', 'xyz', '2', 'xyz', '3',
155        'xyz', '4', '' ].join('\0'), 6 ]
156  );
157}
158
159{
160  // Arrays containing a single set-cookie value are handled correctly
161  // (https://github.com/nodejs/node/issues/16452)
162  const headers = {
163    'set-cookie': ['foo=bar']
164  };
165  assert.deepStrictEqual(
166    mapToHeaders(headers),
167    [ [ 'set-cookie', 'foo=bar', '' ].join('\0'), 1 ]
168  );
169}
170
171{
172  // pseudo-headers are only allowed a single value
173  const headers = {
174    ':status': 200,
175    ':statuS': 204,
176  };
177
178  assert.throws(() => mapToHeaders(headers), {
179    code: 'ERR_HTTP2_HEADER_SINGLE_VALUE',
180    name: 'TypeError',
181    message: 'Header field ":status" must only have a single value'
182  });
183}
184
185// The following are not allowed to have multiple values
186[
187  HTTP2_HEADER_STATUS,
188  HTTP2_HEADER_METHOD,
189  HTTP2_HEADER_AUTHORITY,
190  HTTP2_HEADER_SCHEME,
191  HTTP2_HEADER_PATH,
192  HTTP2_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS,
193  HTTP2_HEADER_ACCESS_CONTROL_MAX_AGE,
194  HTTP2_HEADER_ACCESS_CONTROL_REQUEST_METHOD,
195  HTTP2_HEADER_AGE,
196  HTTP2_HEADER_AUTHORIZATION,
197  HTTP2_HEADER_CONTENT_ENCODING,
198  HTTP2_HEADER_CONTENT_LANGUAGE,
199  HTTP2_HEADER_CONTENT_LENGTH,
200  HTTP2_HEADER_CONTENT_LOCATION,
201  HTTP2_HEADER_CONTENT_MD5,
202  HTTP2_HEADER_CONTENT_RANGE,
203  HTTP2_HEADER_CONTENT_TYPE,
204  HTTP2_HEADER_DATE,
205  HTTP2_HEADER_DNT,
206  HTTP2_HEADER_ETAG,
207  HTTP2_HEADER_EXPIRES,
208  HTTP2_HEADER_FROM,
209  HTTP2_HEADER_IF_MATCH,
210  HTTP2_HEADER_IF_MODIFIED_SINCE,
211  HTTP2_HEADER_IF_NONE_MATCH,
212  HTTP2_HEADER_IF_RANGE,
213  HTTP2_HEADER_IF_UNMODIFIED_SINCE,
214  HTTP2_HEADER_LAST_MODIFIED,
215  HTTP2_HEADER_LOCATION,
216  HTTP2_HEADER_MAX_FORWARDS,
217  HTTP2_HEADER_PROXY_AUTHORIZATION,
218  HTTP2_HEADER_RANGE,
219  HTTP2_HEADER_REFERER,
220  HTTP2_HEADER_RETRY_AFTER,
221  HTTP2_HEADER_TK,
222  HTTP2_HEADER_UPGRADE_INSECURE_REQUESTS,
223  HTTP2_HEADER_USER_AGENT,
224  HTTP2_HEADER_X_CONTENT_TYPE_OPTIONS
225].forEach((name) => {
226  const msg = `Header field "${name}" must only have a single value`;
227  assert.throws(() => mapToHeaders({ [name]: [1, 2, 3] }), {
228    code: 'ERR_HTTP2_HEADER_SINGLE_VALUE',
229    message: msg
230  });
231});
232
233[
234  HTTP2_HEADER_ACCEPT_CHARSET,
235  HTTP2_HEADER_ACCEPT_ENCODING,
236  HTTP2_HEADER_ACCEPT_LANGUAGE,
237  HTTP2_HEADER_ACCEPT_RANGES,
238  HTTP2_HEADER_ACCEPT,
239  HTTP2_HEADER_ACCESS_CONTROL_ALLOW_HEADERS,
240  HTTP2_HEADER_ACCESS_CONTROL_ALLOW_METHODS,
241  HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
242  HTTP2_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS,
243  HTTP2_HEADER_ACCESS_CONTROL_REQUEST_HEADERS,
244  HTTP2_HEADER_ALLOW,
245  HTTP2_HEADER_CACHE_CONTROL,
246  HTTP2_HEADER_CONTENT_DISPOSITION,
247  HTTP2_HEADER_COOKIE,
248  HTTP2_HEADER_EXPECT,
249  HTTP2_HEADER_FORWARDED,
250  HTTP2_HEADER_LINK,
251  HTTP2_HEADER_PREFER,
252  HTTP2_HEADER_PROXY_AUTHENTICATE,
253  HTTP2_HEADER_REFRESH,
254  HTTP2_HEADER_SERVER,
255  HTTP2_HEADER_SET_COOKIE,
256  HTTP2_HEADER_STRICT_TRANSPORT_SECURITY,
257  HTTP2_HEADER_TRAILER,
258  HTTP2_HEADER_VARY,
259  HTTP2_HEADER_VIA,
260  HTTP2_HEADER_WARNING,
261  HTTP2_HEADER_WWW_AUTHENTICATE,
262  HTTP2_HEADER_X_FRAME_OPTIONS
263].forEach((name) => {
264  assert(!(mapToHeaders({ [name]: [1, 2, 3] }) instanceof Error), name);
265});
266
267[
268  HTTP2_HEADER_CONNECTION,
269  HTTP2_HEADER_UPGRADE,
270  HTTP2_HEADER_HTTP2_SETTINGS,
271  HTTP2_HEADER_TE,
272  HTTP2_HEADER_TRANSFER_ENCODING,
273  HTTP2_HEADER_HOST,
274  HTTP2_HEADER_PROXY_CONNECTION,
275  HTTP2_HEADER_KEEP_ALIVE,
276  'Connection',
277  'Upgrade',
278  'HTTP2-Settings',
279  'TE',
280  'Transfer-Encoding',
281  'Proxy-Connection',
282  'Keep-Alive'
283].forEach((name) => {
284  assert.throws(() => mapToHeaders({ [name]: 'abc' }), {
285    code: 'ERR_HTTP2_INVALID_CONNECTION_HEADERS',
286    name: 'TypeError',
287    message: 'HTTP/1 Connection specific headers are forbidden: ' +
288             `"${name.toLowerCase()}"`
289  });
290});
291
292assert.throws(() => mapToHeaders({ [HTTP2_HEADER_TE]: ['abc'] }), {
293  code: 'ERR_HTTP2_INVALID_CONNECTION_HEADERS',
294  name: 'TypeError',
295  message: 'HTTP/1 Connection specific headers are forbidden: ' +
296           `"${HTTP2_HEADER_TE}"`
297});
298
299assert.throws(
300  () => mapToHeaders({ [HTTP2_HEADER_TE]: ['abc', 'trailers'] }), {
301    code: 'ERR_HTTP2_INVALID_CONNECTION_HEADERS',
302    name: 'TypeError',
303    message: 'HTTP/1 Connection specific headers are forbidden: ' +
304             `"${HTTP2_HEADER_TE}"`
305  });
306
307// These should not throw
308mapToHeaders({ te: 'trailers' });
309mapToHeaders({ te: ['trailers'] });
310
311
312{
313  const rawHeaders = [
314    ':status', '200',
315    'cookie', 'foo',
316    'set-cookie', 'sc1',
317    'age', '10',
318    'x-multi', 'first'
319  ];
320  const headers = toHeaderObject(rawHeaders);
321  assert.strictEqual(headers[':status'], 200);
322  assert.strictEqual(headers.cookie, 'foo');
323  assert.deepStrictEqual(headers['set-cookie'], ['sc1']);
324  assert.strictEqual(headers.age, '10');
325  assert.strictEqual(headers['x-multi'], 'first');
326}
327
328{
329  const rawHeaders = [
330    ':status', '200',
331    ':status', '400',
332    'cookie', 'foo',
333    'cookie', 'bar',
334    'set-cookie', 'sc1',
335    'set-cookie', 'sc2',
336    'age', '10',
337    'age', '20',
338    'x-multi', 'first',
339    'x-multi', 'second'
340  ];
341  const headers = toHeaderObject(rawHeaders);
342  assert.strictEqual(headers[':status'], 200);
343  assert.strictEqual(headers.cookie, 'foo; bar');
344  assert.deepStrictEqual(headers['set-cookie'], ['sc1', 'sc2']);
345  assert.strictEqual(headers.age, '10');
346  assert.strictEqual(headers['x-multi'], 'first, second');
347}
348