Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit fb4f71b

Browse files
lundibunditargos
authored andcommittedJan 13, 2020
http2: make maximum tolerated rejected streams configurable
PR-URL: #30534 Fixes: #30505 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: David Carlier <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 518fd8f commit fb4f71b

File tree

6 files changed

+70
-2
lines changed

6 files changed

+70
-2
lines changed
 

‎doc/api/http2.md

+18
Original file line numberDiff line numberDiff line change
@@ -1939,6 +1939,9 @@ error will be thrown.
19391939
<!-- YAML
19401940
added: v8.4.0
19411941
changes:
1942+
- version: REPLACEME
1943+
pr-url: https://github.com/nodejs/node/pull/30534
1944+
description: Added `maxSessionRejectedStreams` option with a default of 100.
19421945
- version: REPLACEME
19431946
pr-url: https://github.com/nodejs/node/pull/30534
19441947
description: Added `maxSessionInvalidFrames` option with a default of 1000.
@@ -2005,6 +2008,12 @@ changes:
20052008
* `maxSessionInvalidFrames` {integer} Sets the maximum number of invalid
20062009
frames that will be tolerated before the session is closed.
20072010
**Default:** `1000`.
2011+
* `maxSessionRejectedStreams` {integer} Sets the maximum number of rejected
2012+
upon creation streams that will be tolerated before the session is closed.
2013+
Each rejection is associated with an `NGHTTP2_ENHANCE_YOUR_CALM`
2014+
error that should tell the peer to not open any more streams, continuing
2015+
to open streams is therefore regarded as a sign of a misbehaving peer.
2016+
**Default:** `100`.
20082017
* `selectPadding` {Function} When `options.paddingStrategy` is equal to
20092018
`http2.constants.PADDING_STRATEGY_CALLBACK`, provides the callback function
20102019
used to determine the padding. See [Using `options.selectPadding()`][].
@@ -2060,6 +2069,9 @@ server.listen(80);
20602069
<!-- YAML
20612070
added: v8.4.0
20622071
changes:
2072+
- version: REPLACEME
2073+
pr-url: https://github.com/nodejs/node/pull/30534
2074+
description: Added `maxSessionRejectedStreams` option with a default of 100.
20632075
- version: REPLACEME
20642076
pr-url: https://github.com/nodejs/node/pull/30534
20652077
description: Added `maxSessionInvalidFrames` option with a default of 1000.
@@ -2126,6 +2138,12 @@ changes:
21262138
* `maxSessionInvalidFrames` {integer} Sets the maximum number of invalid
21272139
frames that will be tolerated before the session is closed.
21282140
**Default:** `1000`.
2141+
* `maxSessionRejectedStreams` {integer} Sets the maximum number of rejected
2142+
upon creation streams that will be tolerated before the session is closed.
2143+
Each rejection is associated with an `NGHTTP2_ENHANCE_YOUR_CALM`
2144+
error that should tell the peer to not open any more streams, continuing
2145+
to open streams is therefore regarded as a sign of a misbehaving peer.
2146+
**Default:** `100`.
21292147
* `selectPadding` {Function} When `options.paddingStrategy` is equal to
21302148
`http2.constants.PADDING_STRATEGY_CALLBACK`, provides the callback function
21312149
used to determine the padding. See [Using `options.selectPadding()`][].

‎lib/internal/http2/core.js

