66
77#include < workerd/api/streams/readable-source.h>
88#include < workerd/api/streams/readable.h>
9+ #include < workerd/io/features.h>
910#include < workerd/io/observer.h>
1011#include < workerd/util/mimetype.h>
1112#include < workerd/util/stream-utils.h>
@@ -20,14 +21,21 @@ kj::Maybe<jsg::JsBufferSource> concat(jsg::Lock& js, jsg::Optional<Blob::Bits> m
2021 return kj::none;
2122 }
2223
24+ auto rejectResizable = FeatureFlags::get (js).getNoResizableArrayBufferInBlob ();
2325 auto maxBlobSize = Worker::Isolate::from (js).getLimitEnforcer ().getBlobSizeLimit ();
2426 static constexpr int kMaxInt KJ_UNUSED = kj::maxValue;
2527 KJ_DASSERT (maxBlobSize <= kMaxInt , " Blob size limit exceeds int range" );
2628 size_t size = 0 ;
29+ kj::SmallArray<size_t , 8 > cachedPartSizes (bits.size ());
30+ int index = 0 ;
2731 for (auto & part: bits) {
2832 size_t partSize = 0 ;
2933 KJ_SWITCH_ONEOF (part) {
30- KJ_CASE_ONEOF (bytes, kj::Array<const byte>) {
34+ KJ_CASE_ONEOF (bytes, jsg::JsBufferSource) {
35+ if (rejectResizable) {
36+ JSG_REQUIRE (
37+ !bytes.isResizable (), TypeError, " Cannot create a Blob with a resizable ArrayBuffer" );
38+ }
3139 partSize = bytes.size ();
3240 }
3341 KJ_CASE_ONEOF (text, kj::String) {
@@ -37,6 +45,7 @@ kj::Maybe<jsg::JsBufferSource> concat(jsg::Lock& js, jsg::Optional<Blob::Bits> m
3745 partSize = blob->getData ().size ();
3846 }
3947 }
48+ cachedPartSizes[index++] = partSize;
4049
4150 // We can skip the remaining checks if the part is empty.
4251 if (partSize == 0 ) continue ;
@@ -66,25 +75,34 @@ kj::Maybe<jsg::JsBufferSource> concat(jsg::Lock& js, jsg::Optional<Blob::Bits> m
6675
6776 auto view = u8 .asArrayPtr ();
6877
78+ index = 0 ;
6979 for (auto & part: bits) {
7080 KJ_SWITCH_ONEOF (part) {
71- KJ_CASE_ONEOF (bytes, kj::Array<const byte>) {
72- if (bytes.size () == 0 ) continue ;
73- KJ_ASSERT (view.size () >= bytes.size ());
74- view.first (bytes.size ()).copyFrom (bytes);
75- view = view.slice (bytes.size ());
81+ KJ_CASE_ONEOF (bytes, jsg::JsBufferSource) {
82+ size_t cachedSize = cachedPartSizes[index++];
83+ // If the ArrayBuffer was resized larger, we'll ignore the additional bytes.
84+ // If the ArrayBuffer was resized smaller, we'll copy up the current size
85+ // and the remaining bytes for this chunk will be left as zeros.
86+ size_t toCopy = kj::min (bytes.size (), cachedSize);
87+ if (toCopy > 0 ) {
88+ KJ_ASSERT (view.size () >= toCopy);
89+ view.first (toCopy).copyFrom (bytes.asArrayPtr ().first (toCopy));
90+ }
91+ view = view.slice (cachedSize);
7692 }
7793 KJ_CASE_ONEOF (text, kj::String) {
7894 auto byteLength = text.asBytes ().size ();
7995 if (byteLength == 0 ) continue ;
8096 KJ_ASSERT (view.size () >= byteLength);
97+ KJ_ASSERT (byteLength == cachedPartSizes[index++]);
8198 view.first (byteLength).copyFrom (text.asBytes ());
8299 view = view.slice (byteLength);
83100 }
84101 KJ_CASE_ONEOF (blob, jsg::Ref<Blob>) {
85102 auto data = blob->getData ();
86103 if (data.size () == 0 ) continue ;
87104 KJ_ASSERT (view.size () >= data.size ());
105+ KJ_ASSERT (data.size () == cachedPartSizes[index++]);
88106 view.first (data.size ()).copyFrom (data);
89107 view = view.slice (data.size ());
90108 }
@@ -129,7 +147,12 @@ Blob::Blob(kj::Array<byte> data, kj::String type)
129147Blob::Blob (jsg::Lock& js, jsg::JsBufferSource data, kj::String type)
130148 : ownData(data.addRef(js)),
131149 data(data.asArrayPtr()),
132- type(kj::mv(type)) {}
150+ type(kj::mv(type)) {
151+ if (FeatureFlags::get (js).getNoResizableArrayBufferInBlob ()) {
152+ JSG_REQUIRE (
153+ !data.isResizable (), TypeError, " Cannot create a Blob with a resizable ArrayBuffer" );
154+ }
155+ }
133156
134157Blob::Blob (jsg::Ref<Blob> parent, kj::ArrayPtr<const byte> data, kj::String type)
135158 : ownData(kj::mv(parent)),
0 commit comments