Skip to content

Commit 3dc3627

Browse files
committed
stream: improve respondWithNewView()
This fixes validating an ArrayBufferView given to ReadableStreamBYOBRequest.respondWithNewView() to improve the web streams compatibility. Signed-off-by: Daeyeon Jeong [email protected]
1 parent dabda03 commit 3dc3627

File tree

4 files changed

+67
-3
lines changed

4 files changed

+67
-3
lines changed

lib/internal/webstreams/readablestream.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ const {
2828
Uint8Array,
2929
} = primordials;
3030

31+
const { arrayBufferIsDetached } = internalBinding('util');
32+
3133
const {
3234
codes: {
3335
ERR_ILLEGAL_CONSTRUCTOR,
@@ -681,6 +683,16 @@ class ReadableStreamBYOBRequest {
681683
'This BYOB request has been invalidated');
682684
}
683685

686+
const viewedBuffer = ArrayBufferViewGetBuffer(view);
687+
const viewedBufferByteLength = ArrayBufferGetByteLength(viewedBuffer);
688+
const isDetached = arrayBufferIsDetached(viewedBuffer);
689+
690+
if (viewedBufferByteLength === 0 && isDetached) {
691+
throw new ERR_INVALID_STATE.TypeError(
692+
'Viewed ArrayBuffer is detached',
693+
);
694+
}
695+
684696
readableByteStreamControllerRespondWithNewView(controller, view);
685697
}
686698

src/node_util.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,12 @@ void ArrayBufferViewHasBuffer(const FunctionCallbackInfo<Value>& args) {
207207
args.GetReturnValue().Set(args[0].As<ArrayBufferView>()->HasBuffer());
208208
}
209209

210+
void ArrayBufferIsDetached(const FunctionCallbackInfo<Value>& args) {
211+
CHECK(args[0]->IsArrayBuffer());
212+
v8::Local<v8::ArrayBuffer> ab = args[0].As<v8::ArrayBuffer>();
213+
args.GetReturnValue().Set(ab->GetBackingStore()->Data() == nullptr);
214+
}
215+
210216
class WeakReference : public BaseObject {
211217
public:
212218
WeakReference(Environment* env, Local<Object> object, Local<Object> target)
@@ -334,6 +340,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
334340
registry->Register(GetExternalValue);
335341
registry->Register(Sleep);
336342
registry->Register(ArrayBufferViewHasBuffer);
343+
registry->Register(ArrayBufferIsDetached);
337344
registry->Register(WeakReference::New);
338345
registry->Register(WeakReference::Get);
339346
registry->Register(WeakReference::IncRef);
@@ -380,6 +387,7 @@ void Initialize(Local<Object> target,
380387
env->SetMethod(target, "sleep", Sleep);
381388

382389
env->SetMethod(target, "arrayBufferViewHasBuffer", ArrayBufferViewHasBuffer);
390+
env->SetMethod(target, "arrayBufferIsDetached", ArrayBufferIsDetached);
383391
Local<Object> constants = Object::New(env->isolate());
384392
NODE_DEFINE_CONSTANT(constants, ALL_PROPERTIES);
385393
NODE_DEFINE_CONSTANT(constants, ONLY_WRITABLE);
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const assert = require('node:assert');
5+
6+
{
7+
// ReadableStream with byte source: respondWithNewView() throws if the
8+
// supplied view's buffer has a different length (in the closed state)
9+
const stream = new ReadableStream({
10+
pull: common.mustCall(async (c) => {
11+
const view = new Uint8Array(new ArrayBuffer(10), 0, 0);
12+
13+
c.close();
14+
15+
assert.throws(() => {
16+
c.byobRequest.respondWithNewView(view);
17+
}, RangeError);
18+
}),
19+
type: 'bytes',
20+
});
21+
22+
const reader = stream.getReader({ mode: 'byob' });
23+
reader.read(new Uint8Array([4, 5, 6]));
24+
}
25+
26+
{
27+
// ReadableStream with byte source: respondWithNewView() throws if the
28+
// supplied view's buffer has been detached (in the closed state)
29+
const stream = new ReadableStream({
30+
pull: common.mustCall((c) => {
31+
c.close();
32+
33+
// Detach it by reading into it
34+
const view = new Uint8Array([1, 2, 3]);
35+
reader.read(view);
36+
37+
assert.throws(() => {
38+
c.byobRequest.respondWithNewView(view);
39+
}, TypeError);
40+
}),
41+
type: 'bytes',
42+
});
43+
44+
const reader = stream.getReader({ mode: 'byob' });
45+
reader.read(new Uint8Array([4, 5, 6]));
46+
}

test/parallel/test-whatwg-readablebytestream.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,7 @@ class Source {
9898
this.controller.close();
9999
}
100100

101-
assert.throws(() => byobRequest.respondWithNewView({}), {
102-
code: 'ERR_INVALID_ARG_TYPE',
103-
});
101+
assert.throws(() => byobRequest.respondWithNewView({}), TypeError);
104102

105103
byobRequest.respond(bytesRead);
106104

0 commit comments

Comments
 (0)