Skip to content

Commit a88616c

Browse files
authored
fix: only apply our pipeTo/pipeThrough optimisations to TransformStreams who have no transformers (IdentityStreams). (#556)
1 parent 3ccb4c0 commit a88616c

File tree

6 files changed

+105
-1
lines changed

6 files changed

+105
-1
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ jobs:
255255
- streaming-close
256256
- tee
257257
- timers
258+
- transform-stream
258259
- urlsearchparams
259260
steps:
260261
- name: Checkout fastly/js-compute-runtime
@@ -404,6 +405,7 @@ jobs:
404405
- 'secret-store'
405406
- 'status'
406407
- 'timers'
408+
- 'transform-stream'
407409
- 'urlsearchparams'
408410
steps:
409411
- uses: actions/checkout@v3
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/// <reference path="../../../../../types/index.d.ts" />
2+
/* eslint-env serviceworker */
3+
/* global TransformStream */
4+
5+
import { routes } from "../../../test-harness.js";
6+
7+
function upperCase() {
8+
const decoder = new TextDecoder()
9+
const encoder = new TextEncoder()
10+
return new TransformStream({
11+
transform(chunk, controller) {
12+
controller.enqueue(encoder.encode(decoder.decode(chunk).toUpperCase()));
13+
},
14+
});
15+
}
16+
17+
routes.set("/identity", () => {
18+
return fetch('https://http-me.glitch.me/test?body=hello', { backend: 'http-me' }).then(response => {
19+
return new Response(response.body.pipeThrough(new TransformStream));
20+
})
21+
});
22+
routes.set("/uppercase", () => {
23+
return fetch('https://http-me.glitch.me/test?body=hello', { backend: 'http-me' }).then(response => {
24+
return new Response(response.body.pipeThrough(upperCase()));
25+
})
26+
});
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# This file describes a Fastly Compute@Edge package. To learn more visit:
2+
# https://developer.fastly.com/reference/fastly-toml/
3+
4+
authors = ["[email protected]"]
5+
description = ""
6+
language = "other"
7+
manifest_version = 2
8+
name = "transform-stream"
9+
service_id = ""
10+
11+
[scripts]
12+
build = "node ../../../../js-compute-runtime-cli.js bin/index.js"
13+
14+
[local_server]
15+
16+
[local_server.backends]
17+
18+
[local_server.backends.http-me]
19+
url = "https://http-me.glitch.me"
20+
21+
[setup]
22+
23+
[setup.backends]
24+
25+
[setup.backends.http-me]
26+
address = "http-me.glitch.me"
27+
port = 443
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"GET /identity": {
3+
"environments": [
4+
"c@e", "viceroy"
5+
],
6+
"downstream_request": {
7+
"method": "GET",
8+
"pathname": "/identity"
9+
},
10+
"downstream_response": {
11+
"status": 200,
12+
"body": "hello"
13+
}
14+
},
15+
"GET /uppercase": {
16+
"environments": [
17+
"c@e", "viceroy"
18+
],
19+
"downstream_request": {
20+
"method": "GET",
21+
"pathname": "/uppercase"
22+
},
23+
"downstream_response": {
24+
"status": 200,
25+
"body": "HELLO"
26+
}
27+
}
28+
}

runtime/js-compute-runtime/builtins/transform-stream.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,14 @@ bool pipeTo(JSContext *cx, unsigned argc, JS::Value *vp) {
4242
JS::RootedObject target(cx, args[0].isObject() ? &args[0].toObject() : nullptr);
4343
if (target && builtins::NativeStreamSource::stream_has_native_source(cx, self) &&
4444
JS::IsWritableStream(target) && builtins::TransformStream::is_ts_writable(cx, target)) {
45-
builtins::NativeStreamSource::set_stream_piped_to_ts_writable(cx, self, target);
45+
if (auto ts = builtins::TransformStream::ts_from_writable(cx, target)) {
46+
auto streamHasTransformer =
47+
JS::GetReservedSlot(ts, builtins::TransformStream::Slots::HasTransformer).toBoolean();
48+
// We only want to apply the optimisation on identity-streams
49+
if (!streamHasTransformer) {
50+
builtins::NativeStreamSource::set_stream_piped_to_ts_writable(cx, self, target);
51+
}
52+
}
4653
}
4754

4855
return JS::Call(cx, args.thisv(), original_pipeTo, JS::HandleValueArray(args), args.rval());
@@ -318,6 +325,15 @@ JSObject *TransformStream::writable(JSObject *self) {
318325
return &JS::GetReservedSlot(self, TransformStream::Slots::Writable).toObject();
319326
}
320327

328+
JSObject *TransformStream::ts_from_writable(JSContext *cx, JS::HandleObject writable) {
329+
MOZ_ASSERT(is_ts_writable(cx, writable));
330+
JSObject *sink = builtins::NativeStreamSink::get_stream_sink(cx, writable);
331+
if (!sink || !builtins::NativeStreamSink::is_instance(sink)) {
332+
return nullptr;
333+
}
334+
return builtins::NativeStreamSink::owner(sink);
335+
}
336+
321337
bool TransformStream::is_ts_writable(JSContext *cx, JS::HandleObject writable) {
322338
JSObject *sink = builtins::NativeStreamSink::get_stream_sink(cx, writable);
323339
if (!sink || !builtins::NativeStreamSink::is_instance(sink)) {
@@ -466,6 +482,9 @@ bool TransformStream::constructor(JSContext *cx, unsigned argc, JS::Value *vp) {
466482
if (!self)
467483
return false;
468484

485+
JS::SetReservedSlot(self, TransformStream::Slots::HasTransformer,
486+
JS::BooleanValue(JS::ToBoolean(transformer)));
487+
469488
args.rval().setObject(*self);
470489
return true;
471490
}

runtime/js-compute-runtime/builtins/transform-stream.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class TransformStream : public BuiltinImpl<TransformStream> {
2222
// used as a body.
2323
UsedAsMixin, // `true` if the TransformStream is used in another transforming
2424
// builtin, such as CompressionStream.
25+
HasTransformer,
2526
Count
2627
};
2728
static const JSFunctionSpec static_methods[];
@@ -39,6 +40,7 @@ class TransformStream : public BuiltinImpl<TransformStream> {
3940
JS::HandleObject target);
4041
static JSObject *writable(JSObject *self);
4142
static bool is_ts_writable(JSContext *cx, JS::HandleObject writable);
43+
static JSObject *ts_from_writable(JSContext *cx, JS::HandleObject writable);
4244
static JSObject *controller(JSObject *self);
4345
static bool backpressure(JSObject *self);
4446
static JSObject *backpressureChangePromise(JSObject *self);

0 commit comments

Comments
 (0)