+14
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ const {
212212
kSessionPriorityListenerCount,
213213
kSessionFrameErrorListenerCount,
214214
kSessionMaxInvalidFrames,
215+
kSessionMaxRejectedStreams,
215216
kSessionUint8FieldCount,
216217
kSessionHasRemoteSettingsListeners,
217218
kSessionRemoteSettingsIsUpToDate,
@@ -970,6 +971,12 @@ function setupHandle(socket, type, options) {
970971
uint32[0] = options.maxSessionInvalidFrames;
971972
}
972973

974+
if (isUint32(options.maxSessionRejectedStreams)) {
975+
const uint32 = new Uint32Array(
976+
this[kNativeFields].buffer, kSessionMaxRejectedStreams, 1);
977+
uint32[0] = options.maxSessionRejectedStreams;
978+
}
979+
973980
const settings = typeof options.settings === 'object' ?
974981
options.settings : {};
975982

@@ -2804,6 +2811,13 @@ function initializeOptions(options) {
28042811
if (options.maxSessionInvalidFrames !== undefined)
28052812
validateUint32(options.maxSessionInvalidFrames, 'maxSessionInvalidFrames');
28062813

2814+
if (options.maxSessionRejectedStreams !== undefined) {
2815+
validateUint32(
2816+
options.maxSessionRejectedStreams,
2817+
'maxSessionRejectedStreams'
2818+
);
2819+
}
2820+
28072821
// Used only with allowHTTP1
28082822
options.Http1IncomingMessage = options.Http1IncomingMessage ||
28092823
http.IncomingMessage;

‎src/node_http2.cc

+3-1
Original file line numberDiff line numberDiff line change
@@ -952,7 +952,8 @@ int Http2Session::OnBeginHeadersCallback(nghttp2_session* handle,
952952
if (UNLIKELY(!session->CanAddStream() ||
953953
Http2Stream::New(session, id, frame->headers.cat) ==
954954
nullptr)) {
955-
if (session->rejected_stream_count_++ > 100 &&
955+
if (session->rejected_stream_count_++ >
956+
session->js_fields_.max_rejected_streams &&
956957
!IsReverted(SECURITY_REVERT_CVE_2019_9514)) {
957958
return NGHTTP2_ERR_CALLBACK_FAILURE;
958959
}
@@ -3111,6 +3112,7 @@ void Initialize(Local<Object> target,
31113112
NODE_DEFINE_CONSTANT(target, kSessionPriorityListenerCount);
31123113
NODE_DEFINE_CONSTANT(target, kSessionFrameErrorListenerCount);
31133114
NODE_DEFINE_CONSTANT(target, kSessionMaxInvalidFrames);
3115+
NODE_DEFINE_CONSTANT(target, kSessionMaxRejectedStreams);
31143116
NODE_DEFINE_CONSTANT(target, kSessionUint8FieldCount);
31153117

31163118
NODE_DEFINE_CONSTANT(target, kSessionHasRemoteSettingsListeners);

‎src/node_http2.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,7 @@ typedef struct {
680680
uint8_t priority_listener_count;
681681
uint8_t frame_error_listener_count;
682682
uint32_t max_invalid_frames = 1000;
683+
uint32_t max_rejected_streams = 100;
683684
} SessionJSFields;
684685

685686
// Indices for js_fields_, which serves as a way to communicate data with JS
@@ -693,6 +694,7 @@ enum SessionUint8Fields {
693694
kSessionFrameErrorListenerCount =
694695
offsetof(SessionJSFields, frame_error_listener_count),
695696
kSessionMaxInvalidFrames = offsetof(SessionJSFields, max_invalid_frames),
697+
kSessionMaxRejectedStreams = offsetof(SessionJSFields, max_rejected_streams),
696698
kSessionUint8FieldCount = sizeof(SessionJSFields)
697699
};
698700

@@ -1028,7 +1030,7 @@ class Http2Session : public AsyncWrap, public StreamListener {
10281030
// limit will result in the session being destroyed, as an indication of a
10291031
// misbehaving peer. This counter is reset once new streams are being
10301032
// accepted again.
1031-
int32_t rejected_stream_count_ = 0;
1033+
uint32_t rejected_stream_count_ = 0;
10321034
// Also use the invalid frame count as a measure for rejecting input frames.
10331035
uint32_t invalid_frame_count_ = 0;
10341036

‎test/parallel/test-http2-createsecureserver-options.js

+16
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,22 @@ Object.entries({
5252
},
5353
},
5454
],
55+
maxSessionRejectedStreams: [
56+
{
57+
val: -1,
58+
err: {
59+
name: 'RangeError',
60+
code: 'ERR_OUT_OF_RANGE',
61+
},
62+
},
63+
{
64+
val: Number.NEGATIVE_INFINITY,
65+
err: {
66+
name: 'RangeError',
67+
code: 'ERR_OUT_OF_RANGE',
68+
},
69+
},
70+
],
5571
}).forEach(([opt, tests]) => {
5672
tests.forEach(({ val, err }) => {
5773
assert.throws(

‎test/parallel/test-http2-createserver-options.js

+16
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,22 @@ Object.entries({
5252
},
5353
},
5454
],
55+
maxSessionRejectedStreams: [
56+
{
57+
val: -1,
58+
err: {
59+
name: 'RangeError',
60+
code: 'ERR_OUT_OF_RANGE',
61+
},
62+
},
63+
{
64+
val: Number.NEGATIVE_INFINITY,
65+
err: {
66+
name: 'RangeError',
67+
code: 'ERR_OUT_OF_RANGE',
68+
},
69+
},
70+
]
5571
}).forEach(([opt, tests]) => {
5672
tests.forEach(({ val, err }) => {
5773
assert.throws(

0 commit comments

Comments
 (0)
Please sign in to comment.