Title: [278274] trunk
Revision
278274
Author
you...@apple.com
Date
2021-05-31 01:29:02 -0700 (Mon, 31 May 2021)

Log Message

Go to network in case fetch event is not yet responded when being destroyed instead of failing the load
https://bugs.webkit.org/show_bug.cgi?id=226374
<rdar://78298472>

Reviewed by Alex Christensen.

Source/WebCore:

In case worker is terminated, instead of failing fetch events that are pending a response, we should go to the network.
This mirrors what is already done in ServiceWorkerFetchTask.

This can for instance happen in case a lot of fetches are done in parallel on the same service worker.
The service worker will do the fetch itself but given there are lots of fetches, some fetch might not start until other loads are complete.
This may trigger the fetch timeout which might then trigger terminating the worker.
We should probably revisit our fetch timeout policy now that we have added worker spin detection.

Test: http/wpt/service-workers/fetch-worker-terminate.https.html

* testing/ServiceWorkerInternals.cpp:
(WebCore::ServiceWorkerInternals::terminate):
(WebCore::ServiceWorkerInternals::waitForFetchEventToFinish):
* testing/ServiceWorkerInternals.h:
* testing/ServiceWorkerInternals.idl:
* workers/service/FetchEvent.cpp:
(WebCore::FetchEvent::~FetchEvent):
Update logging to only log the case where respondWith is called but fetch event is destroyed before processing the response.
Otherwise, we would log the case of respondWith being never called, which is happening often and leads to go to the network.
(WebCore::FetchEvent::processResponse):
* workers/service/FetchEvent.h:
* workers/service/context/ServiceWorkerFetch.cpp:
(WebCore::ServiceWorkerFetch::processResponse):

LayoutTests:

* http/wpt/service-workers/fetch-worker-terminate-worker.js: Added.
(doTest):
* http/wpt/service-workers/fetch-worker-terminate.https-expected.txt: Added.
* http/wpt/service-workers/fetch-worker-terminate.https.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (278273 => 278274)


--- trunk/LayoutTests/ChangeLog	2021-05-31 07:51:36 UTC (rev 278273)
+++ trunk/LayoutTests/ChangeLog	2021-05-31 08:29:02 UTC (rev 278274)
@@ -1,3 +1,16 @@
+2021-05-31  Youenn Fablet  <you...@apple.com>
+
+        Go to network in case fetch event is not yet responded when being destroyed instead of failing the load
+        https://bugs.webkit.org/show_bug.cgi?id=226374
+        <rdar://78298472>
+
+        Reviewed by Alex Christensen.
+
+        * http/wpt/service-workers/fetch-worker-terminate-worker.js: Added.
+        (doTest):
+        * http/wpt/service-workers/fetch-worker-terminate.https-expected.txt: Added.
+        * http/wpt/service-workers/fetch-worker-terminate.https.html: Added.
+
 2021-05-30  Wenson Hsieh  <wenson_hs...@apple.com>
 
         REGRESSION (r258118): SVG paths that contain a single move command incorrect client bounding rects

Added: trunk/LayoutTests/http/wpt/service-workers/fetch-worker-terminate-worker.js (0 => 278274)


--- trunk/LayoutTests/http/wpt/service-workers/fetch-worker-terminate-worker.js	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/service-workers/fetch-worker-terminate-worker.js	2021-05-31 08:29:02 UTC (rev 278274)
@@ -0,0 +1,14 @@
+function doTest(event)
+{
+    if (!self.internals)
+        return;
+
+    if (event.request.url.includes("terminate-while-fetching")) {
+        event.respondWith(new Promise(resolve => {
+           // we do not resolve the promise.
+           setTimeout(() => internals.terminate(), 500);
+        }));
+    }
+}
+
+self.addEventListener("fetch", doTest);

Added: trunk/LayoutTests/http/wpt/service-workers/fetch-worker-terminate.https-expected.txt (0 => 278274)


--- trunk/LayoutTests/http/wpt/service-workers/fetch-worker-terminate.https-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/service-workers/fetch-worker-terminate.https-expected.txt	2021-05-31 08:29:02 UTC (rev 278274)
@@ -0,0 +1,5 @@
+
+
+PASS Setup worker
+PASS Make sure a load goes to the network when worker terminates while load is not yet responded
+

Added: trunk/LayoutTests/http/wpt/service-workers/fetch-worker-terminate.https.html (0 => 278274)


--- trunk/LayoutTests/http/wpt/service-workers/fetch-worker-terminate.https.html	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/service-workers/fetch-worker-terminate.https.html	2021-05-31 08:29:02 UTC (rev 278274)
@@ -0,0 +1,34 @@
+<html>
+<head>
+<script src=""
+<script src=""
+<script src=""
+</head>
+<body>
+<script>
+var scope = "resources";
+var activeWorker;
+
+promise_test(async (test) => {
+    var registration = await navigator.serviceWorker.register("fetch-worker-terminate-worker.js", { scope : scope });
+    activeWorker = registration.active;
+    if (activeWorker)
+        return;
+    activeWorker = registration.installing;
+    return new Promise(resolve => {
+        activeWorker.addEventListener('statechange', () => {
+            if (activeWorker.state === "activated")
+                resolve();
+        });
+    });
+}, "Setup worker");
+
+promise_test(async (test) => {
+    const iframe = await with_iframe(scope);
+
+    const response = await iframe.contentWindow.fetch("/terminate-while-fetching");
+    assert_equals(response.status, 404);
+}, "Make sure a load goes to the network when worker terminates while load is not yet responded");
+</script>
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (278273 => 278274)


