Skip to content

Commit 11f9a4b

Browse files
authored
fix: Correct behaviour of chained body proxy streams in some circumstances (#1259)
1 parent 88e388c commit 11f9a4b

File tree

1 file changed

+35
-11
lines changed

1 file changed

+35
-11
lines changed

runtime/fastly/builtins/fetch/request-response.cpp

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1580,23 +1580,47 @@ bool RequestOrResponse::body_source_pull_algorithm(JSContext *cx, JS::CallArgs a
15801580
// piped to the same destination this is guaranteed to happen in the right
15811581
// order: ReadableStream#pipeTo locks the destination WritableStream until the
15821582
// source ReadableStream is closed/canceled, so only one stream can ever be
1583-
// piped in at the same time.
1583+
// piped in at the same time. There may be a chain of TransformStreams in
1584+
// between the source and destination, so we walk the piping chain to find the
1585+
// final destination.
15841586
JS::RootedObject pipe_dest(cx, NativeStreamSource::piped_to_transform_stream(source));
15851587
if (pipe_dest) {
1588+
// Walk the chain of TransformStreams to find the final destination
1589+
JS::RootedObject current_dest(cx, pipe_dest);
1590+
JS::RootedObject next_dest(cx);
1591+
1592+
while (current_dest) {
1593+
// Try to find the next TransformStream in the chain
1594+
JS::RootedObject readable(cx, TransformStream::readable(current_dest));
1595+
JS::RootedObject next_source(cx, NativeStreamSource::get_stream_source(cx, readable));
1596+
if (next_source) {
1597+
next_dest.set(NativeStreamSource::piped_to_transform_stream(next_source));
1598+
} else {
1599+
next_dest.set(nullptr);
1600+
}
15861601

1587-
if (TransformStream::readable_used_as_body(pipe_dest)) {
1602+
// If there's no next destination, we've found the last one in the chain
1603+
if (!next_dest) {
1604+
// If this is used as a body, we can append directly and close
1605+
if (TransformStream::readable_used_as_body(current_dest)) {
1606+
JS::RootedObject dest_owner(cx, TransformStream::owner(current_dest));
1607+
if (!RequestOrResponse::append_body(cx, dest_owner, body_owner)) {
1608+
return false;
1609+
}
15881610

1589-
JS::RootedObject dest_owner(cx, TransformStream::owner(pipe_dest));
1590-
if (!RequestOrResponse::append_body(cx, dest_owner, body_owner)) {
1591-
return false;
1592-
}
1611+
JS::RootedObject stream(cx, NativeStreamSource::stream(source));
1612+
bool success = JS::ReadableStreamClose(cx, stream);
1613+
MOZ_RELEASE_ASSERT(success);
15931614

1594-
JS::RootedObject stream(cx, NativeStreamSource::stream(source));
1595-
bool success = JS::ReadableStreamClose(cx, stream);
1596-
MOZ_RELEASE_ASSERT(success);
1615+
args.rval().setUndefined();
1616+
return true;
1617+
}
1618+
// If the last one isn't used as a body it must be doing something else with
1619+
// the data, so we fall back to reading and writing chunks as normal
1620+
break;
1621+
}
15971622

1598-
args.rval().setUndefined();
1599-
return true;
1623+
current_dest.set(next_dest);
16001624
}
16011625
}
16021626

0 commit comments

Comments
 (0)