cd345eb9ec340c36e0f0a6eed82dd988b4f97d39
[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 "InspectorInstrumentation.h"
35 #include "JSBlob.h"
36 #include "MIMETypeRegistry.h"
37 #include "ReadableStreamSink.h"
38 #include "ResourceError.h"
39 #include "ScriptExecutionContext.h"
40 #include <wtf/text/StringConcatenateNumbers.h>
41
42 namespace WebCore {
43
44 // https://fetch.spec.whatwg.org/#null-body-status
45 static inline bool isNullBodyStatus(int status)
46 {
47     return status == 101 || status == 204 || status == 205 || status == 304;
48 }
49
50 Ref<FetchResponse> FetchResponse::create(ScriptExecutionContext& context, Optional<FetchBody>&& body, FetchHeaders::Guard guard, ResourceResponse&& response)
51 {
52     bool isSynthetic = response.type() == ResourceResponse::Type::Default || response.type() == ResourceResponse::Type::Error;
53     bool isOpaque = response.tainting() == ResourceResponse::Tainting::Opaque;
54     auto headers = isOpaque ? FetchHeaders::create(guard) : FetchHeaders::create(guard, HTTPHeaderMap { response.httpHeaderFields() });
55
56     auto fetchResponse = adoptRef(*new FetchResponse(context, WTFMove(body), WTFMove(headers), WTFMove(response)));
57     if (!isSynthetic)
58         fetchResponse->m_filteredResponse = ResourceResponseBase::filter(fetchResponse->m_internalResponse);
59     if (isOpaque)
60         fetchResponse->setBodyAsOpaque();
61     return fetchResponse;
62 }
63
64 ExceptionOr<Ref<FetchResponse>> FetchResponse::create(ScriptExecutionContext& context, Optional<FetchBody::Init>&& body, Init&& init)
65 {
66     // 1. If init’s status member is not in the range 200 to 599, inclusive, then throw a RangeError.
67     if (init.status < 200  || init.status > 599)
68         return Exception { RangeError, "Status must be between 200 and 599"_s };
69
70     // 2. If init’s statusText member does not match the reason-phrase token production, then throw a TypeError.
71     if (!isValidReasonPhrase(init.statusText))
72         return Exception { TypeError, "Status text must be a valid reason-phrase."_s };
73
74     // 3. Let r be a new Response object associated with a new response.
75     // NOTE: Creation of the Response object is delayed until all potential exceptional cases are handled.
76     
77     // 4. Set r’s headers to a new Headers object, whose header list is r’s response’s header list, and guard is "response".
78     auto headers = FetchHeaders::create(FetchHeaders::Guard::Response);
79
80     // 5. Set r’s response’s status to init’s status member.
81     auto status = init.status;
82     
83     // 6. Set r’s response’s status message to init’s statusText member.
84     auto statusText = init.statusText;
85     
86     // 7. If init’s headers member is present, then fill r’s headers with init’s headers member.
87     if (init.headers) {
88         auto result = headers->fill(*init.headers);
89         if (result.hasException())
90             return result.releaseException();
91     }
92
93     Optional<FetchBody> extractedBody;
94
95     // 8. If body is non-null, run these substeps:
96     if (body) {
97         // 8.1 If init’s status member is a null body status, then throw a TypeError.
98         //     (NOTE: 101 is included in null body status due to its use elsewhere. It does not affect this step.)
99         if (isNullBodyStatus(init.status))
100             return Exception { TypeError, "Response cannot have a body with the given status."_s };
101
102         // 8.2 Let Content-Type be null.
103         String contentType;
104
105         // 8.3 Set r’s response’s body and Content-Type to the result of extracting body.
106         auto result = FetchBody::extract(WTFMove(*body), contentType);
107         if (result.hasException())
108             return result.releaseException();
109         extractedBody = result.releaseReturnValue();
110
111         // 8.4 If Content-Type is non-null and r’s response’s header list does not contain `Content-Type`, then append
112         //     `Content-Type`/Content-Type to r’s response’s header list.
113         if (!contentType.isNull() && !headers->fastHas(HTTPHeaderName::ContentType))
114             headers->fastSet(HTTPHeaderName::ContentType, contentType);
115     }
116
117     // 9. Set r’s MIME type to the result of extracting a MIME type from r’s response’s header list.
118     auto contentType = headers->fastGet(HTTPHeaderName::ContentType);
119
120     // 10. Set r’s response’s HTTPS state to current settings object’s HTTPS state.
121     // FIXME: Implement.
122
123     // 11. Resolve r’s trailer promise with a new Headers object whose guard is "immutable".
124     // FIXME: Implement.
125     
126     // 12. Return r.
127     auto r = adoptRef(*new FetchResponse(context, WTFMove(extractedBody), WTFMove(headers), { }));
128
129     r->m_contentType = contentType;
130     auto mimeType = extractMIMETypeFromMediaType(contentType);
131     r->m_internalResponse.setMimeType(mimeType.isEmpty() ? defaultMIMEType() : mimeType);
132     r->m_internalResponse.setTextEncodingName(extractCharsetFromMediaType(contentType));
133
134     r->m_internalResponse.setHTTPStatusCode(status);
135     r->m_internalResponse.setHTTPStatusText(statusText);
136
137     return r;
138 }
139
140 Ref<FetchResponse> FetchResponse::error(ScriptExecutionContext& context)
141 {
142     auto response = adoptRef(*new FetchResponse(context, { }, FetchHeaders::create(FetchHeaders::Guard::Immutable), { }));
143     response->m_internalResponse.setType(Type::Error);
144     return response;
145 }
146
147 ExceptionOr<Ref<FetchResponse>> FetchResponse::redirect(ScriptExecutionContext& context, const String& url, int status)
148 {
149     // FIXME: Tighten the URL parsing algorithm according https://url.spec.whatwg.org/#concept-url-parser.
150     URL requestURL = context.completeURL(url);
151     if (!requestURL.isValid())
152         return Exception { TypeError, makeString("Redirection URL '", requestURL.string(), "' is invalid") };
153     if (!requestURL.user().isEmpty() || !requestURL.pass().isEmpty())
154         return Exception { TypeError, "Redirection URL contains credentials"_s };
155     if (!ResourceResponse::isRedirectionStatusCode(status))
156         return Exception { RangeError, makeString("Status code ", status, "is not a redirection status code") };
157     auto redirectResponse = adoptRef(*new FetchResponse(context, { }, FetchHeaders::create(FetchHeaders::Guard::Immutable), { }));
158     redirectResponse->m_internalResponse.setHTTPStatusCode(status);
159     redirectResponse->m_internalResponse.setHTTPHeaderField(HTTPHeaderName::Location, requestURL.string());
160     redirectResponse->m_headers->fastSet(HTTPHeaderName::Location, requestURL.string());
161     return redirectResponse;
162 }
163
164 FetchResponse::FetchResponse(ScriptExecutionContext& context, Optional<FetchBody>&& body, Ref<FetchHeaders>&& headers, ResourceResponse&& response)
165     : FetchBodyOwner(context, WTFMove(body), WTFMove(headers))
166     , m_internalResponse(WTFMove(response))
167 {
168 }
169
170 ExceptionOr<Ref<FetchResponse>> FetchResponse::clone(ScriptExecutionContext& context)
171 {
172     if (isDisturbedOrLocked())
173         return Exception { TypeError, "Body is disturbed or locked"_s };
174
175     ASSERT(scriptExecutionContext());
176
177     // If loading, let's create a stream so that data is teed on both clones.
178     if (isLoading() && !m_readableStreamSource)
179         createReadableStream(*context.execState());
180
181     // Synthetic responses do not store headers in m_internalResponse.
182     if (m_internalResponse.type() == ResourceResponse::Type::Default)
183         m_internalResponse.setHTTPHeaderFields(HTTPHeaderMap { headers().internalHeaders() });
184
185     auto clone = FetchResponse::create(context, WTF::nullopt, headers().guard(), ResourceResponse { m_internalResponse });
186     clone->cloneBody(*this);
187     clone->m_opaqueLoadIdentifier = m_opaqueLoadIdentifier;
188     clone->m_bodySizeWithPadding = m_bodySizeWithPadding;
189     return clone;
190 }
191
192 void FetchResponse::addAbortSteps(Ref<AbortSignal>&& signal)
193 {
194     m_abortSignal = WTFMove(signal);
195     m_abortSignal->addAlgorithm([this, weakThis = makeWeakPtr(this)] {
196         // FIXME: Cancel request body if it is a stream.
197         if (!weakThis)
198             return;
199
200         m_abortSignal = nullptr;
201
202         setLoadingError(Exception { AbortError, "Fetch is aborted"_s });
203
204         if (m_bodyLoader) {
205             if (auto callback = m_bodyLoader->takeNotificationCallback())
206                 callback(Exception { AbortError, "Fetch is aborted"_s });
207
208             if (auto callback = m_bodyLoader->takeConsumeDataCallback())
209                 callback(Exception { AbortError, "Fetch is aborted"_s });
210         }
211
212         if (m_readableStreamSource) {
213             if (!m_readableStreamSource->isCancelling())
214                 m_readableStreamSource->error(*loadingException());
215             m_readableStreamSource = nullptr;
216         }
217         if (m_body)
218             m_body->loadingFailed(*loadingException());
219
220         if (auto bodyLoader = WTFMove(m_bodyLoader))
221             bodyLoader->stop();
222     });
223 }
224
225 void FetchResponse::fetch(ScriptExecutionContext& context, FetchRequest& request, NotificationCallback&& responseCallback)
226 {
227     if (request.signal().aborted()) {
228         responseCallback(Exception { AbortError, "Request signal is aborted"_s });
229         // FIXME: Cancel request body if it is a stream.
230         return;
231     }
232
233     if (request.hasReadableStreamBody()) {
234         responseCallback(Exception { NotSupportedError, "ReadableStream uploading is not supported"_s });
235         return;
236     }
237
238     InspectorInstrumentation::willFetch(context, request.url());
239
240     auto response = adoptRef(*new FetchResponse(context, FetchBody { }, FetchHeaders::create(FetchHeaders::Guard::Immutable), { }));
241
242     response->body().consumer().setAsLoading();
243
244     response->addAbortSteps(request.signal());
245
246     response->m_bodyLoader = std::make_unique<BodyLoader>(response.get(), WTFMove(responseCallback));
247     if (!response->m_bodyLoader->start(context, request))
248         response->m_bodyLoader = nullptr;
249 }
250
251 const String& FetchResponse::url() const
252 {
253     if (m_responseURL.isNull()) {
254         URL url = filteredResponse().url();
255         url.removeFragmentIdentifier();
256         m_responseURL = url.string();
257     }
258     return m_responseURL;
259 }
260
261 const ResourceResponse& FetchResponse::filteredResponse() const
262 {
263     if (m_filteredResponse)
264         return m_filteredResponse.value();
265     return m_internalResponse;
266 }
267
268 void FetchResponse::BodyLoader::didSucceed()
269 {
270     ASSERT(m_response.hasPendingActivity());
271     m_response.m_body->loadingSucceeded();
272
273 #if ENABLE(STREAMS_API)
274     if (m_response.m_readableStreamSource) {
275         if (m_response.body().consumer().hasData())
276             m_response.m_readableStreamSource->enqueue(m_response.body().consumer().takeAsArrayBuffer());
277
278         m_response.closeStream();
279     }
280 #endif
281     if (auto consumeDataCallback = WTFMove(m_consumeDataCallback))
282         consumeDataCallback(nullptr);
283
284     if (m_loader->isStarted()) {
285         Ref<FetchResponse> protector(m_response);
286         m_response.m_bodyLoader = nullptr;
287     }
288 }
289
290 void FetchResponse::BodyLoader::didFail(const ResourceError& error)
291 {
292     ASSERT(m_response.hasPendingActivity());
293
294     m_response.setLoadingError(ResourceError { error });
295
296     if (auto responseCallback = WTFMove(m_responseCallback))
297         responseCallback(Exception { TypeError, error.localizedDescription() });
298
299     if (auto consumeDataCallback = WTFMove(m_consumeDataCallback))
300         consumeDataCallback(Exception { TypeError, error.localizedDescription() });
301
302 #if ENABLE(STREAMS_API)
303     if (m_response.m_readableStreamSource) {
304         if (!m_response.m_readableStreamSource->isCancelling())
305             m_response.m_readableStreamSource->error(*m_response.loadingException());
306         m_response.m_readableStreamSource = nullptr;
307     }
308 #endif
309
310     // Check whether didFail is called as part of FetchLoader::start.
311     if (m_loader && m_loader->isStarted()) {
312         Ref<FetchResponse> protector(m_response);
313         m_response.m_bodyLoader = nullptr;
314     }
315 }
316
317 FetchResponse::BodyLoader::BodyLoader(FetchResponse& response, NotificationCallback&& responseCallback)
318     : m_response(response)
319     , m_responseCallback(WTFMove(responseCallback))
320     , m_pendingActivity(m_response.makePendingActivity(m_response))
321 {
322 }
323
324 FetchResponse::BodyLoader::~BodyLoader()
325 {
326 }
327
328 static uint64_t nextOpaqueLoadIdentifier { 0 };
329 void FetchResponse::BodyLoader::didReceiveResponse(const ResourceResponse& resourceResponse)
330 {
331     m_response.m_filteredResponse = ResourceResponseBase::filter(resourceResponse);
332     m_response.m_internalResponse = resourceResponse;
333     m_response.m_internalResponse.setType(m_response.m_filteredResponse->type());
334     if (resourceResponse.tainting() == ResourceResponse::Tainting::Opaque) {
335         m_response.m_opaqueLoadIdentifier = ++nextOpaqueLoadIdentifier;
336         m_response.setBodyAsOpaque();
337     }
338
339     m_response.m_headers->filterAndFill(m_response.m_filteredResponse->httpHeaderFields(), FetchHeaders::Guard::Response);
340     m_response.updateContentType();
341
342     if (auto responseCallback = WTFMove(m_responseCallback))
343         responseCallback(m_response);
344 }
345
346 void FetchResponse::BodyLoader::didReceiveData(const char* data, size_t size)
347 {
348 #if ENABLE(STREAMS_API)
349     ASSERT(m_response.m_readableStreamSource || m_consumeDataCallback);
350 #else
351     ASSERT(m_consumeDataCallback);
352 #endif
353
354     if (m_consumeDataCallback) {
355         ReadableStreamChunk chunk { reinterpret_cast<const uint8_t*>(data), size };
356         m_consumeDataCallback(&chunk);
357         return;
358     }
359
360 #if ENABLE(STREAMS_API)
361     auto& source = *m_response.m_readableStreamSource;
362
363     if (!source.isPulling()) {
364         m_response.body().consumer().append(data, size);
365         return;
366     }
367
368     if (m_response.body().consumer().hasData() && !source.enqueue(m_response.body().consumer().takeAsArrayBuffer())) {
369         stop();
370         return;
371     }
372     if (!source.enqueue(ArrayBuffer::tryCreate(data, size))) {
373         stop();
374         return;
375     }
376     source.resolvePullPromise();
377 #else
378     UNUSED_PARAM(data);
379     UNUSED_PARAM(size);
380 #endif
381 }
382
383 bool FetchResponse::BodyLoader::start(ScriptExecutionContext& context, const FetchRequest& request)
384 {
385     m_loader = std::make_unique<FetchLoader>(*this, &m_response.m_body->consumer());
386     m_loader->start(context, request);
387     return m_loader->isStarted();
388 }
389
390 void FetchResponse::BodyLoader::stop()
391 {
392     m_responseCallback = { };
393     if (m_loader)
394         m_loader->stop();
395 }
396
397 void FetchResponse::BodyLoader::consumeDataByChunk(ConsumeDataByChunkCallback&& consumeDataCallback)
398 {
399     ASSERT(!m_consumeDataCallback);
400     m_consumeDataCallback = WTFMove(consumeDataCallback);
401     auto data = m_loader->startStreaming();
402     if (!data)
403         return;
404
405     ReadableStreamChunk chunk { reinterpret_cast<const uint8_t*>(data->data()), data->size() };
406     m_consumeDataCallback(&chunk);
407 }
408
409 FetchResponse::ResponseData FetchResponse::consumeBody()
410 {
411     ASSERT(!isBodyReceivedByChunk());
412
413     if (isBodyNull())
414         return nullptr;
415
416     ASSERT(!m_isDisturbed);
417     m_isDisturbed = true;
418
419     return body().take();
420 }
421
422 void FetchResponse::consumeBodyReceivedByChunk(ConsumeDataByChunkCallback&& callback)
423 {
424     ASSERT(isBodyReceivedByChunk());
425     ASSERT(!isDisturbed());
426     m_isDisturbed = true;
427
428     if (hasReadableStreamBody()) {
429         m_body->consumer().extract(*m_body->readableStream(), WTFMove(callback));
430         return;
431     }
432
433     ASSERT(isLoading());
434     m_bodyLoader->consumeDataByChunk(WTFMove(callback));
435 }
436
437 void FetchResponse::setBodyData(ResponseData&& data, uint64_t bodySizeWithPadding)
438 {
439     m_bodySizeWithPadding = bodySizeWithPadding;
440     WTF::switchOn(data,
441         [this](Ref<FormData>& formData) {
442             if (isBodyNull())
443                 setBody({ });
444             body().setAsFormData(WTFMove(formData));
445         },
446         [this](Ref<SharedBuffer>& buffer) {
447             if (isBodyNull())
448                 setBody({ });
449             body().consumer().setData(WTFMove(buffer));
450         },
451         [](std::nullptr_t&) {
452         }
453     );
454 }
455
456 #if ENABLE(STREAMS_API)
457 void FetchResponse::consumeChunk(Ref<JSC::Uint8Array>&& chunk)
458 {
459     body().consumer().append(chunk->data(), chunk->byteLength());
460 }
461
462 void FetchResponse::consumeBodyAsStream()
463 {
464     ASSERT(m_readableStreamSource);
465     if (!isLoading()) {
466         FetchBodyOwner::consumeBodyAsStream();
467         return;
468     }
469
470     ASSERT(m_bodyLoader);
471
472     auto data = m_bodyLoader->startStreaming();
473     if (data) {
474         if (!m_readableStreamSource->enqueue(data->tryCreateArrayBuffer())) {
475             stop();
476             return;
477         }
478         m_readableStreamSource->resolvePullPromise();
479     }
480 }
481
482 void FetchResponse::closeStream()
483 {
484     ASSERT(m_readableStreamSource);
485     m_readableStreamSource->close();
486     m_readableStreamSource = nullptr;
487 }
488
489 void FetchResponse::feedStream()
490 {
491     ASSERT(m_readableStreamSource);
492     bool shouldCloseStream = !m_bodyLoader;
493
494     if (body().consumer().hasData()) {
495         if (!m_readableStreamSource->enqueue(body().consumer().takeAsArrayBuffer())) {
496             stop();
497             return;
498         }
499         if (!shouldCloseStream) {
500             m_readableStreamSource->resolvePullPromise();
501             return;
502         }
503     } else if (!shouldCloseStream)
504         return;
505
506     closeStream();
507 }
508
509 RefPtr<SharedBuffer> FetchResponse::BodyLoader::startStreaming()
510 {
511     ASSERT(m_loader);
512     return m_loader->startStreaming();
513 }
514
515 void FetchResponse::cancel()
516 {
517     m_isDisturbed = true;
518     stop();
519 }
520
521 #endif
522
523 void FetchResponse::stop()
524 {
525     RefPtr<FetchResponse> protectedThis(this);
526     FetchBodyOwner::stop();
527     if (auto bodyLoader = WTFMove(m_bodyLoader))
528         bodyLoader->stop();
529 }
530
531 const char* FetchResponse::activeDOMObjectName() const
532 {
533     return "Response";
534 }
535
536 bool FetchResponse::canSuspendForDocumentSuspension() const
537 {
538     // FIXME: We can probably do the same strategy as XHR.
539     return !isActive();
540 }
541
542 ResourceResponse FetchResponse::resourceResponse() const
543 {
544     auto response = m_internalResponse;
545
546     if (headers().guard() != FetchHeaders::Guard::Immutable) {
547         // FIXME: Add a setHTTPHeaderFields on ResourceResponseBase.
548         for (auto& header : headers().internalHeaders())
549             response.setHTTPHeaderField(header.key, header.value);
550     }
551
552     return response;
553 }
554
555 } // namespace WebCore