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
48namespace WebCore {
49
50static const unsigned bufferSize = 1024;
51static const int maxVectorLength = 0x7fffffff;
52static const long long positionNotSpecified = -1;
53
54static const int httpOK = 200;
55static const int httpPartialContent = 206;
56static const int httpNotAllowed = 403;
57static const int httpNotFound = 404;
58static const int httpRequestedRangeNotSatisfiable = 416;
59static const int httpInternalError = 500;
60static const char* httpOKText = "OK";
61static const char* httpPartialContentText = "Partial Content";
62static const char* httpNotAllowedText = "Not Allowed";
63static const char* httpNotFoundText = "Not Found";
64static const char* httpRequestedRangeNotSatisfiableText = "Requested Range Not Satisfiable";
65static const char* httpInternalErrorText = "Internal Server Error";
66
67static const int notFoundError = 1;
68static const int securityError = 2;
69static const int rangeError = 3;
70static const int notReadableError = 4;
71
72///////////////////////////////////////////////////////////////////////////////
73// BlobResourceSynchronousLoader
74
75namespace {
76
77class BlobResourceSynchronousLoader : public ResourceHandleClient {
78public:
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
86private:
87    ResourceError& m_error;
88    ResourceResponse& m_response;
89    Vector<char>& m_data;
90};
91
92BlobResourceSynchronousLoader::BlobResourceSynchronousLoader(ResourceError& error, ResourceResponse& response, Vector<char>& data)
93    : m_error(error)
94    , m_response(response)
95    , m_data(data)
96{
97}
98
99void 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
115void BlobResourceSynchronousLoader::didReceiveData(ResourceHandle*, const char*, int, int)
116{
117}
118
119void BlobResourceSynchronousLoader::didFinishLoading(ResourceHandle*, double)
120{
121}
122
123void BlobResourceSynchronousLoader::didFail(ResourceHandle*, const ResourceError& error)
124{
125    m_error = error;
126}
127
128}
129
130///////////////////////////////////////////////////////////////////////////////
131// BlobResourceHandle
132
133// static
134void 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
141BlobResourceHandle::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
162BlobResourceHandle::~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
173void 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
187void delayedStartBlobResourceHandle(void* context)
188{
189    RefPtr<BlobResourceHandle> handle = adoptRef(static_cast<BlobResourceHandle*>(context));
190    handle->doStart();
191}
192
193void 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
207void 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
237void 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
268void 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
297void 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
326int 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
369int 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
389int 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
422void 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
445void 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
456void 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
473void 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
486void BlobResourceHandle::didRead(int bytesRead)
487{
488    consumeData(m_buffer.data(), bytesRead);
489}
490
491void 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
520void 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
534void BlobResourceHandle::notifyResponse()
535{
536    if (!client())
537        return;
538
539    if (m_errorCode) {
540        notifyResponseOnError();
541        notifyFinish();
542    } else
543        notifyResponseOnSuccess();
544}
545
546void 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
558void 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
584void BlobResourceHandle::notifyReceiveData(const char* data, int bytesRead)
585{
586    if (client())
587        client()->didReceiveData(this, data, bytesRead, bytesRead);
588}
589
590void BlobResourceHandle::notifyFail(int errorCode)
591{
592    if (client())
593        client()->didFail(this, ResourceError(String(), errorCode, firstRequest().url(), String()));
594}
595
596static void doNotifyFinish(void* context)
597{
598    BlobResourceHandle* handle = static_cast<BlobResourceHandle*>(context);
599    if (handle->client())
600        handle->client()->didFinishLoading(handle, 0);
601}
602
603void 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