8d9d6a65dd28868cd299a767713af09649e698f9
[WebKit-https.git] / Source / WebCore / Modules / fetch / FetchResponse.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 "FetchResponse.h"
31
32 #include "FetchRequest.h"
33 #include "HTTPParsers.h"
34 #include "JSBlob.h"
35 #include "JSFetchResponse.h"
36 #include "ScriptExecutionContext.h"
37
38 namespace WebCore {
39
40 static inline bool isRedirectStatus(int status)
41 {
42     return status == 301 || status == 302 || status == 303 || status == 307 || status == 308;
43 }
44
45 Ref<FetchResponse> FetchResponse::error(ScriptExecutionContext& context)
46 {
47     auto response = adoptRef(*new FetchResponse(context, { }, FetchHeaders::create(FetchHeaders::Guard::Immutable), { }));
48     response->m_response.setType(Type::Error);
49     return response;
50 }
51
52 ExceptionOr<Ref<FetchResponse>> FetchResponse::redirect(ScriptExecutionContext& context, const String& url, int status)
53 {
54     // FIXME: Tighten the URL parsing algorithm according https://url.spec.whatwg.org/#concept-url-parser.
55     URL requestURL = context.completeURL(url);
56     if (!requestURL.isValid() || !requestURL.user().isEmpty() || !requestURL.pass().isEmpty())
57         return Exception { TypeError };
58     if (!isRedirectStatus(status))
59         return Exception { RangeError };
60     auto redirectResponse = adoptRef(*new FetchResponse(context, { }, FetchHeaders::create(FetchHeaders::Guard::Immutable), { }));
61     redirectResponse->m_response.setHTTPStatusCode(status);
62     redirectResponse->m_headers->fastSet(HTTPHeaderName::Location, requestURL.string());
63     return WTFMove(redirectResponse);
64 }
65
66 ExceptionOr<void> FetchResponse::setStatus(int status, const String& statusText)
67 {
68     if (!isValidReasonPhrase(statusText))
69         return Exception { TypeError };
70     m_response.setHTTPStatusCode(status);
71     m_response.setHTTPStatusText(statusText);
72     return { };
73 }
74
75 void FetchResponse::initializeWith(FetchBody::Init&& body)
76 {
77     ASSERT(scriptExecutionContext());
78     extractBody(*scriptExecutionContext(), WTFMove(body));
79     updateContentType();
80 }
81
82 void FetchResponse::setBodyAsReadableStream()
83 {
84     ASSERT(isBodyNull());
85     setBody(FetchBody::readableStreamBody());
86     updateContentType();
87 }
88
89 FetchResponse::FetchResponse(ScriptExecutionContext& context, std::optional<FetchBody>&& body, Ref<FetchHeaders>&& headers, ResourceResponse&& response)
90     : FetchBodyOwner(context, WTFMove(body), WTFMove(headers))
91     , m_response(WTFMove(response))
92 {
93 }
94
95 Ref<FetchResponse> FetchResponse::cloneForJS()
96 {
97     ASSERT(scriptExecutionContext());
98     ASSERT(!isDisturbedOrLocked());
99
100     auto clone = adoptRef(*new FetchResponse(*scriptExecutionContext(), std::nullopt, FetchHeaders::create(headers()), ResourceResponse(m_response)));
101     clone->cloneBody(*this);
102     return clone;
103 }
104
105 void FetchResponse::fetch(ScriptExecutionContext& context, FetchRequest& request, FetchPromise&& promise)
106 {
107     if (request.isBodyReadableStream()) {
108         promise.reject(TypeError, "ReadableStream uploading is not supported");
109         return;
110     }
111     auto response = adoptRef(*new FetchResponse(context, FetchBody::loadingBody(), FetchHeaders::create(FetchHeaders::Guard::Immutable), { }));
112
113     response->m_bodyLoader.emplace(response.get(), WTFMove(promise));
114     if (!response->m_bodyLoader->start(context, request))
115         response->m_bodyLoader = std::nullopt;
116 }
117
118 const String& FetchResponse::url() const
119 {
120     if (m_responseURL.isNull())
121         m_responseURL = m_response.url().serialize(true);
122     return m_responseURL;
123 }
124
125 void FetchResponse::BodyLoader::didSucceed()
126 {
127     ASSERT(m_response.hasPendingActivity());
128     m_response.m_body->loadingSucceeded();
129
130 #if ENABLE(STREAMS_API)
131     if (m_response.m_readableStreamSource && !m_response.body().consumer().hasData())
132         m_response.closeStream();
133 #endif
134
135     if (m_loader->isStarted()) {
136         Ref<FetchResponse> protector(m_response);
137         m_response.m_bodyLoader = std::nullopt;
138     }
139 }
140
141 void FetchResponse::BodyLoader::didFail()
142 {
143     ASSERT(m_response.hasPendingActivity());
144     if (m_promise)
145         std::exchange(m_promise, std::nullopt)->reject(TypeError);
146
147 #if ENABLE(STREAMS_API)
148     if (m_response.m_readableStreamSource) {
149         if (!m_response.m_readableStreamSource->isCancelling())
150             m_response.m_readableStreamSource->error(ASCIILiteral("Loading failed"));
151         m_response.m_readableStreamSource = nullptr;
152     }
153 #endif
154
155     // Check whether didFail is called as part of FetchLoader::start.
156     if (m_loader->isStarted()) {
157         Ref<FetchResponse> protector(m_response);
158         m_response.m_bodyLoader = std::nullopt;
159     }
160 }
161
162 FetchResponse::BodyLoader::BodyLoader(FetchResponse& response, FetchPromise&& promise)
163     : m_response(response)
164     , m_promise(WTFMove(promise))
165 {
166     m_response.setPendingActivity(&m_response);
167 }
168
169 FetchResponse::BodyLoader::~BodyLoader()
170 {
171     m_response.unsetPendingActivity(&m_response);
172 }
173
174 void FetchResponse::BodyLoader::didReceiveResponse(const ResourceResponse& resourceResponse)
175 {
176     ASSERT(m_promise);
177
178     m_response.m_response = resourceResponse;
179     m_response.m_headers->filterAndFill(resourceResponse.httpHeaderFields(), FetchHeaders::Guard::Response);
180     m_response.updateContentType();
181
182     std::exchange(m_promise, std::nullopt)->resolve(m_response);
183 }
184
185 void FetchResponse::BodyLoader::didReceiveData(const char* data, size_t size)
186 {
187 #if ENABLE(STREAMS_API)
188     ASSERT(m_response.m_readableStreamSource);
189     auto& source = *m_response.m_readableStreamSource;
190
191     if (!source.isPulling()) {
192         m_response.body().consumer().append(data, size);
193         return;
194     }
195
196     if (m_response.body().consumer().hasData() && !source.enqueue(m_response.body().consumer().takeAsArrayBuffer())) {
197         stop();
198         return;
199     }
200     if (!source.enqueue(ArrayBuffer::tryCreate(data, size))) {
201         stop();
202         return;
203     }
204     source.resolvePullPromise();
205 #else
206     UNUSED_PARAM(data);
207     UNUSED_PARAM(size);
208 #endif
209 }
210
211 bool FetchResponse::BodyLoader::start(ScriptExecutionContext& context, const FetchRequest& request)
212 {
213     m_loader = std::make_unique<FetchLoader>(*this, &m_response.m_body->consumer());
214     m_loader->start(context, request);
215     return m_loader->isStarted();
216 }
217
218 void FetchResponse::BodyLoader::stop()
219 {
220     m_promise = std::nullopt;
221     if (m_loader)
222         m_loader->stop();
223 }
224
225 void FetchResponse::consume(unsigned type, Ref<DeferredPromise>&& wrapper)
226 {
227     ASSERT(type <= static_cast<unsigned>(FetchBodyConsumer::Type::Text));
228     auto consumerType = static_cast<FetchBodyConsumer::Type>(type);
229
230     if (isLoading()) {
231         consumeOnceLoadingFinished(consumerType, WTFMove(wrapper));
232         return;
233     }
234
235     switch (consumerType) {
236     case FetchBodyConsumer::Type::ArrayBuffer:
237         arrayBuffer(WTFMove(wrapper));
238         return;
239     case FetchBodyConsumer::Type::Blob:
240         blob(WTFMove(wrapper));
241         return;
242     case FetchBodyConsumer::Type::JSON:
243         json(WTFMove(wrapper));
244         return;
245     case FetchBodyConsumer::Type::Text:
246         text(WTFMove(wrapper));
247         return;
248     case FetchBodyConsumer::Type::None:
249         ASSERT_NOT_REACHED();
250         return;
251     }
252 }
253
254 #if ENABLE(STREAMS_API)
255 void FetchResponse::startConsumingStream(unsigned type)
256 {
257     m_isDisturbed = true;
258     auto consumerType = static_cast<FetchBodyConsumer::Type>(type);
259     m_consumer.setType(consumerType);
260     if (consumerType == FetchBodyConsumer::Type::Blob)
261         m_consumer.setContentType(Blob::normalizedContentType(extractMIMETypeFromMediaType(m_contentType)));
262 }
263
264 void FetchResponse::consumeChunk(Ref<JSC::Uint8Array>&& chunk)
265 {
266     m_consumer.append(chunk->data(), chunk->byteLength());
267 }
268
269 void FetchResponse::finishConsumingStream(Ref<DeferredPromise>&& promise)
270 {
271     m_consumer.resolve(WTFMove(promise));
272 }
273
274 void FetchResponse::consumeBodyAsStream()
275 {
276     ASSERT(m_readableStreamSource);
277     m_isDisturbed = true;
278     if (!isLoading()) {
279         body().consumeAsStream(*this, *m_readableStreamSource);
280         if (!m_readableStreamSource->isPulling())
281             m_readableStreamSource = nullptr;
282         return;
283     }
284
285     ASSERT(m_bodyLoader);
286
287     RefPtr<SharedBuffer> data = m_bodyLoader->startStreaming();
288     if (data) {
289         if (!m_readableStreamSource->enqueue(data->tryCreateArrayBuffer())) {
290             stop();
291             return;
292         }
293         m_readableStreamSource->resolvePullPromise();
294     }
295 }
296
297 void FetchResponse::closeStream()
298 {
299     ASSERT(m_readableStreamSource);
300     m_readableStreamSource->close();
301     m_readableStreamSource = nullptr;
302 }
303
304 void FetchResponse::feedStream()
305 {
306     ASSERT(m_readableStreamSource);
307     bool shouldCloseStream = !m_bodyLoader;
308
309     if (body().consumer().hasData()) {
310         if (!m_readableStreamSource->enqueue(body().consumer().takeAsArrayBuffer())) {
311             stop();
312             return;
313         }
314         if (!shouldCloseStream) {
315             m_readableStreamSource->resolvePullPromise();
316             return;
317         }
318     } else if (!shouldCloseStream)
319         return;
320
321     closeStream();
322 }
323
324 ReadableStreamSource* FetchResponse::createReadableStreamSource()
325 {
326     ASSERT(!m_readableStreamSource);
327     ASSERT(!m_isDisturbed);
328
329     if (isBodyNull())
330         return nullptr;
331
332     m_readableStreamSource = adoptRef(*new FetchResponseSource(*this));
333     return m_readableStreamSource.get();
334 }
335
336 RefPtr<SharedBuffer> FetchResponse::BodyLoader::startStreaming()
337 {
338     ASSERT(m_loader);
339     return m_loader->startStreaming();
340 }
341
342 void FetchResponse::cancel()
343 {
344     m_isDisturbed = true;
345     stop();
346 }
347
348 #endif
349
350 void FetchResponse::stop()
351 {
352     RefPtr<FetchResponse> protectedThis(this);
353     FetchBodyOwner::stop();
354     if (m_bodyLoader) {
355         m_bodyLoader->stop();
356         m_bodyLoader = std::nullopt;
357     }
358 }
359
360 const char* FetchResponse::activeDOMObjectName() const
361 {
362     return "Response";
363 }
364
365 bool FetchResponse::canSuspendForDocumentSuspension() const
366 {
367     // FIXME: We can probably do the same strategy as XHR.
368     return !isActive();
369 }
370
371 } // namespace WebCore