• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
BlobResourceSynchronousLoader(ResourceError & error,ResourceResponse & response,Vector<char> & data)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 
didReceiveResponse(ResourceHandle * handle,const ResourceResponse & response)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 
didReceiveData(ResourceHandle *,const char *,int,int)115 void BlobResourceSynchronousLoader::didReceiveData(ResourceHandle*, const char*, int, int)
116 {
117 }
118 
didFinishLoading(ResourceHandle *,double)119 void BlobResourceSynchronousLoader::didFinishLoading(ResourceHandle*, double)
120 {
121 }
122 
didFail(ResourceHandle *,const ResourceError & error)123 void BlobResourceSynchronousLoader::didFail(ResourceHandle*, const ResourceError& error)
124 {
125     m_error = error;
126 }
127 
128 }
129 
130 ///////////////////////////////////////////////////////////////////////////////
131 // BlobResourceHandle
132 
133 // static
loadResourceSynchronously(PassRefPtr<BlobStorageData> blobData,const ResourceRequest & request,ResourceError & error,ResourceResponse & response,Vector<char> & data)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 
BlobResourceHandle(PassRefPtr<BlobStorageData> blobData,const ResourceRequest & request,ResourceHandleClient * client,bool async)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 
~BlobResourceHandle()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 
cancel()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 
delayedStartBlobResourceHandle(void * context)187 void delayedStartBlobResourceHandle(void* context)
188 {
189     RefPtr<BlobResourceHandle> handle = adoptRef(static_cast<BlobResourceHandle*>(context));
190     handle->doStart();
191 }
192 
start()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 
doStart()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 
getSizeForNext()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 
didGetSize(long long size)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 
seek()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 
readSync(char * buf,int length)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 
readDataSync(const BlobDataItem & item,char * buf,int length)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 
readFileSync(const BlobDataItem & item,char * buf,int length)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 
readAsync()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 
readDataAsync(const BlobDataItem & item)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 
readFileAsync(const BlobDataItem & item)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 
didOpen(bool success)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 
didRead(int bytesRead)486 void BlobResourceHandle::didRead(int bytesRead)
487 {
488     consumeData(m_buffer.data(), bytesRead);
489 }
490 
consumeData(const char * data,int bytesRead)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 
failed(int errorCode)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 
notifyResponse()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 
notifyResponseOnSuccess()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 
notifyResponseOnError()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 
notifyReceiveData(const char * data,int bytesRead)584 void BlobResourceHandle::notifyReceiveData(const char* data, int bytesRead)
585 {
586     if (client())
587         client()->didReceiveData(this, data, bytesRead, bytesRead);
588 }
589 
notifyFail(int errorCode)590 void BlobResourceHandle::notifyFail(int errorCode)
591 {
592     if (client())
593         client()->didFail(this, ResourceError(String(), errorCode, firstRequest().url(), String()));
594 }
595 
doNotifyFinish(void * context)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 
notifyFinish()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