+196
-2
lines changedFilter options
+196
-2
lines changed Original file line number Diff line number Diff line change
@@ -2919,6 +2919,10 @@ changes:
2919
2919
a server should wait when an [`'unknownProtocol'`][] is emitted. If the
2920
2920
socket has not been destroyed by that time the server will destroy it.
2921
2921
**Default:** `10000`.
2922
+
* `strictFieldWhitespaceValidation` {boolean} If `true`, it turns on strict leading
2923
+
and trailing whitespace validation for HTTP/2 header field names and values
2924
+
as per [RFC-9113](https://www.rfc-editor.org/rfc/rfc9113.html#section-8.2.1).
2925
+
**Default:** `true`.
2922
2926
* ...: Any [`net.createServer()`][] option can be provided.
2923
2927
* `onRequestHandler` {Function} See [Compatibility API][]
2924
2928
* Returns: {Http2Server}
@@ -3090,6 +3094,10 @@ changes:
3090
3094
a server should wait when an [`'unknownProtocol'`][] event is emitted. If
3091
3095
the socket has not been destroyed by that time the server will destroy it.
3092
3096
**Default:** `10000`.
3097
+
* `strictFieldWhitespaceValidation` {boolean} If `true`, it turns on strict leading
3098
+
and trailing whitespace validation for HTTP/2 header field names and values
3099
+
as per [RFC-9113](https://www.rfc-editor.org/rfc/rfc9113.html#section-8.2.1).
3100
+
**Default:** `true`.
3093
3101
* `onRequestHandler` {Function} See [Compatibility API][]
3094
3102
* Returns: {Http2SecureServer}
3095
3103
@@ -3245,6 +3253,10 @@ changes:
3245
3253
a server should wait when an [`'unknownProtocol'`][] event is emitted. If
3246
3254
the socket has not been destroyed by that time the server will destroy it.
3247
3255
**Default:** `10000`.
3256
+
* `strictFieldWhitespaceValidation` {boolean} If `true`, it turns on strict leading
3257
+
and trailing whitespace validation for HTTP/2 header field names and values
3258
+
as per [RFC-9113](https://www.rfc-editor.org/rfc/rfc9113.html#section-8.2.1).
3259
+
**Default:** `true`.
3248
3260
* `listener` {Function} Will be registered as a one-time listener of the
3249
3261
[`'connect'`][] event.
3250
3262
* Returns: {ClientHttp2Session}
Original file line number Diff line number Diff line change
@@ -229,7 +229,8 @@ const IDX_OPTIONS_MAX_SESSION_MEMORY = 8;
229
229
const IDX_OPTIONS_MAX_SETTINGS = 9;
230
230
const IDX_OPTIONS_STREAM_RESET_RATE = 10;
231
231
const IDX_OPTIONS_STREAM_RESET_BURST = 11;
232
-
const IDX_OPTIONS_FLAGS = 12;
232
+
const IDX_OPTIONS_STRICT_HTTP_FIELD_WHITESPACE_VALIDATION = 12;
233
+
const IDX_OPTIONS_FLAGS = 13;
233
234
234
235
function updateOptionsBuffer(options) {
235
236
let flags = 0;
@@ -293,6 +294,13 @@ function updateOptionsBuffer(options) {
293
294
optionsBuffer[IDX_OPTIONS_STREAM_RESET_BURST] =
294
295
MathMax(1, options.streamResetBurst);
295
296
}
297
+
298
+
if (typeof options.strictFieldWhitespaceValidation === 'boolean') {
299
+
flags |= (1 << IDX_OPTIONS_STRICT_HTTP_FIELD_WHITESPACE_VALIDATION);
300
+
optionsBuffer[IDX_OPTIONS_STRICT_HTTP_FIELD_WHITESPACE_VALIDATION] =
301
+
options.strictFieldWhitespaceValidation === true ? 0 : 1;
302
+
}
303
+
296
304
optionsBuffer[IDX_OPTIONS_FLAGS] = flags;
297
305
}
298
306
Original file line number Diff line number Diff line change
@@ -159,6 +159,12 @@ Http2Options::Http2Options(Http2State* http2_state, SessionType type) {
159
159
buffer[IDX_OPTIONS_PEER_MAX_CONCURRENT_STREAMS]);
160
160
}
161
161
162
+
// Validate headers in accordance to RFC-9113
163
+
if (flags & (1 << IDX_OPTIONS_STRICT_HTTP_FIELD_WHITESPACE_VALIDATION)) {
164
+
nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(
165
+
option, buffer[IDX_OPTIONS_STRICT_HTTP_FIELD_WHITESPACE_VALIDATION]);
166
+
}
167
+
162
168
// The padding strategy sets the mechanism by which we determine how much
163
169
// additional frame padding to apply to DATA and HEADERS frames. Currently
164
170
// this is set on a per-session basis, but eventually we may switch to
Original file line number Diff line number Diff line change
@@ -60,6 +60,7 @@ namespace http2 {
60
60
IDX_OPTIONS_MAX_SETTINGS,
61
61
IDX_OPTIONS_STREAM_RESET_RATE,
62
62
IDX_OPTIONS_STREAM_RESET_BURST,
63
+
IDX_OPTIONS_STRICT_HTTP_FIELD_WHITESPACE_VALIDATION,
63
64
IDX_OPTIONS_FLAGS
64
65
};
65
66
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
1
+
'use strict';
2
+
3
+
const common = require('../common');
4
+
if (!common.hasCrypto)
5
+
common.skip('missing crypto');
6
+
const assert = require('assert');
7
+
const http2 = require('http2');
8
+
const body =
9
+
'<html><head></head><body><h1>this is some data</h2></body></html>';
10
+
11
+
const server = http2.createServer((req, res) => {
12
+
res.setHeader('foobar', 'baz ');
13
+
res.setHeader('X-POWERED-BY', 'node-test\t');
14
+
res.setHeader('x-h2-header', '\tconnection-test');
15
+
res.setHeader('x-h2-header-2', ' connection-test');
16
+
res.setHeader('x-h2-header-3', 'connection-test ');
17
+
res.end(body);
18
+
});
19
+
20
+
const server2 = http2.createServer((req, res) => {
21
+
res.setHeader('foobar', 'baz ');
22
+
res.setHeader('X-POWERED-BY', 'node-test\t');
23
+
res.setHeader('x-h2-header', '\tconnection-test');
24
+
res.setHeader('x-h2-header-2', ' connection-test');
25
+
res.setHeader('x-h2-header-3', 'connection-test ');
26
+
res.end(body);
27
+
});
28
+
29
+
server.listen(0, common.mustCall(() => {
30
+
server2.listen(0, common.mustCall(() => {
31
+
const client = http2.connect(`http://localhost:${server.address().port}`);
32
+
const client2 = http2.connect(`http://localhost:${server2.address().port}`, { strictFieldWhitespaceValidation: false });
33
+
const headers = { ':path': '/' };
34
+
const req = client.request(headers);
35
+
36
+
req.setEncoding('utf8');
37
+
req.on('response', common.mustCall(function(headers) {
38
+
assert.strictEqual(headers.foobar, undefined);
39
+
assert.strictEqual(headers['x-powered-by'], undefined);
40
+
assert.strictEqual(headers['x-powered-by'], undefined);
41
+
assert.strictEqual(headers['x-h2-header'], undefined);
42
+
assert.strictEqual(headers['x-h2-header-2'], undefined);
43
+
assert.strictEqual(headers['x-h2-header-3'], undefined);
44
+
}));
45
+
46
+
let data = '';
47
+
req.on('data', (d) => data += d);
48
+
req.on('end', () => {
49
+
assert.strictEqual(body, data);
50
+
client.close();
51
+
client.on('close', common.mustCall(() => {
52
+
server.close();
53
+
}));
54
+
55
+
const req2 = client2.request(headers);
56
+
let data2 = '';
57
+
req2.setEncoding('utf8');
58
+
req2.on('response', common.mustCall(function(headers) {
59
+
assert.strictEqual(headers.foobar, 'baz ');
60
+
assert.strictEqual(headers['x-powered-by'], 'node-test\t');
61
+
assert.strictEqual(headers['x-h2-header'], '\tconnection-test');
62
+
assert.strictEqual(headers['x-h2-header-2'], ' connection-test');
63
+
assert.strictEqual(headers['x-h2-header-3'], 'connection-test ');
64
+
}));
65
+
req2.on('data', (d) => data2 += d);
66
+
req2.on('end', () => {
67
+
assert.strictEqual(body, data2);
68
+
client2.close();
69
+
client2.on('close', common.mustCall(() => {
70
+
server2.close();
71
+
}));
72
+
});
73
+
req2.end();
74
+
});
75
+
76
+
req.end();
77
+
}));
78
+
}));
79
+
80
+
server.on('error', common.mustNotCall());
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
1
+
'use strict';
2
+
3
+
const common = require('../common');
4
+
if (!common.hasCrypto)
5
+
common.skip('missing crypto');
6
+
const assert = require('assert');
7
+
const http2 = require('http2');
8
+
const body =
9
+
'<html><head></head><body><h1>this is some data</h2></body></html>';
10
+
11
+
const server = http2.createServer((req, res) => {
12
+
assert.strictEqual(req.headers['x-powered-by'], undefined);
13
+
assert.strictEqual(req.headers.foobar, undefined);
14
+
assert.strictEqual(req.headers['x-h2-header'], undefined);
15
+
assert.strictEqual(req.headers['x-h2-header-2'], undefined);
16
+
assert.strictEqual(req.headers['x-h2-header-3'], undefined);
17
+
assert.strictEqual(req.headers['x-h2-header-4'], undefined);
18
+
res.writeHead(200);
19
+
res.end(body);
20
+
});
21
+
22
+
const server2 = http2.createServer({ strictFieldWhitespaceValidation: false }, (req, res) => {
23
+
assert.strictEqual(req.headers.foobar, 'baz ');
24
+
assert.strictEqual(req.headers['x-powered-by'], 'node-test\t');
25
+
assert.strictEqual(req.headers['x-h2-header'], '\tconnection-test');
26
+
assert.strictEqual(req.headers['x-h2-header-2'], ' connection-test');
27
+
assert.strictEqual(req.headers['x-h2-header-3'], 'connection-test ');
28
+
assert.strictEqual(req.headers['x-h2-header-4'], 'connection-test\t');
29
+
res.writeHead(200);
30
+
res.end(body);
31
+
});
32
+
33
+
server.listen(0, common.mustCall(() => {
34
+
server2.listen(0, common.mustCall(() => {
35
+
const client = http2.connect(`http://localhost:${server.address().port}`);
36
+
const client2 = http2.connect(`http://localhost:${server2.address().port}`);
37
+
const headers = {
38
+
'foobar': 'baz ',
39
+
':path': '/',
40
+
'x-powered-by': 'node-test\t',
41
+
'x-h2-header': '\tconnection-test',
42
+
'x-h2-header-2': ' connection-test',
43
+
'x-h2-header-3': 'connection-test ',
44
+
'x-h2-header-4': 'connection-test\t'
45
+
};
46
+
const req = client.request(headers);
47
+
48
+
req.setEncoding('utf8');
49
+
req.on('response', common.mustCall(function(headers) {
50
+
assert.strictEqual(headers[':status'], 200);
51
+
}));
52
+
53
+
let data = '';
54
+
req.on('data', (d) => data += d);
55
+
req.on('end', () => {
56
+
assert.strictEqual(body, data);
57
+
client.close();
58
+
client.on('close', common.mustCall(() => {
59
+
server.close();
60
+
}));
61
+
62
+
const req2 = client2.request(headers);
63
+
let data2 = '';
64
+
req2.setEncoding('utf8');
65
+
req2.on('response', common.mustCall(function(headers) {
66
+
assert.strictEqual(headers[':status'], 200);
67
+
}));
68
+
req2.on('data', (d) => data2 += d);
69
+
req2.on('end', () => {
70
+
assert.strictEqual(body, data2);
71
+
client2.close();
72
+
client2.on('close', common.mustCall(() => {
73
+
server2.close();
74
+
}));
75
+
});
76
+
req2.end();
77
+
});
78
+
79
+
req.end();
80
+
}));
81
+
}));
82
+
83
+
server.on('error', common.mustNotCall());
Original file line number Diff line number Diff line change
@@ -25,7 +25,8 @@ const IDX_OPTIONS_MAX_SESSION_MEMORY = 8;
25
25
const IDX_OPTIONS_MAX_SETTINGS = 9;
26
26
const IDX_OPTIONS_STREAM_RESET_RATE = 10;
27
27
const IDX_OPTIONS_STREAM_RESET_BURST = 11;
28
-
const IDX_OPTIONS_FLAGS = 12;
28
+
const IDX_OPTIONS_STRICT_HTTP_FIELD_WHITESPACE_VALIDATION = 12;
29
+
const IDX_OPTIONS_FLAGS = 13;
29
30
30
31
{
31
32
updateOptionsBuffer({
@@ -41,6 +42,7 @@ const IDX_OPTIONS_FLAGS = 12;
41
42
maxSettings: 10,
42
43
streamResetRate: 11,
43
44
streamResetBurst: 12,
45
+
strictFieldWhitespaceValidation: false
44
46
});
45
47
46
48
strictEqual(optionsBuffer[IDX_OPTIONS_MAX_DEFLATE_DYNAMIC_TABLE_SIZE], 1);
@@ -55,6 +57,7 @@ const IDX_OPTIONS_FLAGS = 12;
55
57
strictEqual(optionsBuffer[IDX_OPTIONS_MAX_SETTINGS], 10);
56
58
strictEqual(optionsBuffer[IDX_OPTIONS_STREAM_RESET_RATE], 11);
57
59
strictEqual(optionsBuffer[IDX_OPTIONS_STREAM_RESET_BURST], 12);
60
+
strictEqual(optionsBuffer[IDX_OPTIONS_STRICT_HTTP_FIELD_WHITESPACE_VALIDATION], 1);
58
61
59
62
const flags = optionsBuffer[IDX_OPTIONS_FLAGS];
60
63
@@ -69,6 +72,7 @@ const IDX_OPTIONS_FLAGS = 12;
69
72
ok(flags & (1 << IDX_OPTIONS_MAX_SETTINGS));
70
73
ok(flags & (1 << IDX_OPTIONS_STREAM_RESET_RATE));
71
74
ok(flags & (1 << IDX_OPTIONS_STREAM_RESET_BURST));
75
+
ok(flags & (1 << IDX_OPTIONS_STRICT_HTTP_FIELD_WHITESPACE_VALIDATION));
72
76
}
73
77
74
78
{
You can’t perform that action at this time.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4