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