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