From fe1ce3f6af79c035885160f3de98a33a85a943dd Mon Sep 17 00:00:00 2001 From: Hyeseong Kim Date: Mon, 3 Apr 2023 18:15:37 +0900 Subject: [PATCH 01/10] add a rfc --- text/0000-custom-fetch-handler.md | 67 +++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 text/0000-custom-fetch-handler.md diff --git a/text/0000-custom-fetch-handler.md b/text/0000-custom-fetch-handler.md new file mode 100644 index 0000000..f4b67ad --- /dev/null +++ b/text/0000-custom-fetch-handler.md @@ -0,0 +1,67 @@ +--- +Status: Draft +Author: Hyeseong Kim +Start Date: 2023-04-03 +RFC PR: (leave this empty) +Related Issues: (leave this empty) +--- + +# Custom Fetch Handler + +## Summary + +This proposes a way to customize the [Fetch API]'s behavior based on well-known Web APIs. + +## Motivation + +In the Brane Project, Non-same-origin requests should be checked on the host thread, but cannot be intercepted since they operate on different context. (every miniapps have its own context) + +## Detailed design + +To solve this problem, we need a way to delegate fetch requests to different contexts. It has a similar purpose to the [Service Worker's fetch handler], but it should be able to customize origin constraints. + +A custom fetch handler is basically a [ponyfill](https://github.com/sindresorhus/ponyfill) to servie worker. It has intentionally similar interface to it to get better interopability. + +### Interface + +```webidl +interface NewFetchObject { + [NewObject] Promise fetch(RequestInfo input, optional RequestInit init = {}); +} + +interface FetchHandlerInit { + [NewObject] Promise fetch(FetchEvent event); +} + +interface FetchHandlerContainer { + [NewObject] NewFetchObject register(FetchHandlerInit init = {}); +} +``` + +### Usage + +```js +import * as FetchHandler from '@braneproject/fetch-handler'; + +const originalFetch = globalThis.fetch; + +const { fetch } = FetchHandler.register({ + async fetch(event) { + // Pass-through to original fetch + if (event.request.url.origin !== self.location.origin) { + return; + } + // Prevent the default, and handle it by custom code. + return event.respondWith(requestToHost(event.request)); + }, +}); + +globalThis.fetch = fetch; +``` + +### Implementation + +TBD + +[Fetch API]: https://fetch.spec.whatwg.org/#fetch-method +[Service Worker's fetch handler]: https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/fetch_event From 839132e9bcc2da31d6efb1957e6eb508bedf5c8d Mon Sep 17 00:00:00 2001 From: Hyeseong Kim Date: Tue, 2 May 2023 12:22:22 +0900 Subject: [PATCH 02/10] Update text/0000-custom-fetch-handler.md Co-authored-by: Jaeho Lee --- text/0000-custom-fetch-handler.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-custom-fetch-handler.md b/text/0000-custom-fetch-handler.md index f4b67ad..101f0ec 100644 --- a/text/0000-custom-fetch-handler.md +++ b/text/0000-custom-fetch-handler.md @@ -20,7 +20,7 @@ In the Brane Project, Non-same-origin requests should be checked on the host thr To solve this problem, we need a way to delegate fetch requests to different contexts. It has a similar purpose to the [Service Worker's fetch handler], but it should be able to customize origin constraints. -A custom fetch handler is basically a [ponyfill](https://github.com/sindresorhus/ponyfill) to servie worker. It has intentionally similar interface to it to get better interopability. +A custom fetch handler is basically a [ponyfill](https://github.com/sindresorhus/ponyfill) to service worker. It has intentionally similar interface to it to get better interoperability. ### Interface From af1380a5c6f1e43fb40347b82fa503a9cfa74576 Mon Sep 17 00:00:00 2001 From: Hyeseong Kim Date: Tue, 2 May 2023 14:48:54 +0900 Subject: [PATCH 03/10] add eventtarget usage --- text/0000-custom-fetch-handler.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/text/0000-custom-fetch-handler.md b/text/0000-custom-fetch-handler.md index 101f0ec..55f8124 100644 --- a/text/0000-custom-fetch-handler.md +++ b/text/0000-custom-fetch-handler.md @@ -25,7 +25,7 @@ A custom fetch handler is basically a [ponyfill](https://github.com/sindresorhus ### Interface ```webidl -interface NewFetchObject { +interface NewFetchObject : EventTarget { [NewObject] Promise fetch(RequestInfo input, optional RequestInit init = {}); } @@ -59,6 +59,18 @@ const { fetch } = FetchHandler.register({ globalThis.fetch = fetch; ``` +It can be combined with a `sw.js` script. + +```js +import * as FetchHandler from '@braneproject/fetch-handler'; + +const fetchObject = FetchHandler.register(); + +(function (self) { + importScripts('sw.js'); +})(fetchObject); +``` + ### Implementation TBD From ac0f8bfaaef07119e425a789cdab1e06c12e20d9 Mon Sep 17 00:00:00 2001 From: Hyeseong Kim Date: Mon, 8 May 2023 12:26:38 +0900 Subject: [PATCH 04/10] add missing onfetch attribute --- text/0000-custom-fetch-handler.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/text/0000-custom-fetch-handler.md b/text/0000-custom-fetch-handler.md index 55f8124..3552c2c 100644 --- a/text/0000-custom-fetch-handler.md +++ b/text/0000-custom-fetch-handler.md @@ -27,6 +27,8 @@ A custom fetch handler is basically a [ponyfill](https://github.com/sindresorhus ```webidl interface NewFetchObject : EventTarget { [NewObject] Promise fetch(RequestInfo input, optional RequestInit init = {}); + + attribute EventHandler onfetch; } interface FetchHandlerInit { From b719b0f9b5bf326003d5e25559654fa3474bc42f Mon Sep 17 00:00:00 2001 From: Hyeseong Kim Date: Wed, 10 May 2023 19:55:15 +0900 Subject: [PATCH 05/10] update spec --- text/0000-custom-fetch-handler.md | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/text/0000-custom-fetch-handler.md b/text/0000-custom-fetch-handler.md index 3552c2c..b675f5b 100644 --- a/text/0000-custom-fetch-handler.md +++ b/text/0000-custom-fetch-handler.md @@ -20,26 +20,43 @@ In the Brane Project, Non-same-origin requests should be checked on the host thr To solve this problem, we need a way to delegate fetch requests to different contexts. It has a similar purpose to the [Service Worker's fetch handler], but it should be able to customize origin constraints. -A custom fetch handler is basically a [ponyfill](https://github.com/sindresorhus/ponyfill) to service worker. It has intentionally similar interface to it to get better interoperability. - ### Interface ```webidl -interface NewFetchObject : EventTarget { +interface FetchObject : EventTarget { [NewObject] Promise fetch(RequestInfo input, optional RequestInit init = {}); + [NewObject] sequence> drainExtendLifecyclePromises(); attribute EventHandler onfetch; } -interface FetchHandlerInit { +interface FetchObjectInit { [NewObject] Promise fetch(FetchEvent event); } -interface FetchHandlerContainer { - [NewObject] NewFetchObject register(FetchHandlerInit init = {}); +interface FetchHandlerModule { + [NewObject] FetchObject register(FetchObjectInit init = {}); } ``` +There are definitions inherited from Web's living standard + +- [`EventTarget`](https://dom.spec.whatwg.org/#eventtarget), [`EventHandler`](https://html.spec.whatwg.org/multipage/webappapis.html#eventhandler) definition from the DOM standard +- [`Response`](https://fetch.spec.whatwg.org/#response-class), [`RequestInfo`](https://fetch.spec.whatwg.org/#requestinfo), [`RequestInit`](https://fetch.spec.whatwg.org/#requestinit) definitions from the Fetch standard +- [`FetchEvent`](https://w3c.github.io/ServiceWorker/#fetchevent) definition from the Service Worker standard + +#### `FetchHandlerModule` + +The `register` method creates a new `FetchObject` instance. + +#### `FetchObject` + +The `FetchObject` is basically a [ponyfill](https://github.com/sindresorhus/ponyfill) to [`ServiceWorkerGlobalScope`](https://w3c.github.io/ServiceWorker/#serviceworkerglobalscope-interface). It has intentionally similar interface to it to get better interoperability. + +The `fetch` method should be called for making network request same as the Fetch API, but can be intercepted by event listeners. + +The `drainExtendLifecyclePromises` method should return promises passed by `event.wailUntil(p)` inside of event listeners, and cleanup. + ### Usage ```js From e761c7d060b6b50f5a1897942e3c5c5be1fb07d6 Mon Sep 17 00:00:00 2001 From: Hyeseong Kim Date: Mon, 15 May 2023 20:36:56 +0900 Subject: [PATCH 06/10] add beacon, and specify more about extend lifecycle promises --- text/0000-custom-fetch-handler.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/text/0000-custom-fetch-handler.md b/text/0000-custom-fetch-handler.md index b675f5b..c1b4114 100644 --- a/text/0000-custom-fetch-handler.md +++ b/text/0000-custom-fetch-handler.md @@ -25,6 +25,7 @@ To solve this problem, we need a way to delegate fetch requests to different con ```webidl interface FetchObject : EventTarget { [NewObject] Promise fetch(RequestInfo input, optional RequestInit init = {}); + boolean sendBeacon(USVString url, optional BodyInit? data = null); [NewObject] sequence> drainExtendLifecyclePromises(); attribute EventHandler onfetch; @@ -53,9 +54,11 @@ The `register` method creates a new `FetchObject` instance. The `FetchObject` is basically a [ponyfill](https://github.com/sindresorhus/ponyfill) to [`ServiceWorkerGlobalScope`](https://w3c.github.io/ServiceWorker/#serviceworkerglobalscope-interface). It has intentionally similar interface to it to get better interoperability. -The `fetch` method should be called for making network request same as the Fetch API, but can be intercepted by event listeners. +The `fetch` method makes network request same as the [Fetch API], but can be intercepted by event listeners. It should append the result promise to the extend lifecycle promises if `{ keepalive: true }` passed. -The `drainExtendLifecyclePromises` method should return promises passed by `event.wailUntil(p)` inside of event listeners, and cleanup. +The `sendBeacon` method transmits data to url same as the [Beacon API], but can be intercepted by event listeners. It should append the result promise to the extend lifecycle promises. + +The `drainExtendLifecyclePromises` method should returns extend lifecycle promises made by `event.waitUntil(...)`, `sendBeacon(...)`, fetch with `{ keepalive: true }`, etc. And cleanup the promises. ### Usage @@ -95,4 +98,5 @@ const fetchObject = FetchHandler.register(); TBD [Fetch API]: https://fetch.spec.whatwg.org/#fetch-method +[Beacon API]: https://w3c.github.io/beacon/#sendbeacon-method [Service Worker's fetch handler]: https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/fetch_event From 6e1f1a1a9b8d612d0260134113c79e46edd71097 Mon Sep 17 00:00:00 2001 From: Hyeseong Kim Date: Thu, 18 May 2023 11:29:30 +0900 Subject: [PATCH 07/10] add a missing reference --- text/0000-custom-fetch-handler.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-custom-fetch-handler.md b/text/0000-custom-fetch-handler.md index c1b4114..6be67d5 100644 --- a/text/0000-custom-fetch-handler.md +++ b/text/0000-custom-fetch-handler.md @@ -43,7 +43,7 @@ interface FetchHandlerModule { There are definitions inherited from Web's living standard - [`EventTarget`](https://dom.spec.whatwg.org/#eventtarget), [`EventHandler`](https://html.spec.whatwg.org/multipage/webappapis.html#eventhandler) definition from the DOM standard -- [`Response`](https://fetch.spec.whatwg.org/#response-class), [`RequestInfo`](https://fetch.spec.whatwg.org/#requestinfo), [`RequestInit`](https://fetch.spec.whatwg.org/#requestinit) definitions from the Fetch standard +- [`Response`](https://fetch.spec.whatwg.org/#response-class), [`RequestInfo`](https://fetch.spec.whatwg.org/#requestinfo), [`RequestInit`](https://fetch.spec.whatwg.org/#requestinit), [`BodyInit`](https://fetch.spec.whatwg.org/#bodyinit) definitions from the Fetch standard - [`FetchEvent`](https://w3c.github.io/ServiceWorker/#fetchevent) definition from the Service Worker standard #### `FetchHandlerModule` From 67e43d00c0340c39ec9e62be71e4985d470c8a1c Mon Sep 17 00:00:00 2001 From: Hyeseong Kim Date: Fri, 19 May 2023 15:36:07 +0900 Subject: [PATCH 08/10] include XHR into spec --- text/0000-custom-fetch-handler.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/text/0000-custom-fetch-handler.md b/text/0000-custom-fetch-handler.md index 6be67d5..a365d3b 100644 --- a/text/0000-custom-fetch-handler.md +++ b/text/0000-custom-fetch-handler.md @@ -26,6 +26,10 @@ To solve this problem, we need a way to delegate fetch requests to different con interface FetchObject : EventTarget { [NewObject] Promise fetch(RequestInfo input, optional RequestInit init = {}); boolean sendBeacon(USVString url, optional BodyInit? data = null); + + const XMLHttpRequest XMLHttpRequest; + + [NewObject] sequence> drainExtendLifecyclePromises(); attribute EventHandler onfetch; @@ -45,6 +49,7 @@ There are definitions inherited from Web's living standard - [`EventTarget`](https://dom.spec.whatwg.org/#eventtarget), [`EventHandler`](https://html.spec.whatwg.org/multipage/webappapis.html#eventhandler) definition from the DOM standard - [`Response`](https://fetch.spec.whatwg.org/#response-class), [`RequestInfo`](https://fetch.spec.whatwg.org/#requestinfo), [`RequestInit`](https://fetch.spec.whatwg.org/#requestinit), [`BodyInit`](https://fetch.spec.whatwg.org/#bodyinit) definitions from the Fetch standard - [`FetchEvent`](https://w3c.github.io/ServiceWorker/#fetchevent) definition from the Service Worker standard +- [`XMLHttpRequest`](https://xhr.spec.whatwg.org/#interface-xmlhttprequest) definition from the XHR standard #### `FetchHandlerModule` @@ -54,9 +59,11 @@ The `register` method creates a new `FetchObject` instance. The `FetchObject` is basically a [ponyfill](https://github.com/sindresorhus/ponyfill) to [`ServiceWorkerGlobalScope`](https://w3c.github.io/ServiceWorker/#serviceworkerglobalscope-interface). It has intentionally similar interface to it to get better interoperability. -The `fetch` method makes network request same as the [Fetch API], but can be intercepted by event listeners. It should append the result promise to the extend lifecycle promises if `{ keepalive: true }` passed. +The `fetch` method makes as same network request as the [Fetch API], but can be intercepted by the `fetch` event listeners. It should append the result promise to the extend lifecycle promises if `{ keepalive: true }` passed. + +The `sendBeacon` method transmits data to url same as the [Beacon API], but can be intercepted by the `fetch` event listener. It should append the result promise to the extend lifecycle promises. -The `sendBeacon` method transmits data to url same as the [Beacon API], but can be intercepted by event listeners. It should append the result promise to the extend lifecycle promises. +The `XMLHttpRequest` is a constructor for [XMLHttpRequest API] that initiate a network request, but can be intercepted by the the `fetch` event listener. Since the synchronous mode cannot be implemented in a single thread context, the constructor must throw a `TypeError` when the flag is set. The `drainExtendLifecyclePromises` method should returns extend lifecycle promises made by `event.waitUntil(...)`, `sendBeacon(...)`, fetch with `{ keepalive: true }`, etc. And cleanup the promises. @@ -99,4 +106,5 @@ TBD [Fetch API]: https://fetch.spec.whatwg.org/#fetch-method [Beacon API]: https://w3c.github.io/beacon/#sendbeacon-method +[XMLHttpRequest API]: https://xhr.spec.whatwg.org/#interface-xmlhttprequest [Service Worker's fetch handler]: https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/fetch_event From 7335aab93045ecba5deb31f29329531437bd934a Mon Sep 17 00:00:00 2001 From: Hyeseong Kim Date: Fri, 19 May 2023 16:12:41 +0900 Subject: [PATCH 09/10] edit motivation --- text/0000-custom-fetch-handler.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/text/0000-custom-fetch-handler.md b/text/0000-custom-fetch-handler.md index a365d3b..cf03e32 100644 --- a/text/0000-custom-fetch-handler.md +++ b/text/0000-custom-fetch-handler.md @@ -14,7 +14,9 @@ This proposes a way to customize the [Fetch API]'s behavior based on well-known ## Motivation -In the Brane Project, Non-same-origin requests should be checked on the host thread, but cannot be intercepted since they operate on different context. (every miniapps have its own context) +In the Brane Project, every network requests should be checked on the host, but cannot be intercepted since they operate on different context (cross-origin). + +This proposal is useful in many situations. When user want to control requests by cross-origin iframes/workers on the host, when creating virtual requests that operate on HTTP semantics, etc. ## Detailed design From 22d12c64cf8f44797eb1e90d464b0fd5c1128ba7 Mon Sep 17 00:00:00 2001 From: Hyeseong Kim Date: Fri, 19 May 2023 16:37:34 +0900 Subject: [PATCH 10/10] fix detail --- text/0000-custom-fetch-handler.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-custom-fetch-handler.md b/text/0000-custom-fetch-handler.md index cf03e32..90cf23c 100644 --- a/text/0000-custom-fetch-handler.md +++ b/text/0000-custom-fetch-handler.md @@ -59,7 +59,7 @@ The `register` method creates a new `FetchObject` instance. #### `FetchObject` -The `FetchObject` is basically a [ponyfill](https://github.com/sindresorhus/ponyfill) to [`ServiceWorkerGlobalScope`](https://w3c.github.io/ServiceWorker/#serviceworkerglobalscope-interface). It has intentionally similar interface to it to get better interoperability. +The `FetchObject` is basically a [ponyfill](https://github.com/sindresorhus/ponyfill) to [`ServiceWorkerGlobalScope`](https://w3c.github.io/ServiceWorker/#serviceworkerglobalscope-interface). It has intentionally subset interface to it to get better interoperability with existing tools for service worker. The `fetch` method makes as same network request as the [Fetch API], but can be intercepted by the `fetch` event listeners. It should append the result promise to the extend lifecycle promises if `{ keepalive: true }` passed.