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