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