diff --git a/builtins/web/fetch/fetch_event.cpp b/builtins/web/fetch/fetch_event.cpp index 0a886912..dbd1a074 100644 --- a/builtins/web/fetch/fetch_event.cpp +++ b/builtins/web/fetch/fetch_event.cpp @@ -15,6 +15,7 @@ #include #include +#include using builtins::web::event::Event; using builtins::web::event::EventTarget; @@ -32,6 +33,8 @@ JSString *fetch_type_atom; JS::PersistentRootedObject INSTANCE; host_api::HttpOutgoingBody *STREAMING_BODY; +constexpr const std::string_view DEFAULT_NO_HANDLER_ERROR_MSG = "ERROR: no fetch-event handler triggered, was one registered?"; + void inc_pending_promise_count(JSObject *self) { MOZ_ASSERT(FetchEvent::is_instance(self)); auto count = JS::GetReservedSlot(self, FetchEvent::Slots::PendingPromiseCount).toInt32(); @@ -322,10 +325,18 @@ bool FetchEvent::respondWith(JSContext *cx, unsigned argc, JS::Value *vp) { return true; } -bool FetchEvent::respondWithError(JSContext *cx, JS::HandleObject self) { + bool FetchEvent::respondWithError(JSContext *cx, JS::HandleObject self, std::optional body_text) { MOZ_RELEASE_ASSERT(state(self) == State::unhandled || state(self) == State::waitToRespond); auto headers = std::make_unique(); + if (body_text) { + auto header_set_res = headers->set("content-type", "text/plain"); + if (auto *err = header_set_res.to_err()) { + HANDLE_ERROR(cx, *err); + return false; + } + } + auto *response = host_api::HttpOutgoingResponse::make(500, std::move(headers)); auto body_res = response->body(); @@ -334,6 +345,11 @@ bool FetchEvent::respondWithError(JSContext *cx, JS::HandleObject self) { return false; } + if (body_text) { + auto body = std::move(body_res.unwrap()); + body->write(reinterpret_cast(body_text->data()), body_text->length()); + } + return send_response(response, self, FetchEvent::State::respondedWithError); } @@ -504,7 +520,9 @@ bool handle_incoming_request(host_api::HttpIncomingRequest *request) { } if (!FetchEvent::response_started(fetch_event)) { - FetchEvent::respondWithError(ENGINE->cx(), fetch_event); + // If at this point no fetch event handler has run, we can + // send a specific error indicating that there is likely no handler registered + FetchEvent::respondWithError(ENGINE->cx(), fetch_event, DEFAULT_NO_HANDLER_ERROR_MSG); return true; } diff --git a/builtins/web/fetch/fetch_event.h b/builtins/web/fetch/fetch_event.h index 11554dbd..951c0a7b 100644 --- a/builtins/web/fetch/fetch_event.h +++ b/builtins/web/fetch/fetch_event.h @@ -7,6 +7,8 @@ #include "../event/event.h" +#include + namespace builtins::web::fetch::fetch_event { class FetchEvent final : public BuiltinNoConstructor { @@ -57,7 +59,19 @@ class FetchEvent final : public BuiltinNoConstructor { static bool init_incoming_request(JSContext *cx, JS::HandleObject self, host_api::HttpIncomingRequest *req); - static bool respondWithError(JSContext *cx, JS::HandleObject self); + /** + * @brief Responds with an error that contains some text for the HTTP response body + * + * @param cx The Javascript context + * @param self A handle to the `FetchEvent` object + * @param body_text optional text to send as the body + * + * @return True if the response was sent successfully + * @throws None directly, but surfaces errors to JS via `HANDLE_ERROR` + */ + static bool respondWithError(JSContext *cx, + JS::HandleObject self, + std::optional body_text = std::nullopt); static bool is_active(JSObject *self); static State state(JSObject *self);