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