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