[Cache API] Add response body storage
[WebKit-https.git] / Source / WebCore / Modules / fetch / FetchBody.cpp
1 /*
2  * Copyright (C) 2016 Canon Inc.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted, provided that the following conditions
6  * are required to be met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  * 3.  Neither the name of Canon Inc. nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR
21  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "FetchBody.h"
31
32 #include "Document.h"
33 #include "FetchBodyOwner.h"
34 #include "FetchHeaders.h"
35 #include "FetchResponseSource.h"
36 #include "HTTPHeaderValues.h"
37 #include "HTTPParsers.h"
38 #include "ReadableStreamSource.h"
39 #include <runtime/ArrayBufferView.h>
40
41 namespace WebCore {
42
43 FetchBody FetchBody::extract(ScriptExecutionContext& context, Init&& value, String& contentType)
44 {
45     if (WTF::holds_alternative<RefPtr<Blob>>(value)) {
46         Ref<const Blob> blob = WTF::get<RefPtr<Blob>>(value).releaseNonNull();
47         contentType = blob->type();
48         return FetchBody(WTFMove(blob));
49     }
50     if (WTF::holds_alternative<RefPtr<DOMFormData>>(value)) {
51         Ref<DOMFormData> domFormData = WTF::get<RefPtr<DOMFormData>>(value).releaseNonNull();
52         auto formData = FormData::createMultiPart(domFormData.get(), domFormData->encoding(), &static_cast<Document&>(context));
53         contentType = makeString("multipart/form-data; boundary=", formData->boundary().data());
54         return FetchBody(WTFMove(formData));
55     }
56
57     if (WTF::holds_alternative<RefPtr<URLSearchParams>>(value)) {
58         Ref<const URLSearchParams> params = WTF::get<RefPtr<URLSearchParams>>(value).releaseNonNull();
59         contentType = HTTPHeaderValues::formURLEncodedContentType();
60         return FetchBody(WTFMove(params));
61     }
62
63     if (WTF::holds_alternative<RefPtr<ArrayBuffer>>(value)) {
64         Ref<const ArrayBuffer> buffer = WTF::get<RefPtr<ArrayBuffer>>(value).releaseNonNull();
65         return FetchBody(WTFMove(buffer));
66     }
67     if (WTF::holds_alternative<RefPtr<ArrayBufferView>>(value)) {
68         Ref<const ArrayBufferView> buffer = WTF::get<RefPtr<ArrayBufferView>>(value).releaseNonNull();
69         return FetchBody(WTFMove(buffer));
70     }
71
72     ASSERT(WTF::holds_alternative<String>(value));
73     contentType = HTTPHeaderValues::textPlainContentType();
74     return FetchBody(WTFMove(WTF::get<String>(value)));
75 }
76
77 FetchBody FetchBody::readableStreamBody()
78 {
79     FetchBody body;
80     body.m_isReadableStream = true;
81     return body;
82 }
83
84 void FetchBody::arrayBuffer(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise)
85 {
86     m_consumer.setType(FetchBodyConsumer::Type::ArrayBuffer);
87     consume(owner, WTFMove(promise));
88 }
89
90 void FetchBody::blob(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise, const String& contentType)
91 {
92     m_consumer.setType(FetchBodyConsumer::Type::Blob);
93     m_consumer.setContentType(Blob::normalizedContentType(extractMIMETypeFromMediaType(contentType)));
94     consume(owner, WTFMove(promise));
95 }
96
97 void FetchBody::json(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise)
98 {
99     if (isText()) {
100         fulfillPromiseWithJSON(WTFMove(promise), textBody());
101         return;
102     }
103     m_consumer.setType(FetchBodyConsumer::Type::JSON);
104     consume(owner, WTFMove(promise));
105 }
106
107 void FetchBody::text(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise)
108 {
109     if (isText()) {
110         promise->resolve<IDLDOMString>(textBody());
111         return;
112     }
113     m_consumer.setType(FetchBodyConsumer::Type::Text);
114     consume(owner, WTFMove(promise));
115 }
116
117 void FetchBody::consumeOnceLoadingFinished(FetchBodyConsumer::Type type, Ref<DeferredPromise>&& promise, const String& contentType)
118 {
119     m_consumer.setType(type);
120     m_consumePromise = WTFMove(promise);
121     if (type == FetchBodyConsumer::Type::Blob)
122         m_consumer.setContentType(Blob::normalizedContentType(extractMIMETypeFromMediaType(contentType)));
123 }
124
125 void FetchBody::consume(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise)
126 {
127     if (isArrayBuffer()) {
128         consumeArrayBuffer(WTFMove(promise));
129         return;
130     }
131     if (isArrayBufferView()) {
132         consumeArrayBufferView(WTFMove(promise));
133         return;
134     }
135     if (isText()) {
136         consumeText(WTFMove(promise), textBody());
137         return;
138     }
139     if (isURLSearchParams()) {
140         consumeText(WTFMove(promise), urlSearchParamsBody().toString());
141         return;
142     }
143     if (isBlob()) {
144         consumeBlob(owner, WTFMove(promise));
145         return;
146     }
147     if (isFormData()) {
148         // FIXME: Support consuming FormData.
149         promise->reject();
150         return;
151     }
152     m_consumer.resolve(WTFMove(promise));
153 }
154
155 #if ENABLE(STREAMS_API)
156 void FetchBody::consumeAsStream(FetchBodyOwner& owner, FetchResponseSource& source)
157 {
158     bool closeStream = false;
159     if (isArrayBuffer()) {
160         closeStream = source.enqueue(ArrayBuffer::tryCreate(arrayBufferBody().data(), arrayBufferBody().byteLength()));
161         m_data = nullptr;
162     } else if (isArrayBufferView()) {
163         closeStream = source.enqueue(ArrayBuffer::tryCreate(arrayBufferViewBody().baseAddress(), arrayBufferViewBody().byteLength()));
164         m_data = nullptr;
165     } else if (isText()) {
166         auto data = UTF8Encoding().encode(textBody(), EntitiesForUnencodables);
167         closeStream = source.enqueue(ArrayBuffer::tryCreate(data.data(), data.length()));
168         m_data = nullptr;
169     } else if (isURLSearchParams()) {
170         auto data = UTF8Encoding().encode(urlSearchParamsBody().toString(), EntitiesForUnencodables);
171         closeStream = source.enqueue(ArrayBuffer::tryCreate(data.data(), data.length()));
172         m_data = nullptr;
173     } else if (isBlob()) {
174         owner.loadBlob(blobBody(), nullptr);
175         m_data = nullptr;
176     } else if (isFormData())
177         source.error(ASCIILiteral("not implemented"));
178     else if (m_consumer.hasData())
179         closeStream = source.enqueue(m_consumer.takeAsArrayBuffer());
180     else
181         closeStream = true;
182
183     if (closeStream)
184         source.close();
185 }
186 #endif
187
188 void FetchBody::consumeArrayBuffer(Ref<DeferredPromise>&& promise)
189 {
190     m_consumer.resolveWithData(WTFMove(promise), static_cast<const uint8_t*>(arrayBufferBody().data()), arrayBufferBody().byteLength());
191     m_data = nullptr;
192 }
193
194 void FetchBody::consumeArrayBufferView(Ref<DeferredPromise>&& promise)
195 {
196     m_consumer.resolveWithData(WTFMove(promise), static_cast<const uint8_t*>(arrayBufferViewBody().baseAddress()), arrayBufferViewBody().byteLength());
197     m_data = nullptr;
198 }
199
200 void FetchBody::consumeText(Ref<DeferredPromise>&& promise, const String& text)
201 {
202     auto data = UTF8Encoding().encode(text, EntitiesForUnencodables);
203     m_consumer.resolveWithData(WTFMove(promise), reinterpret_cast<const uint8_t*>(data.data()), data.length());
204     m_data = nullptr;
205 }
206
207 void FetchBody::consumeBlob(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise)
208 {
209     m_consumePromise = WTFMove(promise);
210     owner.loadBlob(blobBody(), &m_consumer);
211     m_data = nullptr;
212 }
213
214 void FetchBody::loadingFailed()
215 {
216     if (m_consumePromise) {
217         m_consumePromise->reject();
218         m_consumePromise = nullptr;
219     }
220 }
221
222 void FetchBody::loadingSucceeded()
223 {
224     if (m_consumePromise)
225         m_consumer.resolve(m_consumePromise.releaseNonNull());
226 }
227
228 RefPtr<FormData> FetchBody::bodyAsFormData(ScriptExecutionContext& context) const
229 {
230     if (isText())
231         return FormData::create(UTF8Encoding().encode(textBody(), EntitiesForUnencodables));
232     if (isURLSearchParams())
233         return FormData::create(UTF8Encoding().encode(urlSearchParamsBody().toString(), EntitiesForUnencodables));
234     if (isBlob()) {
235         RefPtr<FormData> body = FormData::create();
236         body->appendBlob(blobBody().url());
237         return body;
238     }
239     if (isArrayBuffer())
240         return FormData::create(arrayBufferBody().data(), arrayBufferBody().byteLength());
241     if (isArrayBufferView())
242         return FormData::create(arrayBufferViewBody().baseAddress(), arrayBufferViewBody().byteLength());
243     if (isFormData()) {
244         ASSERT(!context.isWorkerGlobalScope());
245         RefPtr<FormData> body = const_cast<FormData*>(&formDataBody());
246         body->generateFiles(static_cast<Document*>(&context));
247         return body;
248     }
249     ASSERT_NOT_REACHED();
250     return nullptr;
251 }
252
253 FetchBody::TakenData FetchBody::take()
254 {
255     if (m_consumer.hasData()) {
256         auto buffer = m_consumer.takeData();
257         if (!buffer)
258             return nullptr;
259         return buffer.releaseNonNull();
260     }
261
262     if (isBlob()) {
263         auto body = FormData::create();
264         body->appendBlob(blobBody().url());
265         return WTFMove(body);
266     }
267
268     if (isFormData())
269         return formDataBody();
270
271     if (isText()) {
272         auto data = UTF8Encoding().encode(textBody(), EntitiesForUnencodables);
273         return SharedBuffer::create(data.data(), data.length());
274     }
275
276     if (isURLSearchParams()) {
277         auto data = UTF8Encoding().encode(urlSearchParamsBody().toString(), EntitiesForUnencodables);
278         return SharedBuffer::create(data.data(), data.length());
279     }
280
281     if (isArrayBuffer())
282         return SharedBuffer::create(reinterpret_cast<const char*>(arrayBufferBody().data()), arrayBufferBody().byteLength());
283     if (isArrayBufferView())
284         return SharedBuffer::create(reinterpret_cast<const uint8_t*>(arrayBufferViewBody().baseAddress()), arrayBufferViewBody().byteLength());
285
286     return nullptr;
287 }
288
289 FetchBody FetchBody::clone() const
290 {
291     ASSERT(!m_consumePromise);
292     FetchBody clone(m_consumer);
293
294     if (isArrayBuffer())
295         clone.m_data = arrayBufferBody();
296     else if (isArrayBufferView())
297         clone.m_data = arrayBufferViewBody();
298     else if (isBlob())
299         clone.m_data = blobBody();
300     else if (isFormData())
301         clone.m_data = const_cast<FormData&>(formDataBody());
302     else if (isText())
303         clone.m_data = textBody();
304     else if (isURLSearchParams())
305         clone.m_data = urlSearchParamsBody();
306     return clone;
307 }
308
309 }