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