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