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