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