fa98dd80e4a26de4ca1778f3f540e21ba3407ea8
[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 "FetchBodySource.h"
35 #include "FetchHeaders.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     return WTF::switchOn(value, [&](RefPtr<Blob>& value) mutable {
46         Ref<const Blob> blob = value.releaseNonNull();
47         contentType = blob->type();
48         return FetchBody(WTFMove(blob));
49     }, [&](RefPtr<DOMFormData>& value) mutable {
50         Ref<DOMFormData> domFormData = value.releaseNonNull();
51         auto formData = FormData::createMultiPart(domFormData.get(), &downcast<Document>(context));
52         contentType = makeString("multipart/form-data; boundary=", formData->boundary().data());
53         return FetchBody(WTFMove(formData));
54     }, [&](RefPtr<URLSearchParams>& value) mutable {
55         Ref<const URLSearchParams> params = value.releaseNonNull();
56         contentType = HTTPHeaderValues::formURLEncodedContentType();
57         return FetchBody(WTFMove(params));
58     }, [&](RefPtr<ArrayBuffer>& value) mutable {
59         Ref<const ArrayBuffer> buffer = value.releaseNonNull();
60         return FetchBody(WTFMove(buffer));
61     }, [&](RefPtr<ArrayBufferView>& value) mutable {
62         Ref<const ArrayBufferView> buffer = value.releaseNonNull();
63         return FetchBody(WTFMove(buffer));
64     }, [&](RefPtr<ReadableStream>& stream) mutable {
65         return FetchBody(stream.releaseNonNull());
66     }, [&](String& value) {
67         contentType = HTTPHeaderValues::textPlainContentType();
68         return FetchBody(WTFMove(value));
69     });
70 }
71
72 std::optional<FetchBody> FetchBody::fromFormData(FormData* formData)
73 {
74     if (!formData || formData->isEmpty())
75         return std::nullopt;
76
77     if (auto buffer = formData->asSharedBuffer()) {
78         FetchBody body;
79         body.m_consumer.setData(buffer.releaseNonNull());
80         return WTFMove(body);
81     }
82
83     // FIXME: Support blob and form data bodies.
84     return std::nullopt;
85 }
86
87 void FetchBody::arrayBuffer(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise)
88 {
89     m_consumer.setType(FetchBodyConsumer::Type::ArrayBuffer);
90     consume(owner, WTFMove(promise));
91 }
92
93 void FetchBody::blob(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise, const String& contentType)
94 {
95     m_consumer.setType(FetchBodyConsumer::Type::Blob);
96     m_consumer.setContentType(Blob::normalizedContentType(extractMIMETypeFromMediaType(contentType)));
97     consume(owner, WTFMove(promise));
98 }
99
100 void FetchBody::json(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise)
101 {
102     if (isText()) {
103         fulfillPromiseWithJSON(WTFMove(promise), textBody());
104         return;
105     }
106     m_consumer.setType(FetchBodyConsumer::Type::JSON);
107     consume(owner, WTFMove(promise));
108 }
109
110 void FetchBody::text(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise)
111 {
112     if (isText()) {
113         promise->resolve<IDLDOMString>(textBody());
114         return;
115     }
116     m_consumer.setType(FetchBodyConsumer::Type::Text);
117     consume(owner, WTFMove(promise));
118 }
119
120 void FetchBody::consumeOnceLoadingFinished(FetchBodyConsumer::Type type, Ref<DeferredPromise>&& promise, const String& contentType)
121 {
122     m_consumer.setType(type);
123     m_consumer.setConsumePromise(WTFMove(promise));
124     if (type == FetchBodyConsumer::Type::Blob)
125         m_consumer.setContentType(Blob::normalizedContentType(extractMIMETypeFromMediaType(contentType)));
126 }
127
128 void FetchBody::consume(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise)
129 {
130     if (isArrayBuffer()) {
131         consumeArrayBuffer(WTFMove(promise));
132         return;
133     }
134     if (isArrayBufferView()) {
135         consumeArrayBufferView(WTFMove(promise));
136         return;
137     }
138     if (isText()) {
139         consumeText(WTFMove(promise), textBody());
140         return;
141     }
142     if (isURLSearchParams()) {
143         consumeText(WTFMove(promise), urlSearchParamsBody().toString());
144         return;
145     }
146     if (isBlob()) {
147         consumeBlob(owner, WTFMove(promise));
148         return;
149     }
150     if (isFormData()) {
151         // FIXME: Support consuming FormData.
152         promise->reject(NotSupportedError);
153         return;
154     }
155
156     m_consumer.resolve(WTFMove(promise), m_readableStream.get());
157 }
158
159 #if ENABLE(STREAMS_API)
160 void FetchBody::consumeAsStream(FetchBodyOwner& owner, FetchBodySource& source)
161 {
162     bool closeStream = false;
163     if (isArrayBuffer()) {
164         closeStream = source.enqueue(ArrayBuffer::tryCreate(arrayBufferBody().data(), arrayBufferBody().byteLength()));
165         m_data = nullptr;
166     } else if (isArrayBufferView()) {
167         closeStream = source.enqueue(ArrayBuffer::tryCreate(arrayBufferViewBody().baseAddress(), arrayBufferViewBody().byteLength()));
168         m_data = nullptr;
169     } else if (isText()) {
170         auto data = UTF8Encoding().encode(textBody(), EntitiesForUnencodables);
171         closeStream = source.enqueue(ArrayBuffer::tryCreate(data.data(), data.length()));
172         m_data = nullptr;
173     } else if (isURLSearchParams()) {
174         auto data = UTF8Encoding().encode(urlSearchParamsBody().toString(), EntitiesForUnencodables);
175         closeStream = source.enqueue(ArrayBuffer::tryCreate(data.data(), data.length()));
176         m_data = nullptr;
177     } else if (isBlob()) {
178         owner.loadBlob(blobBody(), nullptr);
179         m_data = nullptr;
180     } else if (isFormData())
181         source.error(ASCIILiteral("not implemented"));
182     else if (m_consumer.hasData())
183         closeStream = source.enqueue(m_consumer.takeAsArrayBuffer());
184     else
185         closeStream = true;
186
187     if (closeStream)
188         source.close();
189 }
190 #endif
191
192 void FetchBody::consumeArrayBuffer(Ref<DeferredPromise>&& promise)
193 {
194     m_consumer.resolveWithData(WTFMove(promise), static_cast<const uint8_t*>(arrayBufferBody().data()), arrayBufferBody().byteLength());
195     m_data = nullptr;
196 }
197
198 void FetchBody::consumeArrayBufferView(Ref<DeferredPromise>&& promise)
199 {
200     m_consumer.resolveWithData(WTFMove(promise), static_cast<const uint8_t*>(arrayBufferViewBody().baseAddress()), arrayBufferViewBody().byteLength());
201     m_data = nullptr;
202 }
203
204 void FetchBody::consumeText(Ref<DeferredPromise>&& promise, const String& text)
205 {
206     auto data = UTF8Encoding().encode(text, EntitiesForUnencodables);
207     m_consumer.resolveWithData(WTFMove(promise), reinterpret_cast<const uint8_t*>(data.data()), data.length());
208     m_data = nullptr;
209 }
210
211 void FetchBody::consumeBlob(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise)
212 {
213     m_consumer.setConsumePromise(WTFMove(promise));
214     owner.loadBlob(blobBody(), &m_consumer);
215     m_data = nullptr;
216 }
217
218 void FetchBody::loadingFailed()
219 {
220     m_consumer.loadingFailed();
221 }
222
223 void FetchBody::loadingSucceeded()
224 {
225     m_consumer.loadingSucceeded();
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(&downcast<Document>(context));
247         return body;
248     }
249     if (auto* data = m_consumer.data())
250         return FormData::create(data->data(), data->size());
251
252     ASSERT_NOT_REACHED();
253     return nullptr;
254 }
255
256 FetchBody::TakenData FetchBody::take()
257 {
258     if (m_consumer.hasData()) {
259         auto buffer = m_consumer.takeData();
260         if (!buffer)
261             return nullptr;
262         return buffer.releaseNonNull();
263     }
264
265     if (isBlob()) {
266         auto body = FormData::create();
267         body->appendBlob(blobBody().url());
268         return WTFMove(body);
269     }
270
271     if (isFormData())
272         return formDataBody();
273
274     if (isText()) {
275         auto data = UTF8Encoding().encode(textBody(), EntitiesForUnencodables);
276         return SharedBuffer::create(data.data(), data.length());
277     }
278
279     if (isURLSearchParams()) {
280         auto data = UTF8Encoding().encode(urlSearchParamsBody().toString(), EntitiesForUnencodables);
281         return SharedBuffer::create(data.data(), data.length());
282     }
283
284     if (isArrayBuffer())
285         return SharedBuffer::create(reinterpret_cast<const char*>(arrayBufferBody().data()), arrayBufferBody().byteLength());
286     if (isArrayBufferView())
287         return SharedBuffer::create(reinterpret_cast<const uint8_t*>(arrayBufferViewBody().baseAddress()), arrayBufferViewBody().byteLength());
288
289     return nullptr;
290 }
291
292 FetchBody FetchBody::clone()
293 {
294     FetchBody clone(m_consumer);
295
296     if (isArrayBuffer())
297         clone.m_data = arrayBufferBody();
298     else if (isArrayBufferView())
299         clone.m_data = arrayBufferViewBody();
300     else if (isBlob())
301         clone.m_data = blobBody();
302     else if (isFormData())
303         clone.m_data = const_cast<FormData&>(formDataBody());
304     else if (isText())
305         clone.m_data = textBody();
306     else if (isURLSearchParams())
307         clone.m_data = urlSearchParamsBody();
308
309     if (m_readableStream) {
310         auto clones = m_readableStream->tee();
311         m_readableStream = WTFMove(clones.first);
312         clone.m_readableStream = WTFMove(clones.second);
313     }
314     return clone;
315 }
316
317 }