--- trunk/Source/WebCore/ChangeLog	2021-05-31 07:51:36 UTC (rev 278273)
+++ trunk/Source/WebCore/ChangeLog	2021-05-31 08:29:02 UTC (rev 278274)
@@ -1,3 +1,35 @@
+2021-05-31  Youenn Fablet  <you...@apple.com>
+
+        Go to network in case fetch event is not yet responded when being destroyed instead of failing the load
+        https://bugs.webkit.org/show_bug.cgi?id=226374
+        <rdar://78298472>
+
+        Reviewed by Alex Christensen.
+
+        In case worker is terminated, instead of failing fetch events that are pending a response, we should go to the network.
+        This mirrors what is already done in ServiceWorkerFetchTask.
+
+        This can for instance happen in case a lot of fetches are done in parallel on the same service worker.
+        The service worker will do the fetch itself but given there are lots of fetches, some fetch might not start until other loads are complete.
+        This may trigger the fetch timeout which might then trigger terminating the worker.
+        We should probably revisit our fetch timeout policy now that we have added worker spin detection.
+
+        Test: http/wpt/service-workers/fetch-worker-terminate.https.html
+
+        * testing/ServiceWorkerInternals.cpp:
+        (WebCore::ServiceWorkerInternals::terminate):
+        (WebCore::ServiceWorkerInternals::waitForFetchEventToFinish):
+        * testing/ServiceWorkerInternals.h:
+        * testing/ServiceWorkerInternals.idl:
+        * workers/service/FetchEvent.cpp:
+        (WebCore::FetchEvent::~FetchEvent):
+        Update logging to only log the case where respondWith is called but fetch event is destroyed before processing the response.
+        Otherwise, we would log the case of respondWith being never called, which is happening often and leads to go to the network.
+        (WebCore::FetchEvent::processResponse):
+        * workers/service/FetchEvent.h:
+        * workers/service/context/ServiceWorkerFetch.cpp:
+        (WebCore::ServiceWorkerFetch::processResponse):
+
 2021-05-30  Wenson Hsieh  <wenson_hs...@apple.com>
 
         REGRESSION (r258118): SVG paths that contain a single move command incorrect client bounding rects

Modified: trunk/Source/WebCore/testing/ServiceWorkerInternals.cpp (278273 => 278274)


--- trunk/Source/WebCore/testing/ServiceWorkerInternals.cpp	2021-05-31 07:51:36 UTC (rev 278273)
+++ trunk/Source/WebCore/testing/ServiceWorkerInternals.cpp	2021-05-31 08:29:02 UTC (rev 278274)
@@ -50,13 +50,24 @@
     });
 }
 
+void ServiceWorkerInternals::terminate()
+{
+    callOnMainThread([identifier = m_identifier] () {
+        SWContextManager::singleton().terminateWorker(identifier, Seconds::infinity(), [] { });
+    });
+}
+
 void ServiceWorkerInternals::waitForFetchEventToFinish(FetchEvent& event, DOMPromiseDeferred<IDLInterface<FetchResponse>>&& promise)
 {
     event.onResponse([promise = WTFMove(promise), event = makeRef(event)] (auto&& result) mutable {
-        if (result.has_value())
-            promise.resolve(WTFMove(result.value()));
-        else
-            promise.reject(TypeError, result.error().localizedDescription());
+        if (!result.has_value()) {
+            String description;
+            if (auto& error = result.error())
+                description = error->localizedDescription();
+            promise.reject(TypeError, description);
+            return;
+        }
+        promise.resolve(WTFMove(result.value()));
     });
 }
 

Modified: trunk/Source/WebCore/testing/ServiceWorkerInternals.h (278273 => 278274)


--- trunk/Source/WebCore/testing/ServiceWorkerInternals.h	2021-05-31 07:51:36 UTC (rev 278273)
+++ trunk/Source/WebCore/testing/ServiceWorkerInternals.h	2021-05-31 08:29:02 UTC (rev 278274)
@@ -46,6 +46,8 @@
     ~ServiceWorkerInternals();
 
     void setOnline(bool isOnline);
+    void terminate();
+
     void waitForFetchEventToFinish(FetchEvent&, DOMPromiseDeferred<IDLInterface<FetchResponse>>&&);
     Ref<FetchEvent> createBeingDispatchedFetchEvent(ScriptExecutionContext&);
     Ref<FetchResponse> createOpaqueWithBlobBodyResponse(ScriptExecutionContext&);

Modified: trunk/Source/WebCore/testing/ServiceWorkerInternals.idl (278273 => 278274)


