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