Use completion handlers for ResourceHandleClient::canAuthenticateAgainstProtectionSpa...
[WebKit-https.git] / Source / WebCore / platform / network / BlobResourceHandle.cpp
1 /*
2  * Copyright (C) 2010 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32
33 #include "BlobResourceHandle.h"
34
35 #include "AsyncFileStream.h"
36 #include "BlobData.h"
37 #include "FileStream.h"
38 #include "FileSystem.h"
39 #include "HTTPHeaderNames.h"
40 #include "HTTPParsers.h"
41 #include "ParsedContentRange.h"
42 #include "URL.h"
43 #include "ResourceError.h"
44 #include "ResourceHandleClient.h"
45 #include "ResourceRequest.h"
46 #include "ResourceResponse.h"
47 #include "SharedBuffer.h"
48 #include <wtf/CompletionHandler.h>
49 #include <wtf/MainThread.h>
50 #include <wtf/Ref.h>
51
52 namespace WebCore {
53
54 static const unsigned bufferSize = 512 * 1024;
55
56 static const int httpOK = 200;
57 static const int httpPartialContent = 206;
58 static const int httpNotAllowed = 403;
59 static const int httpRequestedRangeNotSatisfiable = 416;
60 static const int httpInternalError = 500;
61 static const char* httpOKText = "OK";
62 static const char* httpPartialContentText = "Partial Content";
63 static const char* httpNotAllowedText = "Not Allowed";
64 static const char* httpRequestedRangeNotSatisfiableText = "Requested Range Not Satisfiable";
65 static const char* httpInternalErrorText = "Internal Server Error";
66
67 static const char* const webKitBlobResourceDomain = "WebKitBlobResource";
68
69 ///////////////////////////////////////////////////////////////////////////////
70 // BlobResourceSynchronousLoader
71
72 namespace {
73
74 class BlobResourceSynchronousLoader : public ResourceHandleClient {
75 public:
76     BlobResourceSynchronousLoader(ResourceError&, ResourceResponse&, Vector<char>&);
77
78     void didReceiveResponseAsync(ResourceHandle*, ResourceResponse&&, CompletionHandler<void()>&&) final;
79     void didFail(ResourceHandle*, const ResourceError&) final;
80     void willSendRequestAsync(ResourceHandle*, ResourceRequest&&, ResourceResponse&&, CompletionHandler<void(ResourceRequest&&)>&&) final;
81 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
82     void canAuthenticateAgainstProtectionSpaceAsync(ResourceHandle*, const ProtectionSpace&, CompletionHandler<void(bool)>&&) final;
83 #endif
84
85 private:
86     ResourceError& m_error;
87     ResourceResponse& m_response;
88     Vector<char>& m_data;
89 };
90
91 BlobResourceSynchronousLoader::BlobResourceSynchronousLoader(ResourceError& error, ResourceResponse& response, Vector<char>& data)
92     : m_error(error)
93     , m_response(response)
94     , m_data(data)
95 {
96 }
97
98 void BlobResourceSynchronousLoader::willSendRequestAsync(ResourceHandle*, ResourceRequest&& request, ResourceResponse&&, CompletionHandler<void(ResourceRequest&&)>&& completionHandler)
99 {
100     ASSERT_NOT_REACHED();
101     completionHandler(WTFMove(request));
102 }
103
104 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
105 void BlobResourceSynchronousLoader::canAuthenticateAgainstProtectionSpaceAsync(ResourceHandle*, const ProtectionSpace&, CompletionHandler<void(bool)>&& completionHandler)
106 {
107     ASSERT_NOT_REACHED();
108     completionHandler(false);
109 }
110 #endif
111
112 void BlobResourceSynchronousLoader::didReceiveResponseAsync(ResourceHandle* handle, ResourceResponse&& response, CompletionHandler<void()>&& completionHandler)
113 {
114     // We cannot handle the size that is more than maximum integer.
115     if (response.expectedContentLength() > INT_MAX) {
116         m_error = ResourceError(webKitBlobResourceDomain, static_cast<int>(BlobResourceHandle::Error::NotReadableError), response.url(), "File is too large");
117         completionHandler();
118         return;
119     }
120
121     m_response = response;
122
123     // Read all the data.
124     m_data.resize(static_cast<size_t>(response.expectedContentLength()));
125     static_cast<BlobResourceHandle*>(handle)->readSync(m_data.data(), static_cast<int>(m_data.size()));
126     completionHandler();
127 }
128
129 void BlobResourceSynchronousLoader::didFail(ResourceHandle*, const ResourceError& error)
130 {
131     m_error = error;
132 }
133
134 }
135
136 ///////////////////////////////////////////////////////////////////////////////
137 // BlobResourceHandle
138
139 Ref<BlobResourceHandle> BlobResourceHandle::createAsync(BlobData* blobData, const ResourceRequest& request, ResourceHandleClient* client)
140 {
141     return adoptRef(*new BlobResourceHandle(blobData, request, client, true));
142 }
143
144 void BlobResourceHandle::loadResourceSynchronously(BlobData* blobData, const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data)
145 {
146     if (!equalLettersIgnoringASCIICase(request.httpMethod(), "get")) {
147         error = ResourceError(webKitBlobResourceDomain, static_cast<int>(Error::MethodNotAllowed), response.url(), "Request method must be GET");
148         return;
149     }
150
151     BlobResourceSynchronousLoader loader(error, response, data);
152     RefPtr<BlobResourceHandle> handle = adoptRef(new BlobResourceHandle(blobData, request, &loader, false));
153     handle->start();
154 }
155
156 BlobResourceHandle::BlobResourceHandle(BlobData* blobData, const ResourceRequest& request, ResourceHandleClient* client, bool async)
157     : ResourceHandle { nullptr, request, client, false /* defersLoading */, false /* shouldContentSniff */, true /* shouldContentEncodingSniff */ }
158     , m_blobData { blobData }
159     , m_async { async }
160 {
161     if (m_async)
162         m_asyncStream = std::make_unique<AsyncFileStream>(*this);
163     else
164         m_stream = std::make_unique<FileStream>();
165 }
166
167 BlobResourceHandle::~BlobResourceHandle() = default;
168
169 void BlobResourceHandle::cancel()
170 {
171     m_asyncStream = nullptr;
172     m_fileOpened = false;
173
174     m_aborted = true;
175
176     ResourceHandle::cancel();
177 }
178
179 void BlobResourceHandle::start()
180 {
181     if (!m_async) {
182         doStart();
183         return;
184     }
185
186     // Finish this async call quickly and return.
187     callOnMainThread([protectedThis = makeRef(*this)]() mutable {
188         protectedThis->doStart();
189     });
190 }
191
192 void BlobResourceHandle::doStart()
193 {
194     ASSERT(isMainThread());
195
196     // Do not continue if the request is aborted or an error occurs.
197     if (erroredOrAborted())
198         return;
199
200     if (!equalLettersIgnoringASCIICase(firstRequest().httpMethod(), "get")) {
201         notifyFail(Error::MethodNotAllowed);
202         return;
203     }
204
205     // If the blob data is not found, fail now.
206     if (!m_blobData) {
207         notifyFail(Error::NotFoundError);
208         return;
209     }
210
211     // Parse the "Range" header we care about.
212     String range = firstRequest().httpHeaderField(HTTPHeaderName::Range);
213     if (!range.isEmpty() && !parseRange(range, m_rangeOffset, m_rangeEnd, m_rangeSuffixLength)) {
214         m_errorCode = Error::RangeError;
215         notifyResponse();
216         return;
217     }
218
219     if (m_async)
220         getSizeForNext();
221     else {
222         Ref<BlobResourceHandle> protectedThis(*this); // getSizeForNext calls the client
223         for (size_t i = 0; i < m_blobData->items().size() && !erroredOrAborted(); ++i)
224             getSizeForNext();
225         notifyResponse();
226     }
227 }
228
229 void BlobResourceHandle::getSizeForNext()
230 {
231     ASSERT(isMainThread());
232
233     // Do we finish validating and counting size for all items?
234     if (m_sizeItemCount >= m_blobData->items().size()) {
235         seek();
236
237         // Start reading if in asynchronous mode.
238         if (m_async) {
239             Ref<BlobResourceHandle> protectedThis(*this);
240             notifyResponse();
241         }
242         return;
243     }
244
245     const BlobDataItem& item = m_blobData->items().at(m_sizeItemCount);
246     switch (item.type()) {
247     case BlobDataItem::Type::Data:
248         didGetSize(item.length());
249         break;
250     case BlobDataItem::Type::File:
251         // Files know their sizes, but asking the stream to verify that the file wasn't modified.
252         if (m_async)
253             m_asyncStream->getSize(item.file()->path(), item.file()->expectedModificationTime());
254         else
255             didGetSize(m_stream->getSize(item.file()->path(), item.file()->expectedModificationTime()));
256         break;
257     default:
258         ASSERT_NOT_REACHED();
259     }
260 }
261
262 void BlobResourceHandle::didGetSize(long long size)
263 {
264     ASSERT(isMainThread());
265
266     // Do not continue if the request is aborted or an error occurs.
267     if (erroredOrAborted())
268         return;
269
270     // If the size is -1, it means the file has been moved or changed. Fail now.
271     if (size == -1) {
272         notifyFail(Error::NotFoundError);
273         return;
274     }
275
276     // The size passed back is the size of the whole file. If the underlying item is a sliced file, we need to use the slice length.
277     const BlobDataItem& item = m_blobData->items().at(m_sizeItemCount);
278     size = item.length();
279
280     // Cache the size.
281     m_itemLengthList.append(size);
282
283     // Count the size.
284     m_totalSize += size;
285     m_totalRemainingSize += size;
286     m_sizeItemCount++;
287
288     // Continue with the next item.
289     getSizeForNext();
290 }
291
292 void BlobResourceHandle::seek()
293 {
294     ASSERT(isMainThread());
295
296     // Convert from the suffix length to the range.
297     if (m_rangeSuffixLength != kPositionNotSpecified) {
298         m_rangeOffset = m_totalRemainingSize - m_rangeSuffixLength;
299         m_rangeEnd = m_rangeOffset + m_rangeSuffixLength - 1;
300     }
301
302     // Bail out if the range is not provided.
303     if (m_rangeOffset == kPositionNotSpecified)
304         return;
305
306     // Skip the initial items that are not in the range.
307     long long offset = m_rangeOffset;
308     for (m_readItemCount = 0; m_readItemCount < m_blobData->items().size() && offset >= m_itemLengthList[m_readItemCount]; ++m_readItemCount)
309         offset -= m_itemLengthList[m_readItemCount];
310
311     // Set the offset that need to jump to for the first item in the range.
312     m_currentItemReadSize = offset;
313
314     // Adjust the total remaining size in order not to go beyond the range.
315     if (m_rangeEnd != kPositionNotSpecified) {
316         long long rangeSize = m_rangeEnd - m_rangeOffset + 1;
317         if (m_totalRemainingSize > rangeSize)
318             m_totalRemainingSize = rangeSize;
319     } else
320         m_totalRemainingSize -= m_rangeOffset;
321 }
322
323 int BlobResourceHandle::readSync(char* buf, int length)
324 {
325     ASSERT(isMainThread());
326
327     ASSERT(!m_async);
328     Ref<BlobResourceHandle> protectedThis(*this);
329
330     int offset = 0;
331     int remaining = length;
332     while (remaining) {
333         // Do not continue if the request is aborted or an error occurs.
334         if (erroredOrAborted())
335             break;
336
337         // If there is no more remaining data to read, we are done.
338         if (!m_totalRemainingSize || m_readItemCount >= m_blobData->items().size())
339             break;
340
341         const BlobDataItem& item = m_blobData->items().at(m_readItemCount);
342         int bytesRead = 0;
343         if (item.type() == BlobDataItem::Type::Data)
344             bytesRead = readDataSync(item, buf + offset, remaining);
345         else if (item.type() == BlobDataItem::Type::File)
346             bytesRead = readFileSync(item, buf + offset, remaining);
347         else
348             ASSERT_NOT_REACHED();
349
350         if (bytesRead > 0) {
351             offset += bytesRead;
352             remaining -= bytesRead;
353         }
354     }
355
356     int result;
357     if (erroredOrAborted())
358         result = -1;
359     else
360         result = length - remaining;
361
362     if (result > 0)
363         notifyReceiveData(buf, result);
364
365     if (!result)
366         notifyFinish();
367
368     return result;
369 }
370
371 int BlobResourceHandle::readDataSync(const BlobDataItem& item, char* buf, int length)
372 {
373     ASSERT(isMainThread());
374
375     ASSERT(!m_async);
376
377     long long remaining = item.length() - m_currentItemReadSize;
378     int bytesToRead = (length > remaining) ? static_cast<int>(remaining) : length;
379     if (bytesToRead > m_totalRemainingSize)
380         bytesToRead = static_cast<int>(m_totalRemainingSize);
381     memcpy(buf, item.data().data() + item.offset() + m_currentItemReadSize, bytesToRead);
382     m_totalRemainingSize -= bytesToRead;
383
384     m_currentItemReadSize += bytesToRead;
385     if (m_currentItemReadSize == item.length()) {
386         m_readItemCount++;
387         m_currentItemReadSize = 0;
388     }
389
390     return bytesToRead;
391 }
392
393 int BlobResourceHandle::readFileSync(const BlobDataItem& item, char* buf, int length)
394 {
395     ASSERT(isMainThread());
396
397     ASSERT(!m_async);
398
399     if (!m_fileOpened) {
400         long long bytesToRead = m_itemLengthList[m_readItemCount] - m_currentItemReadSize;
401         if (bytesToRead > m_totalRemainingSize)
402             bytesToRead = m_totalRemainingSize;
403         bool success = m_stream->openForRead(item.file()->path(), item.offset() + m_currentItemReadSize, bytesToRead);
404         m_currentItemReadSize = 0;
405         if (!success) {
406             m_errorCode = Error::NotReadableError;
407             return 0;
408         }
409
410         m_fileOpened = true;
411     }
412
413     int bytesRead = m_stream->read(buf, length);
414     if (bytesRead < 0) {
415         m_errorCode = Error::NotReadableError;
416         return 0;
417     }
418     if (!bytesRead) {
419         m_stream->close();
420         m_fileOpened = false;
421         m_readItemCount++;
422     } else
423         m_totalRemainingSize -= bytesRead;
424
425     return bytesRead;
426 }
427
428 void BlobResourceHandle::readAsync()
429 {
430     ASSERT(isMainThread());
431
432     // Do not continue if the request is aborted or an error occurs.
433     if (erroredOrAborted())
434         return;
435
436     // If there is no more remaining data to read, we are done.
437     if (!m_totalRemainingSize || m_readItemCount >= m_blobData->items().size()) {
438         notifyFinish();
439         return;
440     }
441
442     const BlobDataItem& item = m_blobData->items().at(m_readItemCount);
443     if (item.type() == BlobDataItem::Type::Data)
444         readDataAsync(item);
445     else if (item.type() == BlobDataItem::Type::File)
446         readFileAsync(item);
447     else
448         ASSERT_NOT_REACHED();
449 }
450
451 void BlobResourceHandle::readDataAsync(const BlobDataItem& item)
452 {
453     ASSERT(isMainThread());
454     ASSERT(item.data().data());
455
456     Ref<BlobResourceHandle> protectedThis(*this);
457
458     long long bytesToRead = item.length() - m_currentItemReadSize;
459     if (bytesToRead > m_totalRemainingSize)
460         bytesToRead = m_totalRemainingSize;
461     consumeData(reinterpret_cast<const char*>(item.data().data()->data()) + item.offset() + m_currentItemReadSize, static_cast<int>(bytesToRead));
462     m_currentItemReadSize = 0;
463 }
464
465 void BlobResourceHandle::readFileAsync(const BlobDataItem& item)
466 {
467     ASSERT(isMainThread());
468
469     if (m_fileOpened) {
470         m_asyncStream->read(m_buffer.data(), m_buffer.size());
471         return;
472     }
473
474     long long bytesToRead = m_itemLengthList[m_readItemCount] - m_currentItemReadSize;
475     if (bytesToRead > m_totalRemainingSize)
476         bytesToRead = static_cast<int>(m_totalRemainingSize);
477     m_asyncStream->openForRead(item.file()->path(), item.offset() + m_currentItemReadSize, bytesToRead);
478     m_fileOpened = true;
479     m_currentItemReadSize = 0;
480 }
481
482 void BlobResourceHandle::didOpen(bool success)
483 {
484     ASSERT(m_async);
485
486     if (!success) {
487         failed(Error::NotReadableError);
488         return;
489     }
490
491     // Continue the reading.
492     readAsync();
493 }
494
495 void BlobResourceHandle::didRead(int bytesRead)
496 {
497     if (bytesRead < 0) {
498         failed(Error::NotReadableError);
499         return;
500     }
501
502     consumeData(m_buffer.data(), bytesRead);
503 }
504
505 void BlobResourceHandle::consumeData(const char* data, int bytesRead)
506 {
507     ASSERT(m_async);
508     Ref<BlobResourceHandle> protectedThis(*this);
509
510     m_totalRemainingSize -= bytesRead;
511
512     // Notify the client.
513     if (bytesRead)
514         notifyReceiveData(data, bytesRead);
515
516     if (m_fileOpened) {
517         // When the current item is a file item, the reading is completed only if bytesRead is 0.
518         if (!bytesRead) {
519             // Close the file.
520             m_fileOpened = false;
521             m_asyncStream->close();
522
523             // Move to the next item.
524             m_readItemCount++;
525         }
526     } else {
527         // Otherwise, we read the current text item as a whole and move to the next item.
528         m_readItemCount++;
529     }
530
531     // Continue the reading.
532     readAsync();
533 }
534
535 void BlobResourceHandle::failed(Error errorCode)
536 {
537     ASSERT(m_async);
538     Ref<BlobResourceHandle> protectedThis(*this);
539
540     // Notify the client.
541     notifyFail(errorCode);
542
543     // Close the file if needed.
544     if (m_fileOpened) {
545         m_fileOpened = false;
546         m_asyncStream->close();
547     }
548 }
549
550 void BlobResourceHandle::notifyResponse()
551 {
552     if (!client())
553         return;
554
555     if (m_errorCode != Error::NoError) {
556         Ref<BlobResourceHandle> protectedThis(*this);
557         notifyResponseOnError();
558         notifyFinish();
559     } else
560         notifyResponseOnSuccess();
561 }
562
563 void BlobResourceHandle::notifyResponseOnSuccess()
564 {
565     ASSERT(isMainThread());
566
567     bool isRangeRequest = m_rangeOffset != kPositionNotSpecified;
568     ResourceResponse response(firstRequest().url(), m_blobData->contentType(), m_totalRemainingSize, String());
569     response.setHTTPStatusCode(isRangeRequest ? httpPartialContent : httpOK);
570     response.setHTTPStatusText(isRangeRequest ? httpPartialContentText : httpOKText);
571
572     response.setHTTPHeaderField(HTTPHeaderName::ContentType, m_blobData->contentType());
573     response.setHTTPHeaderField(HTTPHeaderName::ContentLength, String::number(m_totalRemainingSize));
574
575     if (isRangeRequest)
576         response.setHTTPHeaderField(HTTPHeaderName::ContentRange, ParsedContentRange(m_rangeOffset, m_rangeEnd, m_totalSize).headerValue());
577     // FIXME: If a resource identified with a blob: URL is a File object, user agents must use that file's name attribute,
578     // as if the response had a Content-Disposition header with the filename parameter set to the File's name attribute.
579     // Notably, this will affect a name suggested in "File Save As".
580
581     client()->didReceiveResponseAsync(this, WTFMove(response), [this, protectedThis = makeRef(*this)] {
582         m_buffer.resize(bufferSize);
583         readAsync();
584     });
585 }
586
587 void BlobResourceHandle::notifyResponseOnError()
588 {
589     ASSERT(m_errorCode != Error::NoError);
590
591     ResourceResponse response(firstRequest().url(), "text/plain", 0, String());
592     switch (m_errorCode) {
593     case Error::RangeError:
594         response.setHTTPStatusCode(httpRequestedRangeNotSatisfiable);
595         response.setHTTPStatusText(httpRequestedRangeNotSatisfiableText);
596         break;
597     case Error::SecurityError:
598         response.setHTTPStatusCode(httpNotAllowed);
599         response.setHTTPStatusText(httpNotAllowedText);
600         break;
601     default:
602         response.setHTTPStatusCode(httpInternalError);
603         response.setHTTPStatusText(httpInternalErrorText);
604         break;
605     }
606
607     client()->didReceiveResponseAsync(this, WTFMove(response), [this, protectedThis = makeRef(*this)] {
608         m_buffer.resize(bufferSize);
609         readAsync();
610     });
611 }
612
613 void BlobResourceHandle::notifyReceiveData(const char* data, int bytesRead)
614 {
615     if (client())
616         client()->didReceiveBuffer(this, SharedBuffer::create(reinterpret_cast<const uint8_t*>(data), bytesRead), bytesRead);
617 }
618
619 void BlobResourceHandle::notifyFail(Error errorCode)
620 {
621     if (client())
622         client()->didFail(this, ResourceError(webKitBlobResourceDomain, static_cast<int>(errorCode), firstRequest().url(), String()));
623 }
624
625 static void doNotifyFinish(BlobResourceHandle& handle)
626 {
627     if (handle.aborted())
628         return;
629
630     if (!handle.client())
631         return;
632
633     handle.client()->didFinishLoading(&handle);
634 }
635
636 void BlobResourceHandle::notifyFinish()
637 {
638     if (!m_async) {
639         doNotifyFinish(*this);
640         return;
641     }
642
643     // Schedule to notify the client from a standalone function because the client might dispose the handle immediately from the callback function
644     // while we still have BlobResourceHandle calls in the stack.
645     callOnMainThread([protectedThis = makeRef(*this)]() mutable {
646         doNotifyFinish(protectedThis);
647     });
648
649 }
650
651 } // namespace WebCore