--- trunk/Source/WebCore/testing/ServiceWorkerInternals.idl	2021-05-31 07:51:36 UTC (rev 278273)
+++ trunk/Source/WebCore/testing/ServiceWorkerInternals.idl	2021-05-31 08:29:02 UTC (rev 278274)
@@ -30,6 +30,8 @@
     LegacyNoInterfaceObject,
 ] interface ServiceWorkerInternals {
     undefined setOnline(boolean isOnline);
+    undefined terminate();
+
     Promise<FetchResponse> waitForFetchEventToFinish(FetchEvent event);
     [CallWith=ScriptExecutionContext] FetchEvent createBeingDispatchedFetchEvent();
     [CallWith=ScriptExecutionContext] FetchResponse createOpaqueWithBlobBodyResponse();

Modified: trunk/Source/WebCore/workers/service/FetchEvent.cpp (278273 => 278274)


--- trunk/Source/WebCore/workers/service/FetchEvent.cpp	2021-05-31 07:51:36 UTC (rev 278273)
+++ trunk/Source/WebCore/workers/service/FetchEvent.cpp	2021-05-31 08:29:02 UTC (rev 278274)
@@ -56,8 +56,8 @@
 FetchEvent::~FetchEvent()
 {
     if (auto callback = WTFMove(m_onResponse)) {
-        RELEASE_LOG_ERROR(ServiceWorker, "Fetch event is destroyed without a response, respondWithEntered=%d, waitToRespond=%d, respondWithError=%d, respondPromise=%d", m_respondWithEntered, m_waitToRespond, m_respondWithError, !!m_respondPromise);
-        callback(makeUnexpected(ResourceError { errorDomainWebKitServiceWorker, 0, m_request->url(), "Fetch event is destroyed."_s, ResourceError::Type::Cancellation }));
+        RELEASE_LOG_ERROR_IF(m_respondWithEntered, ServiceWorker, "Fetch event is destroyed without a response, respondWithEntered=%d, waitToRespond=%d, respondWithError=%d, respondPromise=%d", m_respondWithEntered, m_waitToRespond, m_respondWithError, !!m_respondPromise);
+        callback(makeUnexpected(std::optional<ResourceError> { }));
     }
 }
 
@@ -106,7 +106,7 @@
     processResponse(makeUnexpected(WTFMove(error)));
 }
 
-void FetchEvent::processResponse(Expected<Ref<FetchResponse>, ResourceError>&& result)
+void FetchEvent::processResponse(Expected<Ref<FetchResponse>, std::optional<ResourceError>>&& result)
 {
     m_respondPromise = nullptr;
     m_waitToRespond = false;

Modified: trunk/Source/WebCore/workers/service/FetchEvent.h (278273 => 278274)


--- trunk/Source/WebCore/workers/service/FetchEvent.h	2021-05-31 07:51:36 UTC (rev 278273)
+++ trunk/Source/WebCore/workers/service/FetchEvent.h	2021-05-31 08:29:02 UTC (rev 278274)
@@ -59,7 +59,7 @@
 
     ExceptionOr<void> respondWith(Ref<DOMPromise>&&);
 
-    using ResponseCallback = CompletionHandler<void(Expected<Ref<FetchResponse>, ResourceError>&&)>;
+    using ResponseCallback = CompletionHandler<void(Expected<Ref<FetchResponse>, std::optional<ResourceError>>&&)>;
     WEBCORE_EXPORT void onResponse(ResponseCallback&&);
 
     FetchRequest& request() { return m_request.get(); }
@@ -75,7 +75,7 @@
     WEBCORE_EXPORT FetchEvent(const AtomString&, Init&&, IsTrusted);
 
     void promiseIsSettled();
-    void processResponse(Expected<Ref<FetchResponse>, ResourceError>&&);
+    void processResponse(Expected<Ref<FetchResponse>, std::optional<ResourceError>>&&);
     void respondWithError(ResourceError&&);
 
     Ref<FetchRequest> m_request;

Modified: trunk/Source/WebCore/workers/service/context/ServiceWorkerFetch.cpp (278273 => 278274)


--- trunk/Source/WebCore/workers/service/context/ServiceWorkerFetch.cpp	2021-05-31 07:51:36 UTC (rev 278273)
+++ trunk/Source/WebCore/workers/service/context/ServiceWorkerFetch.cpp	2021-05-31 08:29:02 UTC (rev 278274)
@@ -65,10 +65,15 @@
     return { };
 }
 
-static void processResponse(Ref<Client>&& client, Expected<Ref<FetchResponse>, ResourceError>&& result, FetchOptions::Mode mode, FetchOptions::Redirect redirect, const URL& requestURL, CertificateInfo&& certificateInfo)
+static void processResponse(Ref<Client>&& client, Expected<Ref<FetchResponse>, std::optional<ResourceError>>&& result, FetchOptions::Mode mode, FetchOptions::Redirect redirect, const URL& requestURL, CertificateInfo&& certificateInfo)
 {
     if (!result.has_value()) {
-        client->didFail(result.error());
+        auto& error = result.error();
+        if (!error) {
+            client->didNotHandle();
+            return;
+        }
+        client->didFail(*error);
         return;
     }
     auto response = WTFMove(result.value());
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to