Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 9c6be3c

Browse filesBrowse files
zhangyongshengcodebytere
authored andcommitted
http2: allow setting the local window size of a session
PR-URL: #35978 Fixes: #31084 Refs: #26962 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Ricky Zhou <0x19951125@gmail.com>
1 parent 0b40568 commit 9c6be3c
Copy full SHA for 9c6be3c

File tree

Expand file treeCollapse file tree

9 files changed

+237
-5
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

9 files changed

+237
-5
lines changed
Open diff view settings
Collapse file

‎doc/api/errors.md‎

Copy file name to clipboardExpand all lines: doc/api/errors.md
+5Lines changed: 5 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -1226,6 +1226,11 @@ reached.
12261226
An attempt was made to initiate a new push stream from within a push stream.
12271227
Nested push streams are not permitted.
12281228

1229+
<a id="ERR_HTTP2_NO_MEM"></a>
1230+
### `ERR_HTTP2_NO_MEM`
1231+
1232+
Out of memory when using the `http2session.setLocalWindowSize(windowSize)` API.
1233+
12291234
<a id="ERR_HTTP2_NO_SOCKET_MANIPULATION"></a>
12301235
### `ERR_HTTP2_NO_SOCKET_MANIPULATION`
12311236

Collapse file

‎doc/api/http2.md‎

Copy file name to clipboardExpand all lines: doc/api/http2.md
+23Lines changed: 23 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,29 @@ added: v8.4.0
519519
A prototype-less object describing the current remote settings of this
520520
`Http2Session`. The remote settings are set by the *connected* HTTP/2 peer.
521521

522+
#### `http2session.setLocalWindowSize(windowSize)`
523+
<!-- YAML
524+
added: REPLACEME
525+
-->
526+
527+
* `windowSize` {number}
528+
529+
Sets the local endpoint's window size.
530+
The `windowSize` is the total window size to set, not
531+
the delta.
532+
533+
```js
534+
const http2 = require('http2');
535+
536+
const server = http2.createServer();
537+
const expectedWindowSize = 2 ** 20;
538+
server.on('connect', (session) => {
539+
540+
// Set local window size to be 2 ** 20
541+
session.setLocalWindowSize(expectedWindowSize);
542+
});
543+
```
544+
522545
#### `http2session.setTimeout(msecs, callback)`
523546
<!-- YAML
524547
added: v8.4.0
Collapse file

‎lib/internal/errors.js‎

Copy file name to clipboardExpand all lines: lib/internal/errors.js
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -904,6 +904,7 @@ E('ERR_HTTP2_MAX_PENDING_SETTINGS_ACK',
904904
'Maximum number of pending settings acknowledgements', Error);
905905
E('ERR_HTTP2_NESTED_PUSH',
906906
'A push stream cannot initiate another push stream.', Error);
907+
E('ERR_HTTP2_NO_MEM', 'Out of memory', Error);
907908
E('ERR_HTTP2_NO_SOCKET_MANIPULATION',
908909
'HTTP/2 sockets should not be directly manipulated (e.g. read and written)',
909910
Error);
Collapse file

‎lib/internal/http2/core.js‎

