@@ -122,16 +122,6 @@ struct HttpResponseWrapper {
122122 Isolate *isolate = args.GetIsolate ();
123123 auto *res = getHttpResponse<SSL >(args);
124124 if (res) {
125-
126- /* This is how we capture res (C++ this in invocation of this function) */
127- UniquePersistent<Object> resObject (isolate, args.This ());
128-
129- res->onAborted ([resObject = std::move (resObject), isolate]() {
130- HandleScope hs (isolate);
131- /* Mark this resObject invalid */
132- Local<Object>::New (isolate, resObject)->SetAlignedPointerInInternalField (0 , nullptr );
133- });
134-
135125 size_t maxSize = (size_t ) args[0 ]->NumberValue (isolate->GetCurrentContext ()).ToChecked ();
136126
137127 /* This thing perfectly fits in with unique_function, and will Reset on destructor */
@@ -142,49 +132,43 @@ struct HttpResponseWrapper {
142132 std::unique_ptr<std::vector<char >> buffer;
143133 bool overflow = false ;
144134
145- res->onDataV2 ([res, p = std::move (p), buffer = std::move (buffer), overflow, maxSize, isolate](std::string_view data, uint64_t maxRemainingBodyLength) mutable {
135+ res->onDataV2 ([p = std::move (p), buffer = std::move (buffer), overflow, maxSize, isolate](std::string_view data, uint64_t maxRemainingBodyLength) mutable {
146136 HandleScope hs (isolate);
147137
148- if (! overflow) {
149- if (!buffer) {
150- /* Fast path: this is the very first (and possibly only) chunk */
151- if (maxRemainingBodyLength == 0 ) {
152- if (data.size () <= maxSize) {
153- /* Single-chunk zero-copy: wrap data directly, detach after call like onData */
154- Local<ArrayBuffer> ab = ArrayBuffer_New (isolate, ( void *) data. data (), data. size ()) ;
155- Local<Value> argv[] = {ab };
156- CallJS (isolate, Local<Function>::New (isolate, p), 1 , argv);
157- ab-> Detach ();
158- } else {
159- Local<Value> argv[] = { Null (isolate)} ;
160- CallJS (isolate, Local<Function>:: New (isolate, p), 1 , argv) ;
161- }
162- return ;
163- }
138+ if (overflow) {
139+ return ;
140+ } else if (!buffer) {
141+ /* First and possibly only chunk */
142+ if (data.size () > maxSize) {
143+ /* Overflow: return to JS with null */
144+ overflow = true ;
145+ Local<Value> argv[] = {Null (isolate) };
146+ CallJS (isolate, Local<Function>::New (isolate, p), 1 , argv);
147+ } else if (maxRemainingBodyLength == 0 ) {
148+ /* Fast path: Single-chunk zero-copy: wrap data directly, detach after call like onData */
149+ Local<ArrayBuffer> ab = ArrayBuffer_New (isolate, ( void *) data. data (), data. size ()) ;
150+ Local<Value> argv[] = {ab} ;
151+ CallJS (isolate, Local<Function>:: New (isolate, p), 1 , argv);
152+ ab-> Detach () ;
153+ } else {
164154 /* Slow path begins: allocate buffer lazily for first non-terminal chunk */
165- if (data. size () <= maxSize) {
166- buffer = std::make_unique<std::vector< char >>();
155+ buffer = std::make_unique<std::vector< char >>();
156+ if (maxRemainingBodyLength <= maxSize - data. size ()) {
167157 /* Preallocate with hint */
168- if (maxRemainingBodyLength <= maxSize) {
169- buffer->reserve (maxRemainingBodyLength); // this includes the total size on first call (look over this)
170- }
171- buffer->assign (data.begin (), data.end ());
172- } else {
173- overflow = true ;
174- }
175- } else {
176- /* Subsequent chunks: accumulate or mark overflow; guard both sides of subtraction */
177- if (buffer->size () <= maxSize && data.size () <= maxSize - buffer->size ()) {
178- buffer->insert (buffer->end (), data.begin (), data.end ());
179- } else {
180- buffer.reset ();
181- overflow = true ;
158+ buffer->reserve (maxRemainingBodyLength + data.size ());
182159 }
160+ buffer->assign (data.begin (), data.end ());
183161 }
184- }
185-
186- if (maxRemainingBodyLength == 0 ) {
187- if (!overflow) {
162+ } else if (data.size () > maxSize - buffer->size ()) {
163+ /* Subsequent chunks Overflow: return to JS with null */
164+ buffer.reset ();
165+ overflow = true ;
166+ Local<Value> argv[] = {Null (isolate)};
167+ CallJS (isolate, Local<Function>::New (isolate, p), 1 , argv);
168+ } else {
169+ /* Subsequent chunks: accumulate */
170+ buffer->insert (buffer->end (), data.begin (), data.end ());
171+ if (maxRemainingBodyLength == 0 ) {
188172 /* Zero-copy: hand V8 the vector's own memory via a custom deleter */
189173 auto *rawBuffer = buffer.release ();
190174 auto backingStore = ArrayBuffer::NewBackingStore (
@@ -197,9 +181,6 @@ struct HttpResponseWrapper {
197181 Local<ArrayBuffer> ab = ArrayBuffer::New (isolate, std::move (backingStore));
198182 Local<Value> argv[] = {ab};
199183 CallJS (isolate, Local<Function>::New (isolate, p), 1 , argv);
200- } else {
201- Local<Value> argv[] = {Null (isolate)};
202- CallJS (isolate, Local<Function>::New (isolate, p), 1 , argv);
203184 }
204185 }
205186 });
@@ -208,28 +189,24 @@ struct HttpResponseWrapper {
208189 }
209190 }
210191
211- /* Takes a function of (chunk, maxRemainingBodyLength). Combines onAborted and onData into a single callback.
212- * If chunk is null, the connection was aborted. If maxRemainingBodyLength is 0, the last chunk has arrived.
213- * The JS object is invalidated before the abort callback is called. Returns this. */
192+ /* Takes function of chunk and maxRemainingBodyLength. Returns this.
193+ * If maxRemainingBodyLength is 0, the last chunk has arrived. */
214194 template <int SSL >
215195 static void res_onDataV2 (const FunctionCallbackInfo<Value> &args) {
216196 Isolate *isolate = args.GetIsolate ();
217197 auto *res = getHttpResponse<SSL >(args);
218198 if (res) {
219- /* Share the persistent function between both onAborted and onData lambdas */
220- auto sharedP = std::make_shared<UniquePersistent<Function>>(isolate, Local<Function>::Cast (args[0 ]));
221-
222- /* This is how we capture res (C++ this in invocation of this function) */
223- UniquePersistent<Object> resObject (isolate, args.This ());
199+ /* This thing perfectly fits in with unique_function, and will Reset on destructor */
200+ UniquePersistent<Function> p (isolate, Local<Function>::Cast (args[0 ]));
224201
225- res->onDataV2 ([res, sharedP , isolate](std::string_view data, uint64_t maxRemainingBodyLength) {
202+ res->onDataV2 ([p = std::move (p) , isolate](std::string_view data, uint64_t maxRemainingBodyLength) {
226203 HandleScope hs (isolate);
227204
228205 Local<ArrayBuffer> dataArrayBuffer = ArrayBuffer_New (isolate, (void *) data.data (), data.length ());
229206
230207 /* Pass maxRemainingBodyLength so user can preallocate; 0 signals the last chunk */
231208 Local<Value> argv[] = {dataArrayBuffer, BigInt::NewFromUnsigned (isolate, maxRemainingBodyLength)};
232- CallJS (isolate, Local<Function>::New (isolate, *sharedP ), 2 , argv);
209+ CallJS (isolate, Local<Function>::New (isolate, p ), 2 , argv);
233210
234211 dataArrayBuffer->Detach ();
235212 });
0 commit comments