FetchResponse should support ConsumeData callback on chunk data is received: handling...
authorgskachkov@gmail.com <gskachkov@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 29 Jan 2018 23:39:21 +0000 (23:39 +0000)
committergskachkov@gmail.com <gskachkov@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 29 Jan 2018 23:39:21 +0000 (23:39 +0000)
https://bugs.webkit.org/show_bug.cgi?id=182008

Reviewed by Youenn Fablet.

LayoutTests/imported/w3c:

* web-platform-tests/service-workers/service-worker/fetch-event-respond-with-readable-stream-chunk.https-expected.txt: Added.
* web-platform-tests/service-workers/service-worker/fetch-event-respond-with-readable-stream-chunk.https.html: Added.
* web-platform-tests/service-workers/service-worker/resources/fetch-event-respond-with-readable-stream-chunk-iframe.html: Added.
* web-platform-tests/service-workers/service-worker/resources/fetch-event-respond-with-readable-stream-chunk-worker.js: Added.
(const.process):
(this.step):
(this.run):
(const.asyncSteps):

Source/WebCore:

Modify FetchResponse to support ConsumeData callback with
handling of ReadableStream by chunks

* Modules/cache/DOMCache.cpp:
(WebCore::DOMCache::put):
* Modules/fetch/FetchBodyConsumer.cpp:
(WebCore::FetchBodyConsumer::resolve):
* Modules/fetch/FetchResponse.cpp:
(WebCore::FetchResponse::consumeBodyFromReadableStream):
* Modules/fetch/FetchResponse.h:
* Modules/streams/ReadableStreamChunk.h: Added.
* Modules/streams/ReadableStreamSink.cpp:
(WebCore::ReadableStreamToSharedBufferSink::enqueue):
(WebCore::ReadableStreamToSharedBufferSink::close):
* Modules/streams/ReadableStreamSink.h:
* WebCore.xcodeproj/project.pbxproj:
* workers/service/context/ServiceWorkerFetch.cpp:
(WebCore::ServiceWorkerFetch::processResponse):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@227760 268f45cc-cd09-0410-ab3c-d52691b4dbfc