Copy file name to clipboardExpand all lines: lib/internal/http2/core.js
+24-5Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ const {
7070
ERR_HTTP2_INVALID_STREAM,
7171
ERR_HTTP2_MAX_PENDING_SETTINGS_ACK,
7272
ERR_HTTP2_NESTED_PUSH,
73+
ERR_HTTP2_NO_MEM,
7374
ERR_HTTP2_NO_SOCKET_MANIPULATION,
7475
ERR_HTTP2_ORIGIN_LENGTH,
7576
ERR_HTTP2_OUT_OF_STREAMS,
@@ -101,11 +102,13 @@ const {
101102
},
102103
hideStackFrames
103104
} = require('internal/errors');
104-
const { validateInteger,
105-
validateNumber,
106-
validateString,
107-
validateUint32,
108-
isUint32,
105+
const {
106+
isUint32,
107+
validateInt32,
108+
validateInteger,
109+
validateNumber,
110+
validateString,
111+
validateUint32,
109112
} = require('internal/validators');
110113
const fsPromisesInternal = require('internal/fs/promises');
111114
const { utcDate } = require('internal/http');
@@ -252,6 +255,7 @@ const {
252255
NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE,
253256
NGHTTP2_ERR_INVALID_ARGUMENT,
254257
NGHTTP2_ERR_STREAM_CLOSED,
258+
NGHTTP2_ERR_NOMEM,
255259

256260
HTTP2_HEADER_AUTHORITY,
257261
HTTP2_HEADER_DATE,
@@ -1254,6 +1258,21 @@ class Http2Session extends EventEmitter {
12541258
this[kHandle].setNextStreamID(id);
12551259
}
12561260

1261+
// Sets the local window size (local endpoints's window size)
1262+
// Returns 0 if sucess or throw an exception if NGHTTP2_ERR_NOMEM
1263+
// if the window allocation fails
1264+
setLocalWindowSize(windowSize) {
1265+
if (this.destroyed)
1266+
throw new ERR_HTTP2_INVALID_SESSION();
1267+
1268+
validateInt32(windowSize, 'windowSize', 0);
1269+
const ret = this[kHandle].setLocalWindowSize(windowSize);
1270+
1271+
if (ret === NGHTTP2_ERR_NOMEM) {
1272+
this.destroy(new ERR_HTTP2_NO_MEM());
1273+
}
1274+
}
1275+
12571276
// If ping is called while we are still connecting, or after close() has
12581277
// been called, the ping callback will be invoked immediately will a ping
12591278
// cancelled error and a duration of 0.0.
Collapse file

‎src/node_http2.cc‎

Copy file name to clipboardExpand all lines: src/node_http2.cc
+21Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2416,6 +2416,25 @@ void Http2Session::SetNextStreamID(const FunctionCallbackInfo<Value>& args) {
24162416
Debug(session, "set next stream id to %d", id);
24172417
}
24182418

2419+
// Set local window size (local endpoints's window size) to the given
2420+
// window_size for the stream denoted by 0.
2421+
// This function returns 0 if it succeeds, or one of a negative codes
2422+
void Http2Session::SetLocalWindowSize(
2423+
const FunctionCallbackInfo<Value>& args) {
2424+
Environment* env = Environment::GetCurrent(args);
2425+
Http2Session* session;
2426+
ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
2427+
2428+
int32_t window_size = args[0]->Int32Value(env->context()).ToChecked();
2429+
2430+
int result = nghttp2_session_set_local_window_size(
2431+
session->session(), NGHTTP2_FLAG_NONE, 0, window_size);
2432+
2433+
args.GetReturnValue().Set(result);
2434+
2435+
Debug(session, "set local window size to %d", window_size);
2436+
}
2437+
24192438
// A TypedArray instance is shared between C++ and JS land to contain the
24202439
// SETTINGS (either remote or local). RefreshSettings updates the current
24212440
// values established for each of the settings so those can be read in JS land.
@@ -3088,6 +3107,8 @@ void Initialize(Local<Object> target,
30883107
env->SetProtoMethod(session, "request", Http2Session::Request);
30893108
env->SetProtoMethod(session, "setNextStreamID",
30903109
Http2Session::SetNextStreamID);
3110+
env->SetProtoMethod(session, "setLocalWindowSize",
3111+
Http2Session::SetLocalWindowSize);
30913112
env->SetProtoMethod(session, "updateChunksSent",
30923113
Http2Session::UpdateChunksSent);
30933114
env->SetProtoMethod(session, "refreshState", Http2Session::RefreshState);
Collapse file

‎src/node_http2.h‎

Copy file name to clipboardExpand all lines: src/node_http2.h
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,8 @@ class Http2Session : public AsyncWrap,
699699
static void Settings(const v8::FunctionCallbackInfo<v8::Value>& args);
700700
static void Request(const v8::FunctionCallbackInfo<v8::Value>& args);
701701
static void SetNextStreamID(const v8::FunctionCallbackInfo<v8::Value>& args);
702+
static void SetLocalWindowSize(
703+
const v8::FunctionCallbackInfo<v8::Value>& args);
702704
static void Goaway(const v8::FunctionCallbackInfo<v8::Value>& args);
703705
static void UpdateChunksSent(const v8::FunctionCallbackInfo<v8::Value>& args);
704706
static void RefreshState(const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -1115,6 +1117,7 @@ class Origins {
11151117
V(NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE) \
11161118
V(NGHTTP2_ERR_INVALID_ARGUMENT) \
11171119
V(NGHTTP2_ERR_STREAM_CLOSED) \
1120+
V(NGHTTP2_ERR_NOMEM) \
11181121
V(STREAM_OPTION_EMPTY_PAYLOAD) \
11191122
V(STREAM_OPTION_GET_TRAILERS)
11201123

Collapse file

‎test/parallel/test-http2-client-destroy.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-http2-client-destroy.js
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ const Countdown = require('../common/countdown');
7777
};
7878

7979
assert.throws(() => client.setNextStreamID(), sessionError);
80+
assert.throws(() => client.setLocalWindowSize(), sessionError);
8081
assert.throws(() => client.ping(), sessionError);
8182
assert.throws(() => client.settings({}), sessionError);
8283
assert.throws(() => client.goaway(), sessionError);
@@ -87,6 +88,7 @@ const Countdown = require('../common/countdown');
8788
// so that state.destroyed is set to true
8889
setImmediate(() => {
8990
assert.throws(() => client.setNextStreamID(), sessionError);
91+
assert.throws(() => client.setLocalWindowSize(), sessionError);
9092
assert.throws(() => client.ping(), sessionError);
9193
assert.throws(() => client.settings({}), sessionError);
9294
assert.throws(() => client.goaway(), sessionError);
Collapse file
+121Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
if (!common.hasCrypto)
5+
common.skip('missing crypto');
6+
7+
const assert = require('assert');
8+
const http2 = require('http2');
9+
10+
{
11+
const server = http2.createServer();
12+
server.on('stream', common.mustNotCall((stream) => {
13+
stream.respond();
14+
stream.end('ok');
15+
}));
16+
17+
const types = {
18+
boolean: true,
19+
function: () => {},
20+
number: 1,
21+
object: {},
22+
array: [],
23+
null: null,
24+
};
25+
26+
server.listen(0, common.mustCall(() => {
27+
const client = http2.connect(`http://localhost:${server.address().port}`);
28+
29+
client.on('connect', common.mustCall(() => {
30+
const outOfRangeNum = 2 ** 32;
31+
assert.throws(
32+
() => client.setLocalWindowSize(outOfRangeNum),
33+
{
34+
name: 'RangeError',
35+
code: 'ERR_OUT_OF_RANGE',
36+
message: 'The value of "windowSize" is out of range.' +
37+
' It must be >= 0 && <= 2147483647. Received ' + outOfRangeNum
38+
}
39+
);
40+
41+
// Throw if something other than number is passed to setLocalWindowSize
42+
Object.entries(types).forEach(([type, value]) => {
43+
if (type === 'number') {
44+
return;
45+
}
46+
47+
assert.throws(
48+
() => client.setLocalWindowSize(value),
49+
{
50+
name: 'TypeError',
51+
code: 'ERR_INVALID_ARG_TYPE',
52+
message: 'The "windowSize" argument must be of type number.' +
53+
common.invalidArgTypeHelper(value)
54+
}
55+
);
56+
});
57+
58+
server.close();
59+
client.close();
60+
}));
61+
}));
62+
}
63+
64+
{
65+
const server = http2.createServer();
66+
server.on('stream', common.mustNotCall((stream) => {
67+
stream.respond();
68+
stream.end('ok');
69+
}));
70+
71+
server.listen(0, common.mustCall(() => {
72+
const client = http2.connect(`http://localhost:${server.address().port}`);
73+
74+
client.on('connect', common.mustCall(() => {
75+
const windowSize = 2 ** 20;
76+
const defaultSetting = http2.getDefaultSettings();
77+
client.setLocalWindowSize(windowSize);
78+
79+
assert.strictEqual(client.state.effectiveLocalWindowSize, windowSize);
80+
assert.strictEqual(client.state.localWindowSize, windowSize);
81+
assert.strictEqual(
82+
client.state.remoteWindowSize,
83+
defaultSetting.initialWindowSize
84+
);
85+
86+
server.close();
87+
client.close();
88+
}));
89+
}));
90+
}
91+
92+
{
93+
const server = http2.createServer();
94+
server.on('stream', common.mustNotCall((stream) => {
95+
stream.respond();
96+
stream.end('ok');
97+
}));
98+
99+
server.listen(0, common.mustCall(() => {
100+
const client = http2.connect(`http://localhost:${server.address().port}`);
101+
102+
client.on('connect', common.mustCall(() => {
103+
const windowSize = 20;
104+
const defaultSetting = http2.getDefaultSettings();
105+
client.setLocalWindowSize(windowSize);
106+
107+
assert.strictEqual(client.state.effectiveLocalWindowSize, windowSize);
108+
assert.strictEqual(
109+
client.state.localWindowSize,
110+
defaultSetting.initialWindowSize
111+
);
112+
assert.strictEqual(
113+
client.state.remoteWindowSize,
114+
defaultSetting.initialWindowSize
115+
);
116+
117+
server.close();
118+
client.close();
119+
}));
120+
}));
121+
}
Collapse file
+37Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
if (!common.hasCrypto)
5+
common.skip('missing crypto');
6+
7+
const assert = require('assert');
8+
const http2 = require('http2');
9+
10+
const server = http2.createServer();
11+
server.on('stream', common.mustCall((stream) => {
12+
stream.respond();
13+
stream.end('ok');
14+
}));
15+
server.on('session', common.mustCall((session) => {
16+
const windowSize = 2 ** 20;
17+
const defaultSetting = http2.getDefaultSettings();
18+
session.setLocalWindowSize(windowSize);
19+
20+
assert.strictEqual(session.state.effectiveLocalWindowSize, windowSize);
21+
assert.strictEqual(session.state.localWindowSize, windowSize);
22+
assert.strictEqual(
23+
session.state.remoteWindowSize,
24+
defaultSetting.initialWindowSize
25+
);
26+
}));
27+
28+
server.listen(0, common.mustCall(() => {
29+
const client = http2.connect(`http://localhost:${server.address().port}`);
30+
31+
const req = client.request();
32+
req.resume();
33+
req.on('close', common.mustCall(() => {
34+
client.close();
35+
server.close();
36+
}));
37+
}));

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.