http2: add new connectionWindowSize option · nodejs/node@7702a50 · GitHub
Skip to content

Commit 7702a50

Browse files
committed
http2: add new connectionWindowSize option
Signed-off-by: Tim Perry <pimterry@gmail.com> PR-URL: #65619 Backport-PR-URL: #66167 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Yongsheng Zhang <zyszys98@gmail.com>
1 parent 4251b8b commit 7702a50

8 files changed

Lines changed: 212 additions & 35 deletions

File tree

‎doc/api/http2.md‎

Lines changed: 43 additions & 1 deletion

‎lib/internal/http2/core.js‎

Lines changed: 28 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,8 @@ function debugSessionObj(session, message, ...args) {
238238
const kMaxFrameSize = (2 ** 24) - 1;
239239
const kMaxInt = (2 ** 32) - 1;
240240
const kMaxInitialWindowSize = (2 ** 31) - 1; // HTTP/2 spec maximum
241+
// A connection window of 0 stalls the connection
242+
const kMinConnectionWindowSize = 1;
241243
const kMaxStreams = (2 ** 32) - 1;
242244
const kMaxALTSVC = (2 ** 14) - 2;
243245

@@ -3418,19 +3420,38 @@ function connectionListener(socket) {
34183420
this.emit('session', session);
34193421
}
34203422

3421-
function initializeOptions(options) {
3422-
assertIsObject(options, 'options');
3423-
options = { ...options };
3424-
assertIsObject(options.settings, 'options.settings');
3425-
options.settings = { ...options.settings };
3426-
3423+
// Validates the session options that apply to both client and server sessions
3424+
function validateSessionOptions(options) {
34273425
assertIsArray(options.remoteCustomSettings, 'options.remoteCustomSettings');
34283426
if (options.remoteCustomSettings) {
34293427
options.remoteCustomSettings = [ ...options.remoteCustomSettings ];
34303428
if (options.remoteCustomSettings.length > MAX_ADDITIONAL_SETTINGS)
34313429
throw new ERR_HTTP2_TOO_MANY_CUSTOM_SETTINGS();
34323430
}
34333431

3432+
if (options.strictSingleValueFields !== undefined) {
3433+
validateBoolean(
3434+
options.strictSingleValueFields,
3435+
'options.strictSingleValueFields',
3436+
);
3437+
} else {
3438+
options.strictSingleValueFields = true;
3439+
}
3440+
3441+
if (options.connectionWindowSize !== undefined) {
3442+
validateInt32(options.connectionWindowSize, 'options.connectionWindowSize',
3443+
kMinConnectionWindowSize, kMaxInitialWindowSize);
3444+
}
3445+
}
3446+
3447+
function initializeOptions(options) {
3448+
assertIsObject(options, 'options');
3449+
options = { ...options };
3450+
assertIsObject(options.settings, 'options.settings');
3451+
options.settings = { ...options.settings };
3452+
3453+
validateSessionOptions(options);
3454+
34343455
if (options.maxSessionInvalidFrames !== undefined)
34353456
validateUint32(options.maxSessionInvalidFrames, 'options.maxSessionInvalidFrames');
34363457

@@ -3447,16 +3468,6 @@ function initializeOptions(options) {
34473468
// TODO(danbev): is this a good default value?
34483469
options.unknownProtocolTimeout = 10000;
34493470

3450-
if (options.strictSingleValueFields !== undefined) {
3451-
validateBoolean(
3452-
options.strictSingleValueFields,
3453-
'options.strictSingleValueFields',
3454-
);
3455-
} else {
3456-
options.strictSingleValueFields = true;
3457-
}
3458-
3459-
34603471
// Initialize http1Options bag for HTTP/1 fallback when allowHTTP1 is true.
34613472
// This bag is passed to storeHTTPOptions() to configure HTTP/1 server
34623473
// behavior (timeouts, IncomingMessage/ServerResponse classes, etc.).
@@ -3668,21 +3679,7 @@ function connect(authority, options, listener) {
36683679
maxOriginSetSize = 128;
36693680
}
36703681

3671-
assertIsArray(options.remoteCustomSettings, 'options.remoteCustomSettings');
3672-
if (options.remoteCustomSettings) {
3673-
options.remoteCustomSettings = [ ...options.remoteCustomSettings ];
3674-
if (options.remoteCustomSettings.length > MAX_ADDITIONAL_SETTINGS)
3675-
throw new ERR_HTTP2_TOO_MANY_CUSTOM_SETTINGS();
3676-
}
3677-
3678-
if (options.strictSingleValueFields !== undefined) {
3679-
validateBoolean(
3680-
options.strictSingleValueFields,
3681-
'options.strictSingleValueFields',
3682-
);
3683-
} else {
3684-
options.strictSingleValueFields = true;
3685-
}
3682+
validateSessionOptions(options);
36863683

36873684
if (typeof authority === 'string')
36883685
authority = new URL(authority);

‎lib/internal/http2/util.js‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,8 @@ const IDX_OPTIONS_MAX_SETTINGS = 9;
231231
const IDX_OPTIONS_STREAM_RESET_RATE = 10;
232232
const IDX_OPTIONS_STREAM_RESET_BURST = 11;
233233
const IDX_OPTIONS_STRICT_HTTP_FIELD_WHITESPACE_VALIDATION = 12;
234-
const IDX_OPTIONS_FLAGS = 13;
234+
const IDX_OPTIONS_CONNECTION_WINDOW_SIZE = 13;
235+
const IDX_OPTIONS_FLAGS = 14;
235236

236237
function updateOptionsBuffer(options) {
237238
let flags = 0;
@@ -301,6 +302,11 @@ function updateOptionsBuffer(options) {
301302
optionsBuffer[IDX_OPTIONS_STRICT_HTTP_FIELD_WHITESPACE_VALIDATION] =
302303
options.strictFieldWhitespaceValidation === true ? 0 : 1;
303304
}
305+
if (typeof options.connectionWindowSize === 'number') {
306+
flags |= (1 << IDX_OPTIONS_CONNECTION_WINDOW_SIZE);
307+
optionsBuffer[IDX_OPTIONS_CONNECTION_WINDOW_SIZE] =
308+
options.connectionWindowSize;
309+
}
304310

305311
optionsBuffer[IDX_OPTIONS_FLAGS] = flags;
306312
}

‎src/node_http2.cc‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,11 @@ Http2Options::Http2Options(Http2State* http2_state, SessionType type) {
217217
static_cast<size_t>(buffer[IDX_OPTIONS_MAX_SETTINGS]));
218218
}
219219

220+
if (flags & (1 << IDX_OPTIONS_CONNECTION_WINDOW_SIZE)) {
221+
set_connection_window_size(
222+
static_cast<int32_t>(buffer[IDX_OPTIONS_CONNECTION_WINDOW_SIZE]));
223+
}
224+
220225
if ((flags & (1 << IDX_OPTIONS_STREAM_RESET_BURST)) &&
221226
(flags & (1 << IDX_OPTIONS_STREAM_RESET_RATE))) {
222227
nghttp2_option_set_stream_reset_rate_limit(
@@ -612,6 +617,12 @@ Http2Session::Http2Session(Http2State* http2_state,
612617
&alloc_info), 0);
613618
session_.reset(session);
614619

620+
// User settings have to be applied here initially rather than updating
621+
// later as windows cannot be shrunk after they've been advertised.
622+
CHECK_EQ(nghttp2_session_set_local_window_size(
623+
session, NGHTTP2_FLAG_NONE, 0, opts.connection_window_size()),
624+
0);
625+
615626
outgoing_storage_.reserve(1024);
616627
outgoing_buffers_.reserve(32);
617628

‎src/node_http2.h‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ constexpr size_t kDefaultMaxSettings = 10;
3939
// Default maximum total memory cap for Http2Session.
4040
constexpr uint64_t kDefaultMaxSessionMemory = 10000000;
4141

42+
constexpr int32_t kDefaultConnectionWindowSize = 65535;
43+
4244
// These are the standard HTTP/2 defaults as specified by the RFC
4345
constexpr uint32_t DEFAULT_SETTINGS_HEADER_TABLE_SIZE = 4096;
4446
constexpr uint32_t DEFAULT_SETTINGS_ENABLE_PUSH = 1;
@@ -236,9 +238,16 @@ class Http2Options {
236238
return max_session_memory_;
237239
}
238240

241+
void set_connection_window_size(int32_t size) {
242+
connection_window_size_ = size;
243+
}
244+
245+
int32_t connection_window_size() const { return connection_window_size_; }
246+
239247
private:
240248
Nghttp2OptionPointer options_;
241249
uint64_t max_session_memory_ = kDefaultMaxSessionMemory;
250+
int32_t connection_window_size_ = kDefaultConnectionWindowSize;
242251
uint32_t max_header_pairs_ = DEFAULT_MAX_HEADER_LIST_PAIRS;
243252
PaddingStrategy padding_strategy_ = PADDING_STRATEGY_NONE;
244253
size_t max_outstanding_pings_ = kDefaultMaxPings;

‎src/node_http2_state.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ namespace http2 {
6161
IDX_OPTIONS_STREAM_RESET_RATE,
6262
IDX_OPTIONS_STREAM_RESET_BURST,
6363
IDX_OPTIONS_STRICT_HTTP_FIELD_WHITESPACE_VALIDATION,
64+
IDX_OPTIONS_CONNECTION_WINDOW_SIZE,
6465
IDX_OPTIONS_FLAGS
6566
};
6667

Lines changed: 104 additions & 0 deletions

0 commit comments

Comments
 (0)