Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fetch support - merge pending upstream fetch and async reworks and add temporary workarounds #3

Merged
merged 52 commits into from
Jun 14, 2024
Merged
Changes from 3 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
34cf1ef
Use SpiderMonkey's encoding_c instead of our own crate
tschneidereit May 29, 2024
0d02283
Change WPT support to use a CMake option instead of an env var
tschneidereit Apr 18, 2024
bfe3158
WIP: rework headers
tschneidereit Mar 13, 2024
86db17e
WIP: rework headers
tschneidereit Mar 22, 2024
d4e43db
Immediately lock reified body stream when the underlying body has alr…
tschneidereit Apr 6, 2024
dae6953
More headers rework!
tschneidereit Apr 10, 2024
aa5a32c
Use `string_view` instead of `HostString in core::decode`
tschneidereit Apr 29, 2024
d1984e9
Adjust namespace of wpt support functionality
tschneidereit Apr 29, 2024
ca335a7
And yet more headers rework—along with an overhaul of lots of the hos…
tschneidereit Apr 29, 2024
3bb6b5d
Support creating a component without evaluating a top-level script du…
tschneidereit May 28, 2024
c8cf151
Slight improvements to the WPT harness
tschneidereit May 28, 2024
c44b816
Substantially more reworking of the fetch related host API surface
tschneidereit May 28, 2024
2d68405
fix: release build on fetch rework (#54)
guybedford May 29, 2024
3027305
feat: single-tick non-tracking event loop runner
guybedford May 29, 2024
2e23c54
fixup
guybedford May 29, 2024
5bdb946
fixup
guybedford May 29, 2024
55566e9
fix event loop interest handling
guybedford May 29, 2024
4b3f9f5
simplify single tick
guybedford May 29, 2024
1d17ace
simpler loop
guybedford May 30, 2024
6a8ae9e
fixup return paths
guybedford May 30, 2024
2bad18a
Use a templated WASIHandle class to represent handles for WASI 0.2.0
tschneidereit Jun 1, 2024
ec28998
Fix bugs in runtime-evaluation
tschneidereit Jun 2, 2024
429daec
Fix bugs in headers handling
tschneidereit Jun 2, 2024
a0781f0
Make the `extract body` spec operation work for outgoing requests and…
tschneidereit Jun 2, 2024
7f4ba5a
Lock request/response body when consuming it for `.text()`, `.arrayBu…
tschneidereit Jun 2, 2024
e2f3b37
Fix handling of various value types when extracting request/response …
tschneidereit Jun 2, 2024
4820fdf
Remove some outdated comments and TODOs
tschneidereit Jun 3, 2024
f992afe
Ensure that the `ResponseFutureTask` is enqueued for streaming requests
tschneidereit Jun 3, 2024
f40bafb
Fix streaming of outgoing bodies and setting their content-length header
tschneidereit Jun 3, 2024
ce62077
Merge remote-tracking branch 'upstream/fetch-rework' into fetch
noise64 Jun 5, 2024
4e5a07d
Ensure that a streaming outgoing response body keeps the event loop a…
tschneidereit Jun 5, 2024
7221924
Fix construction of URLs for incoming requests/responses
tschneidereit Jun 5, 2024
5033ce2
Improve handling of already-closed outgoing bodies
tschneidereit Jun 5, 2024
27598cf
Merge remote-tracking branch 'refs/remotes/upstream/fetch-rework' int…
noise64 Jun 6, 2024
259fdb2
Remove some code duplication
tschneidereit Jun 6, 2024
066d573
Handle forbidden headers
tschneidereit Jun 7, 2024
1c41c36
checkpoint: smaller fetch gets working
noise64 Jun 10, 2024
38e7ed4
add fetch tests to test runner
noise64 Jun 10, 2024
bc3e5ee
always do at least 1 event loop in run_event_loop_until_interest (so …
noise64 Jun 10, 2024
41a4083
fetch js test notes / debug
noise64 Jun 10, 2024
a50422a
expose runEventLoop directly
noise64 Jun 11, 2024
c03dbed
fetch.js cleanups / guard for infinite event loop
noise64 Jun 11, 2024
c35f466
rename fetch suite to fetchsync
noise64 Jun 12, 2024
10047a3
also wait for pending jobs in run_event_loop_until_interest + cleanups
noise64 Jun 12, 2024
dc480ea
add debug log to inc / dec interest
noise64 Jun 12, 2024
78c5e2d
disable inc in maybe_stream_body
noise64 Jun 12, 2024
1731f10
Merge remote-tracking branch 'upstream/fetch-rework' into fetch
noise64 Jun 13, 2024
63ffbae
extract logging to common place
noise64 Jun 13, 2024
d44b0e5
updated test based on ComponentizeJS tests
noise64 Jun 13, 2024
b6b03ea
remove exposed runEventLoop
noise64 Jun 13, 2024
2af1dea
disabled "fetch sync" tests for now
noise64 Jun 13, 2024
ef86be3
disable "fetch sync" tests for now
noise64 Jun 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions builtins/web/fetch/fetch-api.cpp
Original file line number Diff line number Diff line change
@@ -52,14 +52,8 @@ bool fetch(JSContext *cx, unsigned argc, Value *vp) {
return ReturnPromiseRejectedWithPendingError(cx, args);
}

unique_ptr<host_api::HttpHeaders> headers;
RootedObject headers_obj(cx, RequestOrResponse::maybe_headers(request_obj));
if (headers_obj) {
headers = Headers::handle_clone(cx, headers_obj);
} else {
headers = std::make_unique<host_api::HttpHeaders>();
}

unique_ptr<host_api::HttpHeaders> headers = RequestOrResponse::headers_handle_clone(cx,
request_obj, host_api::HttpHeadersGuard::Request);
if (!headers) {
return ReturnPromiseRejectedWithPendingError(cx, args);
}
5 changes: 3 additions & 2 deletions builtins/web/fetch/fetch_event.cpp
Original file line number Diff line number Diff line change
@@ -170,7 +170,8 @@ bool send_response(host_api::HttpOutgoingResponse *response, JS::HandleObject se

bool start_response(JSContext *cx, JS::HandleObject response_obj, bool streaming) {
auto status = Response::status(response_obj);
auto headers = RequestOrResponse::headers_clone(cx, response_obj);
auto headers = RequestOrResponse::headers_handle_clone(cx, response_obj,
host_api::HttpHeadersGuard::Response);
if (!headers) {
return false;
}
@@ -321,7 +322,7 @@ bool FetchEvent::respondWith(JSContext *cx, unsigned argc, JS::Value *vp) {
bool FetchEvent::respondWithError(JSContext *cx, JS::HandleObject self) {
MOZ_RELEASE_ASSERT(state(self) == State::unhandled || state(self) == State::waitToRespond);

auto headers = std::make_unique<host_api::HttpHeaders>();
auto headers = std::make_unique<host_api::HttpHeaders>(host_api::HttpHeadersGuard::Response);
auto *response = host_api::HttpOutgoingResponse::make(500, std::move(headers));

auto body_res = response->body();
38 changes: 25 additions & 13 deletions builtins/web/fetch/headers.cpp
Original file line number Diff line number Diff line change
@@ -290,7 +290,7 @@ static bool switch_mode(JSContext* cx, HandleObject self, const Headers::Mode mo
SetReservedSlot(self, static_cast<size_t>(Headers::Slots::Entries), ObjectValue(*map));
} else {
MOZ_ASSERT(mode == Headers::Mode::HostOnly);
auto handle = new host_api::HttpHeaders();
auto handle = new host_api::HttpHeaders(Headers::guard(self));
SetReservedSlot(self, static_cast<size_t>(Headers::Slots::Handle), PrivateValue(handle));
}

@@ -351,7 +351,7 @@ static bool switch_mode(JSContext* cx, HandleObject self, const Headers::Mode mo
string_entries.emplace_back(std::move(name), std::move(value));
}

auto handle = host_api::HttpHeaders::FromEntries(string_entries);
auto handle = host_api::HttpHeaders::FromEntries(Headers::guard(self), string_entries);
if (handle.is_err()) {
JS_ReportErrorASCII(cx, "Failed to clone headers");
return false;
@@ -417,7 +417,7 @@ bool prepare_for_entries_modification(JSContext* cx, JS::HandleObject self) {
if (mode == Headers::Mode::HostOnly) {
auto handle = get_handle(self);
if (!handle->is_writable()) {
auto new_handle = handle->clone();
auto new_handle = handle->clone(Headers::guard(self));
if (!new_handle) {
JS_ReportErrorASCII(cx, "Failed to clone headers");
return false;
@@ -446,6 +446,11 @@ bool append_single_normalized_header_value(JSContext *cx, HandleObject self,
}
} else {
MOZ_ASSERT(mode == Headers::Mode::ContentOnly);
auto guard = Headers::guard(self);
if (!host_api::HttpHeaders::check_guard(guard, name_chars)) {
return true;
}

RootedObject entries(cx, Headers::get_entries(cx, self));
if (!entries) {
return false;
@@ -523,35 +528,40 @@ void init_from_handle(JSObject* self, host_api::HttpHeadersReadOnly* handle) {
SetReservedSlot(self, static_cast<uint32_t>(Headers::Slots::Handle), PrivateValue(handle));
}

JSObject *Headers::create(JSContext *cx) {
JSObject *Headers::create(JSContext *cx, host_api::HttpHeadersGuard guard) {
JSObject* self = JS_NewObjectWithGivenProto(cx, &class_, proto_obj);
if (!self) {
return nullptr;
}

SetReservedSlot(self, static_cast<uint32_t>(Slots::Guard),
JS::Int32Value(static_cast<int32_t>(guard)));
SetReservedSlot(self, static_cast<uint32_t>(Slots::Mode),
JS::Int32Value(static_cast<int32_t>(Mode::Uninitialized)));
return self;
}

JSObject *Headers::create(JSContext *cx, host_api::HttpHeadersReadOnly *handle) {
RootedObject self(cx, create(cx));
JSObject *Headers::create(JSContext *cx, host_api::HttpHeadersReadOnly *handle, host_api::HttpHeadersGuard guard) {
RootedObject self(cx, create(cx, guard));
if (!self) {
return nullptr;
}

init_from_handle(self, handle);
return self;
}

JSObject *Headers::create(JSContext *cx, HandleValue init_headers) {
RootedObject self(cx, create(cx));
JSObject *Headers::create(JSContext *cx, HandleValue init_headers, host_api::HttpHeadersGuard guard) {
RootedObject self(cx, create(cx, guard));
if (!self) {
return nullptr;
}
return create(cx, self, init_headers);
return init_entries(cx, self, init_headers);
}

JSObject *Headers::create(JSContext *cx, HandleObject self, HandleValue initv) {
JSObject *Headers::init_entries(JSContext *cx, HandleObject self, HandleValue initv) {
// TODO: check if initv is a Headers instance and clone its handle if so.
// TODO: But note: forbidden headers have to be applied correctly.
bool consumed = false;
if (!core::maybe_consume_sequence_or_record<append_header_value>(cx, initv, self,
&consumed, "Headers")) {
@@ -870,7 +880,9 @@ bool Headers::constructor(JSContext *cx, unsigned argc, JS::Value *vp) {
if (!headersInstance) {
return false;
}
JS::RootedObject headers(cx, create(cx, headersInstance, headersInit));
SetReservedSlot(headersInstance, static_cast<uint32_t>(Slots::Guard),
JS::Int32Value(static_cast<int32_t>(host_api::HttpHeadersGuard::None)));
JS::RootedObject headers(cx, init_entries(cx, headersInstance, headersInit));
if (!headers) {
return false;
}
@@ -916,7 +928,7 @@ unique_ptr<host_api::HttpHeaders> Headers::handle_clone(JSContext* cx, HandleObj

// If this instance uninitialized, return an empty handle without initializing this instance.
if (mode == Mode::Uninitialized) {
return std::make_unique<host_api::HttpHeaders>();
return std::make_unique<host_api::HttpHeaders>(guard(self));
}

if (mode == Mode::ContentOnly && !switch_mode(cx, self, Mode::CachedInContent)) {
@@ -925,7 +937,7 @@ unique_ptr<host_api::HttpHeaders> Headers::handle_clone(JSContext* cx, HandleObj
return nullptr;
}

auto handle = unique_ptr<host_api::HttpHeaders>(get_handle(self)->clone());
auto handle = unique_ptr<host_api::HttpHeaders>(get_handle(self)->clone(guard(self)));
if (!handle) {
JS_ReportErrorASCII(cx, "Failed to clone headers");
return nullptr;
18 changes: 14 additions & 4 deletions builtins/web/fetch/headers.h
Original file line number Diff line number Diff line change
@@ -63,6 +63,7 @@ class Headers final : public BuiltinImpl<Headers> {
Handle,
Entries, // Map holding headers if they are available in-content.
Mode,
Guard,
Count,
};

@@ -88,6 +89,12 @@ class Headers final : public BuiltinImpl<Headers> {
return static_cast<Mode>(modeVal.toInt32());
}

static host_api::HttpHeadersGuard guard(JSObject* self) {
MOZ_ASSERT(Headers::is_instance(self));
Value modeVal = JS::GetReservedSlot(self, static_cast<size_t>(Slots::Guard));
return static_cast<host_api::HttpHeadersGuard>(modeVal.toInt32());
}

static const JSFunctionSpec static_methods[];
static const JSPropertySpec static_properties[];
static const JSFunctionSpec methods[];
@@ -98,10 +105,13 @@ class Headers final : public BuiltinImpl<Headers> {
static bool init_class(JSContext *cx, HandleObject global);
static bool constructor(JSContext *cx, unsigned argc, Value *vp);

static JSObject *create(JSContext *cx);
static JSObject *create(JSContext *cx, HandleValue init_headers);
static JSObject *create(JSContext *cx, HandleObject self, HandleValue init_headers);
static JSObject *create(JSContext *cx, host_api::HttpHeadersReadOnly *handle);
static JSObject *create(JSContext *cx, host_api::HttpHeadersGuard guard);
static JSObject *create(JSContext *cx, HandleValue init_headers,
host_api::HttpHeadersGuard guard);
static JSObject *create(JSContext *cx, host_api::HttpHeadersReadOnly *handle,
host_api::HttpHeadersGuard guard);

static JSObject *init_entries(JSContext *cx, HandleObject self, HandleValue init_headers);

/// Returns a Map object containing the headers.
///
35 changes: 16 additions & 19 deletions builtins/web/fetch/request-response.cpp
Original file line number Diff line number Diff line change
@@ -428,21 +428,26 @@ JSObject *RequestOrResponse::maybe_headers(JSObject *obj) {
return JS::GetReservedSlot(obj, static_cast<uint32_t>(Slots::Headers)).toObjectOrNull();
}

unique_ptr<host_api::HttpHeaders> RequestOrResponse::headers_clone(JSContext* cx,
HandleObject self) {
unique_ptr<host_api::HttpHeaders> RequestOrResponse::headers_handle_clone(JSContext* cx,
HandleObject self, host_api::HttpHeadersGuard guard) {
MOZ_ASSERT(is_instance(self));

RootedObject headers(cx, maybe_headers(self));
if (headers) {
return Headers::handle_clone(cx, headers);
}

auto res = handle(self)->headers();
auto handle = RequestOrResponse::handle(self);
if (!handle) {
return std::make_unique<host_api::HttpHeaders>(guard);
}

auto res = handle->headers();
if (auto *err = res.to_err()) {
HANDLE_ERROR(cx, *err);
return nullptr;
}
return unique_ptr<host_api::HttpHeaders>(res.unwrap()->clone());
return unique_ptr<host_api::HttpHeaders>(res.unwrap()->clone(guard));
}

bool finish_outgoing_body_streaming(JSContext* cx, HandleObject body_owner) {
@@ -500,11 +505,14 @@ bool RequestOrResponse::append_body(JSContext *cx, JS::HandleObject self, JS::Ha
JSObject *RequestOrResponse::headers(JSContext *cx, JS::HandleObject obj) {
JSObject *headers = maybe_headers(obj);
if (!headers) {
host_api::HttpHeadersGuard guard = Request::is_instance(obj)
? host_api::HttpHeadersGuard::Request
: host_api::HttpHeadersGuard::Response;
host_api::HttpHeadersReadOnly *handle;
if (is_incoming(obj) && (handle = headers_handle(obj))) {
headers = Headers::create(cx, handle);
headers = Headers::create(cx, handle, guard);
} else {
headers = Headers::create(cx);
headers = Headers::create(cx, guard);
}
if (!headers) {
return nullptr;
@@ -1702,12 +1710,7 @@ JSObject *Request::create(JSContext *cx, JS::HandleObject requestInstance, JS::H
headers_val.setObject(*input_headers);
}
if (!headers_val.isUndefined()) {
JS::RootedObject headersInstance(
cx, JS_NewObjectWithGivenProto(cx, &Headers::class_, Headers::proto_obj));
if (!headersInstance)
return nullptr;

headers = Headers::create(cx, headersInstance, headers_val);
headers = Headers::create(cx, headers_val, host_api::HttpHeadersGuard::Request);
if (!headers) {
return nullptr;
}
@@ -2498,13 +2501,7 @@ bool Response::constructor(JSContext *cx, unsigned argc, JS::Value *vp) {

// 7. (Reordered) If `init`["headers"] `exists`, then `fill` `this`’s `headers` with
// `init`["headers"].
JS::RootedObject headers(cx);
JS::RootedObject headersInstance(
cx, JS_NewObjectWithGivenProto(cx, &Headers::class_, Headers::proto_obj));
if (!headersInstance)
return false;

headers = Headers::create(cx, headersInstance, headers_val);
JS::RootedObject headers(cx, Headers::create(cx, headers_val, host_api::HttpHeadersGuard::Response));
if (!headers) {
return false;
}
3 changes: 2 additions & 1 deletion builtins/web/fetch/request-response.h
Original file line number Diff line number Diff line change
@@ -58,7 +58,8 @@ class RequestOrResponse final {
*
* The handle is guaranteed to be uniquely owned by the caller.
*/
static unique_ptr<host_api::HttpHeaders> headers_clone(JSContext *, HandleObject self);
static unique_ptr<host_api::HttpHeaders> headers_handle_clone(JSContext *, HandleObject self,
host_api::HttpHeadersGuard guard);

/**
* Returns the RequestOrResponse's Headers, reifying it if necessary.
Loading
Loading