A response body promise should be rejected in case of a failure happening after the HTTP response
https://bugs.webkit.org/show_bug.cgi?id=202792

Reviewed by Chris Dumez.

LayoutTests/imported/w3c:

* web-platform-tests/service-workers/service-worker/fetch-error-worker.js: Added.
(doTest):
* web-platform-tests/service-workers/service-worker/fetch-error.https-expected.txt: Added.
* web-platform-tests/service-workers/service-worker/fetch-error.https.html: Added.

Source/WebCore:

Test: imported/w3c/web-platform-tests/service-workers/service-worker/fetch-error.https.html

* Modules/fetch/FetchResponse.cpp:
(WebCore::FetchResponse::BodyLoader::didFail):
Propagate error to fetch body consumer if any.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@251101 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/imported/w3c/ChangeLog b/LayoutTests/imported/w3c/ChangeLog
index 285bf06..a04a033 100644
--- a/LayoutTests/imported/w3c/ChangeLog
+++ b/LayoutTests/imported/w3c/ChangeLog
@@ -1,3 +1,15 @@
+2019-10-14  Youenn Fablet  <youenn@apple.com>
+
+        A response body promise should be rejected in case of a failure happening after the HTTP response
+        https://bugs.webkit.org/show_bug.cgi?id=202792
+
+        Reviewed by Chris Dumez.
+
+        * web-platform-tests/service-workers/service-worker/fetch-error-worker.js: Added.
+        (doTest):
+        * web-platform-tests/service-workers/service-worker/fetch-error.https-expected.txt: Added.
+        * web-platform-tests/service-workers/service-worker/fetch-error.https.html: Added.
+
 2019-10-11  Ryosuke Niwa  <rniwa@webkit.org>
 
         Add the support for ShadowRoot.delegateFocus
diff --git a/LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/fetch-error-worker.js b/LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/fetch-error-worker.js
new file mode 100644
index 0000000..788252c
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/fetch-error-worker.js
@@ -0,0 +1,22 @@
+importScripts("/resources/testharness.js");
+
+function doTest(event)
+{
+    if (!event.request.url.includes("fetch-error-test"))
+        return;
+
+    let counter = 0;
+    const stream = new ReadableStream({ pull: controller => {
+        switch (++counter) {
+        case 1:
+            controller.enqueue(new Uint8Array([1]));
+            return;
+        default:
+            // We asynchronously error the stream so that there is ample time to resolve the fetch promise and call text() on the response.
+            step_timeout(() => controller.error("Sorry"), 50);
+        }
+    }});
+    event.respondWith(new Response(stream));
+}
+
+self.addEventListener("fetch", doTest);
diff --git a/LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/fetch-error.https-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/fetch-error.https-expected.txt
new file mode 100644
index 0000000..088eb85
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/fetch-error.https-expected.txt
@@ -0,0 +1,6 @@
+CONSOLE MESSAGE: FetchEvent.respondWith received an error: Sorry
+
+PASS Setup service worker 
+PASS Make sure a load that makes progress does not time out 
+PASS Unregister service worker 
+
diff --git a/LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/fetch-error.https.html b/LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/fetch-error.https.html
new file mode 100644
index 0000000..901c158
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/fetch-error.https.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+</head>
+<body>
+<script>
+var scope = "resources";
+var registration;
+
+promise_test(async (test) => {
+    registration = await navigator.serviceWorker.register("fetch-error-worker.js", { scope : scope });
+    var activeWorker = registration.active;
+    if (activeWorker)
+        return;
+    activeWorker = registration.installing;
+    return new Promise(resolve => {
+        activeWorker.addEventListener('statechange', () => {
+            if (activeWorker.state === "activated")
+                resolve();
+        });
+    });
+}, "Setup service worker");
+
+promise_test(async (test) => {
+    const iframe = await with_iframe(scope);
+
+    const response = await iframe.contentWindow.fetch("fetch-error-test");
+    await response.text().then(assert_unreached, (error) => { assert_true(error.message.includes("Sorry")); });
+    iframe.remove();
+}, "Make sure a load that makes progress does not time out");
+
+promise_test(async () => {
+    registration.unregister();
+}, "Unregister service worker");
+</script>
+</body>
+</html>
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 1cbc88e..0e97f72 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,16 @@
+2019-10-14  Youenn Fablet  <youenn@apple.com>
+
+        A response body promise should be rejected in case of a failure happening after the HTTP response
+        https://bugs.webkit.org/show_bug.cgi?id=202792
+
+        Reviewed by Chris Dumez.
+
+        Test: imported/w3c/web-platform-tests/service-workers/service-worker/fetch-error.https.html
+
+        * Modules/fetch/FetchResponse.cpp:
+        (WebCore::FetchResponse::BodyLoader::didFail):
+        Propagate error to fetch body consumer if any.
+
 2019-10-14  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         [Clipboard API] Support writing multiple PasteboardCustomData with SharedBuffers to the pasteboard
diff --git a/Source/WebCore/Modules/fetch/FetchResponse.cpp b/Source/WebCore/Modules/fetch/FetchResponse.cpp
index ee8e2aa..d75e8eb 100644
--- a/Source/WebCore/Modules/fetch/FetchResponse.cpp
+++ b/Source/WebCore/Modules/fetch/FetchResponse.cpp
@@ -307,6 +307,8 @@
         m_response.m_readableStreamSource = nullptr;
     }
 #endif
+    if (m_response.m_body)
+        m_response.m_body->loadingFailed(*m_response.loadingException());
 
     // Check whether didFail is called as part of FetchLoader::start.
     if (m_loader && m_loader->isStarted()) {