14 files changed:
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/fetch-event-respond-with-readable-stream-chunk.https-expected.txt [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/fetch-event-respond-with-readable-stream-chunk.https.html [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/resources/fetch-event-respond-with-readable-stream-chunk-worker.js [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/Modules/cache/DOMCache.cpp
Source/WebCore/Modules/fetch/FetchBodyConsumer.cpp
Source/WebCore/Modules/fetch/FetchResponse.cpp
Source/WebCore/Modules/fetch/FetchResponse.h
Source/WebCore/Modules/streams/ReadableStreamChunk.h [new file with mode: 0644]
Source/WebCore/Modules/streams/ReadableStreamSink.cpp
Source/WebCore/Modules/streams/ReadableStreamSink.h
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/workers/service/context/ServiceWorkerFetch.cpp

index 7292bb3..cc097f3 100644 (file)
@@ -1,3 +1,19 @@
+2018-01-29  Oleksandr Skachkov  <gskachkov@gmail.com>
+
+        FetchResponse should support ConsumeData callback on chunk data is received: handling ReadableStream bodies
+        https://bugs.webkit.org/show_bug.cgi?id=182008
+
+        Reviewed by Youenn Fablet.
+
+        * web-platform-tests/service-workers/service-worker/fetch-event-respond-with-readable-stream-chunk.https-expected.txt: Added.
+        * web-platform-tests/service-workers/service-worker/fetch-event-respond-with-readable-stream-chunk.https.html: Added.
+        * web-platform-tests/service-workers/service-worker/resources/fetch-event-respond-with-readable-stream-chunk-iframe.html: Added.
+        * web-platform-tests/service-workers/service-worker/resources/fetch-event-respond-with-readable-stream-chunk-worker.js: Added.
+        (const.process):
+        (this.step):
+        (this.run):
+        (const.asyncSteps):
+
 2018-01-26  Antoine Quint  <graouts@apple.com>
 
         [Web Animations] Distinguish between an omitted and a null timeline argument to the Animation constructor
diff --git a/LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/fetch-event-respond-with-readable-stream-chunk.https-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/fetch-event-respond-with-readable-stream-chunk.https-expected.txt
new file mode 100644 (file)
index 0000000..7228605
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS Respond by chunks with a Response built from a ReadableStream 
+
diff --git a/LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/fetch-event-respond-with-readable-stream-chunk.https.html b/LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/fetch-event-respond-with-readable-stream-chunk.https.html
new file mode 100644 (file)
index 0000000..4544a9e
--- /dev/null
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>respondWith with a response built from a ReadableStream</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/test-helpers.sub.js"></script>
+<script>
+'use strict';
+
+const WORKER = 'resources/fetch-event-respond-with-readable-stream-chunk-worker.js';
+const SCOPE = 'resources/fetch-event-respond-with-readable-stream-chunk-iframe.html';
+
+promise_test(async t => {
+    var reg = await service_worker_unregister_and_register(t, WORKER, SCOPE);
+    add_completion_callback(() => reg.unregister());
+    await wait_for_state(t, reg.installing, 'activated');
+    let iframe = await with_iframe(SCOPE);
+    t.add_cleanup(() => iframe.remove());
+
+    let response = await iframe.contentWindow.fetch('body-stream');
+    assert_equals(await response.text(), 'chunk #1 chunk #2 chunk #3 chunk #4');
+}, 'Respond by chunks with a Response built from a ReadableStream');
+</script>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/resources/fetch-event-respond-with-readable-stream-chunk-worker.js b/LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/resources/fetch-event-respond-with-readable-stream-chunk-worker.js
new file mode 100644 (file)
index 0000000..f954e3a
--- /dev/null
@@ -0,0 +1,40 @@
+'use strict';
+
+self.addEventListener('fetch', event => {
+    if (!event.request.url.match(/body-stream$/))
+        return;
+
+    var counter = 0;
+    const encoder = new TextEncoder();
+    const stream = new ReadableStream({ pull: controller => {
+        switch (++counter) {
+        case 1:
+            controller.enqueue(encoder.encode(''));
+            return;
+        case 2:
+            controller.enqueue(encoder.encode('chunk #1'));
+            return;
+        case 3:
+            controller.enqueue(encoder.encode(' '));
+            return;
+        case 4:
+            controller.enqueue(encoder.encode('chunk #2'));
+            return;
+        case 5:
+            controller.enqueue(encoder.encode(' '));
+            return;
+        case 6:
+            controller.enqueue(encoder.encode('chunk #3'));
+            return;
+        case 7:
+            controller.enqueue(encoder.encode(' '));
+            return;
+        case 8:
+            controller.enqueue(encoder.encode('chunk #4'));
+            return;
+        default:
+            controller.close();
+        }
+    }});
+    event.respondWith(new Response(stream));
+});
index adaaa50..4967f71 100644 (file)
@@ -1,3 +1,29 @@
+2018-01-29  Oleksandr Skachkov  <gskachkov@gmail.com>
+
+        FetchResponse should support ConsumeData callback on chunk data is received: handling ReadableStream bodies
+        https://bugs.webkit.org/show_bug.cgi?id=182008
+
+        Reviewed by Youenn Fablet.
+
+        Modify FetchResponse to support ConsumeData callback with 
+        handling of ReadableStream by chunks
+
+        * Modules/cache/DOMCache.cpp:
+        (WebCore::DOMCache::put):
+        * Modules/fetch/FetchBodyConsumer.cpp:
+        (WebCore::FetchBodyConsumer::resolve):
+        * Modules/fetch/FetchResponse.cpp:
+        (WebCore::FetchResponse::consumeBodyFromReadableStream):
+        * Modules/fetch/FetchResponse.h:
+        * Modules/streams/ReadableStreamChunk.h: Added.
+        * Modules/streams/ReadableStreamSink.cpp:
+        (WebCore::ReadableStreamToSharedBufferSink::enqueue):
+        (WebCore::ReadableStreamToSharedBufferSink::close):
+        * Modules/streams/ReadableStreamSink.h:
+        * WebCore.xcodeproj/project.pbxproj:
+        * workers/service/context/ServiceWorkerFetch.cpp:
+        (WebCore::ServiceWorkerFetch::processResponse):
+
 2018-01-29  Andy Estes  <aestes@apple.com>
 
         [iOS] Restrict synthetic clicks to the origin that handled the underlying touch event
index 7b6fd18..b1b0f79 100644 (file)
@@ -31,6 +31,7 @@
 #include "HTTPParsers.h"
 #include "JSFetchRequest.h"
 #include "JSFetchResponse.h"
+#include "ReadableStreamChunk.h"
 #include "ScriptExecutionContext.h"
 #include "URL.h"
 
@@ -332,10 +333,17 @@ void DOMCache::put(RequestInfo&& info, Ref<FetchResponse>&& response, DOMPromise
     }
 
     if (response->hasReadableStreamBody()) {
-        setPendingActivity(this);
-        response->consumeBodyFromReadableStream([promise = WTFMove(promise), request = WTFMove(request), response = WTFMove(response), this](ExceptionOr<RefPtr<SharedBuffer>>&& result) mutable {
-            putWithResponseData(WTFMove(promise), WTFMove(request), WTFMove(response), WTFMove(result));
-            unsetPendingActivity(this);
+        response->consumeBodyFromReadableStream([promise = WTFMove(promise), request = WTFMove(request), response = WTFMove(response), data = SharedBuffer::create(), pendingActivity = makePendingActivity(*this), this](auto&& result) mutable {
+
+            if (result.hasException()) {
+                this->putWithResponseData(WTFMove(promise), WTFMove(request), WTFMove(response), result.releaseException().isolatedCopy());
+                return;
+            }
+
+            if (auto chunk = result.returnValue())
+                data->append(reinterpret_cast<const char*>(chunk->data), chunk->size);
+            else
+                this->putWithResponseData(WTFMove(promise), WTFMove(request), WTFMove(response), RefPtr<SharedBuffer> { WTFMove(data) });
         });
         return;
     }
index 8abf165..fd442cc 100644 (file)
@@ -30,6 +30,7 @@
 #include "FetchBodyConsumer.h"
 
 #include "JSBlob.h"
+#include "ReadableStreamChunk.h"
 #include "TextResourceDecoder.h"
 
 namespace WebCore {
@@ -103,15 +104,16 @@ void FetchBodyConsumer::resolve(Ref<DeferredPromise>&& promise, ReadableStream*
 {
     if (stream) {
         ASSERT(!m_sink);
-        m_sink = ReadableStreamToSharedBufferSink::create([promise = WTFMove(promise), type = m_type, contentType = m_contentType](ExceptionOr<RefPtr<SharedBuffer>>&& result) mutable {
+        m_sink = ReadableStreamToSharedBufferSink::create([promise = WTFMove(promise), data = SharedBuffer::create(), type = m_type, contentType = m_contentType](auto&& result) mutable {
             if (result.hasException()) {
                 promise->reject(result.releaseException());
                 return;
             }
 
-            auto* data = result.returnValue() && result.returnValue()->data() ? reinterpret_cast<const unsigned char*>(result.returnValue()->data()) : nullptr;
-            auto size = data ? result.returnValue()->size() : 0;
-            resolveWithTypeAndData(WTFMove(promise), type, contentType, data, size);
+            if (auto chunk = result.returnValue())
+                data->append(reinterpret_cast<const char*>(chunk->data), chunk->size);
+            else
+                resolveWithTypeAndData(WTFMove(promise), type, contentType, reinterpret_cast<const unsigned char*>(data->data()), data->size());
         });
         m_sink->pipeFrom(*stream);
         return;
index 5a3778d..a59d128 100644 (file)
@@ -33,6 +33,7 @@
 #include "HTTPParsers.h"
 #include "JSBlob.h"
 #include "MIMETypeRegistry.h"
+#include "ReadableStreamSink.h"
 #include "ResourceError.h"
 #include "ScriptExecutionContext.h"
 
@@ -339,7 +340,7 @@ FetchResponse::ResponseData FetchResponse::consumeBody()
     return body().take();
 }
 
-void FetchResponse::consumeBodyFromReadableStream(ConsumeDataCallback&& callback)
+void FetchResponse::consumeBodyFromReadableStream(ConsumeDataByChunkCallback&& callback)
 {
     ASSERT(m_body);
     ASSERT(m_body->readableStream());
index fe4bc3a..f4dc4ae 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "FetchBodyOwner.h"
 #include "FetchHeaders.h"
+#include "ReadableStreamSink.h"
 #include "ResourceResponse.h"
 #include <runtime/TypedArrays.h>
 
@@ -41,6 +42,7 @@ class JSValue;
 namespace WebCore {
 
 class FetchRequest;
+struct ReadableStreamChunk;
 class ReadableStreamSource;
 
 class FetchResponse final : public FetchBodyOwner {
@@ -93,7 +95,9 @@ public:
 
     using ConsumeDataCallback = WTF::Function<void(ExceptionOr<RefPtr<SharedBuffer>>&&)>;
     void consumeBodyWhenLoaded(ConsumeDataCallback&&);
-    void consumeBodyFromReadableStream(ConsumeDataCallback&&);
+
+    using ConsumeDataByChunkCallback = WTF::Function<void(ExceptionOr<ReadableStreamChunk*>&&)>;
+    void consumeBodyFromReadableStream(ConsumeDataByChunkCallback&&);
 
     WEBCORE_EXPORT ResourceResponse resourceResponse() const;
 
diff --git a/Source/WebCore/Modules/streams/ReadableStreamChunk.h b/Source/WebCore/Modules/streams/ReadableStreamChunk.h
new file mode 100644 (file)
index 0000000..121ebbd
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 Oleksandr Skachkov <gskachkov@gmail.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+namespace WebCore {
+
+struct ReadableStreamChunk {
+    const uint8_t* data;
+    size_t size;
+};
+
+}
index e02a679..30db240 100644 (file)
@@ -51,17 +51,16 @@ void ReadableStreamToSharedBufferSink::enqueue(const BufferSource& buffer)
     if (!buffer.length())
         return;
 
-    if (!m_data) {
-        m_data = SharedBuffer::create(buffer.data(), buffer.length());
-        return;
+    if (m_callback) {
+        ReadableStreamChunk chunk { buffer.data(), buffer.length() };
+        m_callback(&chunk);
     }
-    m_data->append(reinterpret_cast<const char*>(buffer.data()), buffer.length());
 }
 
 void ReadableStreamToSharedBufferSink::close()
 {
-    if (auto callback = WTFMove(m_callback))
-        callback(WTFMove(m_data));
+    if (m_callback)
+        m_callback(nullptr);
 }
 
 void ReadableStreamToSharedBufferSink::error(String&& message)
index 61c2aab..bb81f26 100644 (file)
@@ -29,6 +29,7 @@
 #if ENABLE(STREAMS_API)
 
 #include "ExceptionOr.h"
+#include "ReadableStreamChunk.h"
 #include <wtf/Function.h>
 #include <wtf/RefCounted.h>
 
@@ -36,6 +37,7 @@ namespace WebCore {
 
 class BufferSource;
 class ReadableStream;
+struct ReadableStreamChunk;
 class SharedBuffer;
 
 class ReadableStreamSink : public RefCounted<ReadableStreamSink> {
@@ -49,20 +51,19 @@ public:
 
 class ReadableStreamToSharedBufferSink final : public ReadableStreamSink {
 public:
-    using Callback = WTF::Function<void(ExceptionOr<RefPtr<SharedBuffer>>&&)>;
+    using Callback = WTF::Function<void(ExceptionOr<ReadableStreamChunk*>&&)>;
     static Ref<ReadableStreamToSharedBufferSink> create(Callback&& callback) { return adoptRef(*new ReadableStreamToSharedBufferSink(WTFMove(callback))); }
     void pipeFrom(ReadableStream&);
     void clearCallback() { m_callback = { }; }
 
 private:
-    ReadableStreamToSharedBufferSink(Callback&&);
+    explicit ReadableStreamToSharedBufferSink(Callback&&);
 
     void enqueue(const BufferSource&) final;
     void close() final;
     void error(String&&) final;
 
     Callback m_callback;
-    RefPtr<SharedBuffer> m_data;
 };
 
 } // namespace WebCore
index 601f41b..cb01f8e 100644 (file)
                8AB4BC77126FDB7100DEB727 /* IgnoreDestructiveWriteCountIncrementer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8AB4BC76126FDB7100DEB727 /* IgnoreDestructiveWriteCountIncrementer.h */; };
                8AF4E55611DC5A36000ED3DE /* PerformanceNavigation.h in Headers */ = {isa = PBXBuildFile; fileRef = 8AF4E55311DC5A36000ED3DE /* PerformanceNavigation.h */; };
                8AF4E55C11DC5A63000ED3DE /* PerformanceTiming.h in Headers */ = {isa = PBXBuildFile; fileRef = 8AF4E55911DC5A63000ED3DE /* PerformanceTiming.h */; };
+               8BD37A68201BB39C0011734A /* ReadableStreamChunk.h in Headers */ = {isa = PBXBuildFile; fileRef = 8BD37A67201BB39C0011734A /* ReadableStreamChunk.h */; settings = {ATTRIBUTES = (Private, ); }; };
                8E4C96DD1AD4483500365A50 /* JSFetchResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 8E4C96D91AD4483500365A50 /* JSFetchResponse.h */; };
                8EC6C963201A251600FBFA53 /* GapLength.h in Headers */ = {isa = PBXBuildFile; fileRef = 8EC6C961201A250100FBFA53 /* GapLength.h */; settings = {ATTRIBUTES = (Private, ); }; };
                8F67561B1288B17B0047ACA3 /* EventQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 8F6756191288B17B0047ACA3 /* EventQueue.h */; settings = {ATTRIBUTES = (Private, ); }; };
                8AF4E55911DC5A63000ED3DE /* PerformanceTiming.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PerformanceTiming.h; sourceTree = "<group>"; };
                8AF4E55A11DC5A63000ED3DE /* PerformanceTiming.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = PerformanceTiming.idl; sourceTree = "<group>"; };
                8E33CD93201A29C100E39093 /* GapLength.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GapLength.cpp; sourceTree = "<group>"; };
+               8BD37A67201BB39C0011734A /* ReadableStreamChunk.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ReadableStreamChunk.h; sourceTree = "<group>"; };
                8E4C96D81AD4483500365A50 /* JSFetchResponse.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSFetchResponse.cpp; sourceTree = "<group>"; };
                8E4C96D91AD4483500365A50 /* JSFetchResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSFetchResponse.h; sourceTree = "<group>"; };
                8EC6C961201A250100FBFA53 /* GapLength.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GapLength.h; sourceTree = "<group>"; };
                                9908B0F21BCACF9100ED3572 /* ReadableStreamBYOBReader.js */,
                                41189EF71AD8232800B95672 /* ReadableStreamBYOBRequest.idl */,
                                9908B0F01BCACF9100ED5672 /* ReadableStreamBYOBRequest.js */,
+                               8BD37A67201BB39C0011734A /* ReadableStreamChunk.h */,
                                41189EF71AD8232800B90A0D /* ReadableStreamDefaultController.idl */,
                                9908B0F01BCACF9100ED0F65 /* ReadableStreamDefaultController.js */,
                                419FAFAD1ABABCD5005B828B /* ReadableStreamDefaultReader.idl */,
                                A84D827C11D333ED00972990 /* RawDataDocumentParser.h in Headers */,
                                416E6FE81BBD12DF000A6043 /* ReadableByteStreamInternalsBuiltins.h in Headers */,
                                416E6FE91BBD12E5000A6043 /* ReadableStreamBuiltins.h in Headers */,
+                               8BD37A68201BB39C0011734A /* ReadableStreamChunk.h in Headers */,
                                4129C9A91F59C56B009D7403 /* ReadableStreamDefaultController.h in Headers */,
                                416E6FE81BBD12DF000A3F64 /* ReadableStreamInternalsBuiltins.h in Headers */,
                                4129C9AF1F59CF5B009D7403 /* ReadableStreamSink.h in Headers */,
index d445b81..ee95d69 100644 (file)
@@ -33,6 +33,7 @@
 #include "FetchEvent.h"
 #include "FetchRequest.h"
 #include "FetchResponse.h"
+#include "ReadableStreamChunk.h"
 #include "ResourceRequest.h"
 #include "ServiceWorker.h"
 #include "ServiceWorkerClientIdentifier.h"
@@ -53,16 +54,16 @@ static void processResponse(Ref<Client>&& client, FetchResponse* response)
     client->didReceiveResponse(response->resourceResponse());
 
     if (response->hasReadableStreamBody()) {
-        // FIXME: We should send the body as chunks.
-        response->consumeBodyFromReadableStream([client = WTFMove(client)] (ExceptionOr<RefPtr<SharedBuffer>>&& result) mutable {
+        response->consumeBodyFromReadableStream([client = WTFMove(client)] (auto&& result) mutable {
             if (result.hasException()) {
                 client->didFail();
                 return;
             }
 
-            if (auto buffer = result.releaseReturnValue())
-                client->didReceiveData(buffer.releaseNonNull());
-            client->didFinish();
+            if (auto chunk = result.returnValue())
+                client->didReceiveData(SharedBuffer::create(reinterpret_cast<const char*>(chunk->data), chunk->size));
+            else
+                client->didFinish();
         });
         return;
     }