Skip to content

Commit 0bd7c40

Browse files
addaleaxtargos
authored andcommitted
http2: fix setting options before handle exists
Currently, when a JS Http2Session object is created, we have to handle the situation in which the native object corresponding to it does not yet exist. As part of that, we create a typed array for storing options that are passed through the `AliasedStruct` mechanism, and up until now, we copied that typed array over the native one once the native one was available. This was not good, because it was overwriting the defaults that were set during construction of the native typed array with zeroes. In order to fix this, create a wrapper for the JS-created typed array that keeps track of which fields were changed, which enables us to only overwrite fields that were intentionally changed on the JS side. It is surprising that this behavior was not tested (which is, guessing from the commit history around these features, my fault). The subseqeuent commit introduces a test that would fail without this change. PR-URL: #37875 Backport-PR-URL: #38673 Fixes: #37849 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Colin Ihrig <[email protected]>
1 parent 8f1aab7 commit 0bd7c40

File tree

1 file changed

+45
-7
lines changed

1 file changed

+45
-7
lines changed

lib/internal/http2/core.js

+45-7
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ const {
1313
ObjectPrototypeHasOwnProperty,
1414
Promise,
1515
ReflectApply,
16+
ReflectGet,
1617
ReflectGetPrototypeOf,
18+
ReflectSet,
1719
Set,
1820
Symbol,
1921
Uint32Array,
@@ -933,6 +935,36 @@ const validateSettings = hideStackFrames((settings) => {
933935
}
934936
});
935937

938+
// Wrap a typed array in a proxy, and allow selectively copying the entries
939+
// that have explicitly been set to another typed array.
940+
function trackAssignmentsTypedArray(typedArray) {
941+
const typedArrayLength = typedArray.length;
942+
const modifiedEntries = new Uint8Array(typedArrayLength);
943+
944+
function copyAssigned(target) {
945+
for (let i = 0; i < typedArrayLength; i++) {
946+
if (modifiedEntries[i]) {
947+
target[i] = typedArray[i];
948+
}
949+
}
950+
}
951+
952+
return new Proxy(typedArray, {
953+
get(obj, prop, receiver) {
954+
if (prop === 'copyAssigned') {
955+
return copyAssigned;
956+
}
957+
return ReflectGet(obj, prop, receiver);
958+
},
959+
set(obj, prop, value) {
960+
if (`${+prop}` === prop) {
961+
modifiedEntries[prop] = 1;
962+
}
963+
return ReflectSet(obj, prop, value);
964+
}
965+
});
966+
}
967+
936968
// Creates the internal binding.Http2Session handle for an Http2Session
937969
// instance. This occurs only after the socket connection has been
938970
// established. Note: the binding.Http2Session will take over ownership
@@ -963,10 +995,13 @@ function setupHandle(socket, type, options) {
963995
handle.consume(socket._handle);
964996

965997
this[kHandle] = handle;
966-
if (this[kNativeFields])
967-
handle.fields.set(this[kNativeFields]);
968-
else
969-
this[kNativeFields] = handle.fields;
998+
if (this[kNativeFields]) {
999+
// If some options have already been set before the handle existed, copy
1000+
// those (and only those) that have manually been set over.
1001+
this[kNativeFields].copyAssigned(handle.fields);
1002+
}
1003+
1004+
this[kNativeFields] = handle.fields;
9701005

9711006
if (socket.encrypted) {
9721007
this[kAlpnProtocol] = socket.alpnProtocol;
@@ -1018,7 +1053,8 @@ function cleanupSession(session) {
10181053
session[kProxySocket] = undefined;
10191054
session[kSocket] = undefined;
10201055
session[kHandle] = undefined;
1021-
session[kNativeFields] = new Uint8Array(kSessionUint8FieldCount);
1056+
session[kNativeFields] = trackAssignmentsTypedArray(
1057+
new Uint8Array(kSessionUint8FieldCount));
10221058
if (handle)
10231059
handle.ondone = null;
10241060
if (socket) {
@@ -1184,8 +1220,10 @@ class Http2Session extends EventEmitter {
11841220
setupFn();
11851221
}
11861222

1187-
if (!this[kNativeFields])
1188-
this[kNativeFields] = new Uint8Array(kSessionUint8FieldCount);
1223+
if (!this[kNativeFields]) {
1224+
this[kNativeFields] = trackAssignmentsTypedArray(
1225+
new Uint8Array(kSessionUint8FieldCount));
1226+
}
11891227
this.on('newListener', sessionListenerAdded);
11901228
this.on('removeListener', sessionListenerRemoved);
11911229

0 commit comments

Comments
 (0)