OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "webkit/glue/media/buffered_resource_loader.h" |
| 6 |
| 7 #include "base/format_macros.h" |
| 8 #include "base/string_util.h" |
| 9 #include "net/base/net_errors.h" |
| 10 #include "third_party/WebKit/WebKit/chromium/public/WebKit.h" |
| 11 #include "third_party/WebKit/WebKit/chromium/public/WebKitClient.h" |
| 12 #include "third_party/WebKit/WebKit/chromium/public/WebString.h" |
| 13 #include "third_party/WebKit/WebKit/chromium/public/WebURLError.h" |
| 14 #include "webkit/glue/multipart_response_delegate.h" |
| 15 #include "webkit/glue/webkit_glue.h" |
| 16 |
| 17 using WebKit::WebFrame; |
| 18 using WebKit::WebString; |
| 19 using WebKit::WebURLError; |
| 20 using WebKit::WebURLLoader; |
| 21 using WebKit::WebURLRequest; |
| 22 using WebKit::WebURLResponse; |
| 23 using webkit_glue::MultipartResponseDelegate; |
| 24 |
| 25 namespace { |
| 26 |
| 27 const char kHttpScheme[] = "http"; |
| 28 const char kHttpsScheme[] = "https"; |
| 29 const char kDataScheme[] = "data"; |
| 30 |
| 31 const int64 kPositionNotSpecified = -1; |
| 32 const int kHttpOK = 200; |
| 33 const int kHttpPartialContent = 206; |
| 34 |
| 35 // Define the number of bytes in a megabyte. |
| 36 const size_t kMegabyte = 1024 * 1024; |
| 37 |
| 38 // Backward capacity of the buffer, by default 2MB. |
| 39 const size_t kBackwardCapcity = 2 * kMegabyte; |
| 40 |
| 41 // Forward capacity of the buffer, by default 10MB. |
| 42 const size_t kForwardCapacity = 10 * kMegabyte; |
| 43 |
| 44 // The threshold of bytes that we should wait until the data arrives in the |
| 45 // future instead of restarting a new connection. This number is defined in the |
| 46 // number of bytes, we should determine this value from typical connection speed |
| 47 // and amount of time for a suitable wait. Now I just make a guess for this |
| 48 // number to be 2MB. |
| 49 // TODO(hclam): determine a better value for this. |
| 50 const int kForwardWaitThreshold = 2 * kMegabyte; |
| 51 |
| 52 // Returns true if |url| operates on HTTP protocol. |
| 53 static bool IsHttpProtocol(const GURL& url) { |
| 54 return url.SchemeIs(kHttpScheme) || url.SchemeIs(kHttpsScheme); |
| 55 } |
| 56 |
| 57 } // namespace |
| 58 |
| 59 namespace webkit_glue { |
| 60 |
| 61 BufferedResourceLoader::BufferedResourceLoader( |
| 62 const GURL& url, |
| 63 int64 first_byte_position, |
| 64 int64 last_byte_position) |
| 65 : buffer_(new media::SeekableBuffer(kBackwardCapcity, kForwardCapacity)), |
| 66 deferred_(false), |
| 67 defer_allowed_(true), |
| 68 completed_(false), |
| 69 range_requested_(false), |
| 70 partial_response_(false), |
| 71 url_(url), |
| 72 first_byte_position_(first_byte_position), |
| 73 last_byte_position_(last_byte_position), |
| 74 start_callback_(NULL), |
| 75 offset_(0), |
| 76 content_length_(kPositionNotSpecified), |
| 77 instance_size_(kPositionNotSpecified), |
| 78 read_callback_(NULL), |
| 79 read_position_(0), |
| 80 read_size_(0), |
| 81 read_buffer_(NULL), |
| 82 first_offset_(0), |
| 83 last_offset_(0), |
| 84 keep_test_loader_(false) { |
| 85 } |
| 86 |
| 87 BufferedResourceLoader::~BufferedResourceLoader() { |
| 88 if (!completed_ && url_loader_.get()) |
| 89 url_loader_->cancel(); |
| 90 } |
| 91 |
| 92 void BufferedResourceLoader::Start(net::CompletionCallback* start_callback, |
| 93 NetworkEventCallback* event_callback, |
| 94 WebFrame* frame) { |
| 95 // Make sure we have not started. |
| 96 DCHECK(!start_callback_.get()); |
| 97 DCHECK(!event_callback_.get()); |
| 98 DCHECK(start_callback); |
| 99 DCHECK(event_callback); |
| 100 CHECK(frame); |
| 101 |
| 102 start_callback_.reset(start_callback); |
| 103 event_callback_.reset(event_callback); |
| 104 |
| 105 if (first_byte_position_ != kPositionNotSpecified) { |
| 106 range_requested_ = true; |
| 107 // TODO(hclam): server may not support range request so |offset_| may not |
| 108 // equal to |first_byte_position_|. |
| 109 offset_ = first_byte_position_; |
| 110 } |
| 111 |
| 112 // Increment the reference count right before we start the request. This |
| 113 // reference will be release when this request has ended. |
| 114 AddRef(); |
| 115 |
| 116 // Prepare the request. |
| 117 WebURLRequest request(url_); |
| 118 request.setHTTPHeaderField(WebString::fromUTF8("Range"), |
| 119 WebString::fromUTF8(GenerateHeaders( |
| 120 first_byte_position_, |
| 121 last_byte_position_))); |
| 122 frame->setReferrerForRequest(request, WebKit::WebURL()); |
| 123 |
| 124 // This flag is for unittests as we don't want to reset |url_loader| |
| 125 if (!keep_test_loader_) |
| 126 url_loader_.reset(frame->createAssociatedURLLoader()); |
| 127 |
| 128 // Start the resource loading. |
| 129 url_loader_->loadAsynchronously(request, this); |
| 130 } |
| 131 |
| 132 void BufferedResourceLoader::Stop() { |
| 133 // Reset callbacks. |
| 134 start_callback_.reset(); |
| 135 event_callback_.reset(); |
| 136 read_callback_.reset(); |
| 137 |
| 138 // Use the internal buffer to signal that we have been stopped. |
| 139 // TODO(hclam): Not so pretty to do this. |
| 140 if (!buffer_.get()) |
| 141 return; |
| 142 |
| 143 // Destroy internal buffer. |
| 144 buffer_.reset(); |
| 145 |
| 146 if (url_loader_.get()) { |
| 147 if (deferred_) |
| 148 url_loader_->setDefersLoading(false); |
| 149 deferred_ = false; |
| 150 |
| 151 if (!completed_) { |
| 152 url_loader_->cancel(); |
| 153 completed_ = true; |
| 154 } |
| 155 } |
| 156 } |
| 157 |
| 158 void BufferedResourceLoader::Read(int64 position, |
| 159 int read_size, |
| 160 uint8* buffer, |
| 161 net::CompletionCallback* read_callback) { |
| 162 DCHECK(!read_callback_.get()); |
| 163 DCHECK(buffer_.get()); |
| 164 DCHECK(read_callback); |
| 165 DCHECK(buffer); |
| 166 |
| 167 // Save the parameter of reading. |
| 168 read_callback_.reset(read_callback); |
| 169 read_position_ = position; |
| 170 read_size_ = read_size; |
| 171 read_buffer_ = buffer; |
| 172 |
| 173 // If read position is beyond the instance size, we cannot read there. |
| 174 if (instance_size_ != kPositionNotSpecified && |
| 175 instance_size_ <= read_position_) { |
| 176 DoneRead(0); |
| 177 return; |
| 178 } |
| 179 |
| 180 // Make sure |offset_| and |read_position_| does not differ by a large |
| 181 // amount. |
| 182 if (read_position_ > offset_ + kint32max || |
| 183 read_position_ < offset_ + kint32min) { |
| 184 DoneRead(net::ERR_CACHE_MISS); |
| 185 return; |
| 186 } |
| 187 |
| 188 // Prepare the parameters. |
| 189 first_offset_ = static_cast<int>(read_position_ - offset_); |
| 190 last_offset_ = first_offset_ + read_size_; |
| 191 |
| 192 // If we can serve the request now, do the actual read. |
| 193 if (CanFulfillRead()) { |
| 194 ReadInternal(); |
| 195 DisableDeferIfNeeded(); |
| 196 return; |
| 197 } |
| 198 |
| 199 // If we expected the read request to be fulfilled later, returns |
| 200 // immediately and let more data to flow in. |
| 201 if (WillFulfillRead()) |
| 202 return; |
| 203 |
| 204 // Make a callback to report failure. |
| 205 DoneRead(net::ERR_CACHE_MISS); |
| 206 } |
| 207 |
| 208 int64 BufferedResourceLoader::GetBufferedFirstBytePosition() { |
| 209 if (buffer_.get()) |
| 210 return offset_ - static_cast<int>(buffer_->backward_bytes()); |
| 211 return kPositionNotSpecified; |
| 212 } |
| 213 |
| 214 int64 BufferedResourceLoader::GetBufferedLastBytePosition() { |
| 215 if (buffer_.get()) |
| 216 return offset_ + static_cast<int>(buffer_->forward_bytes()) - 1; |
| 217 return kPositionNotSpecified; |
| 218 } |
| 219 |
| 220 void BufferedResourceLoader::SetAllowDefer(bool is_allowed) { |
| 221 defer_allowed_ = is_allowed; |
| 222 DisableDeferIfNeeded(); |
| 223 } |
| 224 |
| 225 int64 BufferedResourceLoader::content_length() { |
| 226 return content_length_; |
| 227 } |
| 228 |
| 229 int64 BufferedResourceLoader::instance_size() { |
| 230 return instance_size_; |
| 231 } |
| 232 |
| 233 bool BufferedResourceLoader::partial_response() { |
| 234 return partial_response_; |
| 235 } |
| 236 |
| 237 bool BufferedResourceLoader::network_activity() { |
| 238 return !completed_ && !deferred_; |
| 239 } |
| 240 |
| 241 const GURL& BufferedResourceLoader::url() { |
| 242 return url_; |
| 243 } |
| 244 |
| 245 void BufferedResourceLoader::SetURLLoaderForTest(WebURLLoader* mock_loader) { |
| 246 url_loader_.reset(mock_loader); |
| 247 keep_test_loader_ = true; |
| 248 } |
| 249 |
| 250 // WebKit::WebURLLoaderClient implementations. |
| 251 void BufferedResourceLoader::willSendRequest( |
| 252 WebURLLoader* loader, |
| 253 WebURLRequest& newRequest, |
| 254 const WebURLResponse& redirectResponse) { |
| 255 |
| 256 // The load may have been stopped and |start_callback| is destroyed. |
| 257 // In this case we shouldn't do anything. |
| 258 if (!start_callback_.get()) { |
| 259 // Set the url in the request to an invalid value (empty url). |
| 260 newRequest.setURL(WebKit::WebURL()); |
| 261 return; |
| 262 } |
| 263 |
| 264 if (!IsProtocolSupportedForMedia(newRequest.url())) { |
| 265 // Set the url in the request to an invalid value (empty url). |
| 266 newRequest.setURL(WebKit::WebURL()); |
| 267 DoneStart(net::ERR_ADDRESS_INVALID); |
| 268 Stop(); |
| 269 return; |
| 270 } |
| 271 |
| 272 url_ = newRequest.url(); |
| 273 } |
| 274 |
| 275 void BufferedResourceLoader::didSendData( |
| 276 WebURLLoader* loader, |
| 277 unsigned long long bytes_sent, |
| 278 unsigned long long total_bytes_to_be_sent) { |
| 279 NOTIMPLEMENTED(); |
| 280 } |
| 281 |
| 282 void BufferedResourceLoader::didReceiveResponse( |
| 283 WebURLLoader* loader, |
| 284 const WebURLResponse& response) { |
| 285 |
| 286 // The loader may have been stopped and |start_callback| is destroyed. |
| 287 // In this case we shouldn't do anything. |
| 288 if (!start_callback_.get()) |
| 289 return; |
| 290 |
| 291 // We make a strong assumption that when we reach here we have either |
| 292 // received a response from HTTP/HTTPS protocol or the request was |
| 293 // successful (in particular range request). So we only verify the partial |
| 294 // response for HTTP and HTTPS protocol. |
| 295 if (IsHttpProtocol(url_)) { |
| 296 int error = net::OK; |
| 297 |
| 298 if (response.httpStatusCode() == kHttpPartialContent) |
| 299 partial_response_ = true; |
| 300 |
| 301 if (range_requested_ && partial_response_) { |
| 302 // If we have verified the partial response and it is correct, we will |
| 303 // return net::OK. |
| 304 if (!VerifyPartialResponse(response)) |
| 305 error = net::ERR_INVALID_RESPONSE; |
| 306 } else if (response.httpStatusCode() != kHttpOK) { |
| 307 // We didn't request a range but server didn't reply with "200 OK". |
| 308 error = net::ERR_FAILED; |
| 309 } |
| 310 |
| 311 if (error != net::OK) { |
| 312 DoneStart(error); |
| 313 Stop(); |
| 314 return; |
| 315 } |
| 316 } else { |
| 317 // For any protocol other than HTTP and HTTPS, assume range request is |
| 318 // always fulfilled. |
| 319 partial_response_ = range_requested_; |
| 320 } |
| 321 |
| 322 // Expected content length can be -1, in that case |content_length_| is |
| 323 // not specified and this is a streaming response. |
| 324 content_length_ = response.expectedContentLength(); |
| 325 |
| 326 // If we have not requested a range, then the size of the instance is equal |
| 327 // to the content length. |
| 328 if (!partial_response_) |
| 329 instance_size_ = content_length_; |
| 330 |
| 331 // Calls with a successful response. |
| 332 DoneStart(net::OK); |
| 333 } |
| 334 |
| 335 void BufferedResourceLoader::didReceiveData( |
| 336 WebURLLoader* loader, |
| 337 const char* data, |
| 338 int data_length) { |
| 339 DCHECK(!completed_); |
| 340 DCHECK_GT(data_length, 0); |
| 341 |
| 342 // If this loader has been stopped, |buffer_| would be destroyed. |
| 343 // In this case we shouldn't do anything. |
| 344 if (!buffer_.get()) |
| 345 return; |
| 346 |
| 347 // Writes more data to |buffer_|. |
| 348 buffer_->Append(reinterpret_cast<const uint8*>(data), data_length); |
| 349 |
| 350 // If there is an active read request, try to fulfill the request. |
| 351 if (HasPendingRead() && CanFulfillRead()) { |
| 352 ReadInternal(); |
| 353 } else if (!defer_allowed_) { |
| 354 // If we're not allowed to defer, slide the buffer window forward instead |
| 355 // of deferring. |
| 356 if (buffer_->forward_bytes() > buffer_->forward_capacity()) { |
| 357 size_t excess = buffer_->forward_bytes() - buffer_->forward_capacity(); |
| 358 bool success = buffer_->Seek(excess); |
| 359 DCHECK(success); |
| 360 offset_ += first_offset_ + excess; |
| 361 } |
| 362 } |
| 363 |
| 364 // At last see if the buffer is full and we need to defer the downloading. |
| 365 EnableDeferIfNeeded(); |
| 366 |
| 367 // Notify that we have received some data. |
| 368 NotifyNetworkEvent(); |
| 369 } |
| 370 |
| 371 void BufferedResourceLoader::didDownloadData( |
| 372 WebKit::WebURLLoader* loader, |
| 373 int dataLength) { |
| 374 NOTIMPLEMENTED(); |
| 375 } |
| 376 |
| 377 void BufferedResourceLoader::didReceiveCachedMetadata( |
| 378 WebURLLoader* loader, |
| 379 const char* data, |
| 380 int data_length) { |
| 381 NOTIMPLEMENTED(); |
| 382 } |
| 383 |
| 384 void BufferedResourceLoader::didFinishLoading( |
| 385 WebURLLoader* loader, |
| 386 double finishTime) { |
| 387 DCHECK(!completed_); |
| 388 completed_ = true; |
| 389 |
| 390 // If there is a start callback, calls it. |
| 391 if (start_callback_.get()) { |
| 392 DoneStart(net::OK); |
| 393 } |
| 394 |
| 395 // If there is a pending read but the request has ended, returns with what |
| 396 // we have. |
| 397 if (HasPendingRead()) { |
| 398 // Make sure we have a valid buffer before we satisfy a read request. |
| 399 DCHECK(buffer_.get()); |
| 400 |
| 401 // Try to fulfill with what is in the buffer. |
| 402 if (CanFulfillRead()) |
| 403 ReadInternal(); |
| 404 else |
| 405 DoneRead(net::ERR_CACHE_MISS); |
| 406 } |
| 407 |
| 408 // There must not be any outstanding read request. |
| 409 DCHECK(!HasPendingRead()); |
| 410 |
| 411 // Notify that network response is completed. |
| 412 NotifyNetworkEvent(); |
| 413 |
| 414 url_loader_.reset(); |
| 415 Release(); |
| 416 } |
| 417 |
| 418 void BufferedResourceLoader::didFail( |
| 419 WebURLLoader* loader, |
| 420 const WebURLError& error) { |
| 421 DCHECK(!completed_); |
| 422 completed_ = true; |
| 423 |
| 424 // If there is a start callback, calls it. |
| 425 if (start_callback_.get()) { |
| 426 DoneStart(error.reason); |
| 427 } |
| 428 |
| 429 // If there is a pending read but the request failed, return with the |
| 430 // reason for the error. |
| 431 if (HasPendingRead()) { |
| 432 DoneRead(error.reason); |
| 433 } |
| 434 |
| 435 // Notify that network response is completed. |
| 436 NotifyNetworkEvent(); |
| 437 |
| 438 url_loader_.reset(); |
| 439 Release(); |
| 440 } |
| 441 |
| 442 void BufferedResourceLoader::EnableDeferIfNeeded() { |
| 443 if (!defer_allowed_) |
| 444 return; |
| 445 |
| 446 if (!deferred_ && |
| 447 buffer_->forward_bytes() >= buffer_->forward_capacity()) { |
| 448 deferred_ = true; |
| 449 |
| 450 if (url_loader_.get()) |
| 451 url_loader_->setDefersLoading(true); |
| 452 |
| 453 NotifyNetworkEvent(); |
| 454 } |
| 455 } |
| 456 |
| 457 void BufferedResourceLoader::DisableDeferIfNeeded() { |
| 458 if (deferred_ && |
| 459 (!defer_allowed_ || |
| 460 buffer_->forward_bytes() < buffer_->forward_capacity() / 2)) { |
| 461 deferred_ = false; |
| 462 |
| 463 if (url_loader_.get()) |
| 464 url_loader_->setDefersLoading(false); |
| 465 |
| 466 NotifyNetworkEvent(); |
| 467 } |
| 468 } |
| 469 |
| 470 bool BufferedResourceLoader::CanFulfillRead() { |
| 471 // If we are reading too far in the backward direction. |
| 472 if (first_offset_ < 0 && |
| 473 first_offset_ + static_cast<int>(buffer_->backward_bytes()) < 0) |
| 474 return false; |
| 475 |
| 476 // If the start offset is too far ahead. |
| 477 if (first_offset_ >= static_cast<int>(buffer_->forward_bytes())) |
| 478 return false; |
| 479 |
| 480 // At the point, we verified that first byte requested is within the buffer. |
| 481 // If the request has completed, then just returns with what we have now. |
| 482 if (completed_) |
| 483 return true; |
| 484 |
| 485 // If the resource request is still active, make sure the whole requested |
| 486 // range is covered. |
| 487 if (last_offset_ > static_cast<int>(buffer_->forward_bytes())) |
| 488 return false; |
| 489 |
| 490 return true; |
| 491 } |
| 492 |
| 493 bool BufferedResourceLoader::WillFulfillRead() { |
| 494 // Reading too far in the backward direction. |
| 495 if (first_offset_ < 0 && |
| 496 first_offset_ + static_cast<int>(buffer_->backward_bytes()) < 0) |
| 497 return false; |
| 498 |
| 499 // Try to read too far ahead. |
| 500 if (last_offset_ > kForwardWaitThreshold) |
| 501 return false; |
| 502 |
| 503 // The resource request has completed, there's no way we can fulfill the |
| 504 // read request. |
| 505 if (completed_) |
| 506 return false; |
| 507 |
| 508 return true; |
| 509 } |
| 510 |
| 511 void BufferedResourceLoader::ReadInternal() { |
| 512 // Seek to the first byte requested. |
| 513 bool ret = buffer_->Seek(first_offset_); |
| 514 DCHECK(ret); |
| 515 |
| 516 // Then do the read. |
| 517 int read = static_cast<int>(buffer_->Read(read_buffer_, read_size_)); |
| 518 offset_ += first_offset_ + read; |
| 519 |
| 520 // And report with what we have read. |
| 521 DoneRead(read); |
| 522 } |
| 523 |
| 524 bool BufferedResourceLoader::VerifyPartialResponse( |
| 525 const WebURLResponse& response) { |
| 526 int first_byte_position, last_byte_position, instance_size; |
| 527 |
| 528 if (!MultipartResponseDelegate::ReadContentRanges(response, |
| 529 &first_byte_position, |
| 530 &last_byte_position, |
| 531 &instance_size)) { |
| 532 return false; |
| 533 } |
| 534 |
| 535 if (instance_size != kPositionNotSpecified) { |
| 536 instance_size_ = instance_size; |
| 537 } |
| 538 |
| 539 if (first_byte_position_ != kPositionNotSpecified && |
| 540 first_byte_position_ != first_byte_position) { |
| 541 return false; |
| 542 } |
| 543 |
| 544 // TODO(hclam): I should also check |last_byte_position|, but since |
| 545 // we will never make such a request that it is ok to leave it unimplemented. |
| 546 return true; |
| 547 } |
| 548 |
| 549 std::string BufferedResourceLoader::GenerateHeaders( |
| 550 int64 first_byte_position, |
| 551 int64 last_byte_position) { |
| 552 // Construct the value for the range header. |
| 553 std::string header; |
| 554 if (first_byte_position > kPositionNotSpecified && |
| 555 last_byte_position > kPositionNotSpecified) { |
| 556 if (first_byte_position <= last_byte_position) { |
| 557 header = base::StringPrintf("bytes=%" PRId64 "-%" PRId64, |
| 558 first_byte_position, |
| 559 last_byte_position); |
| 560 } |
| 561 } else if (first_byte_position > kPositionNotSpecified) { |
| 562 header = base::StringPrintf("bytes=%" PRId64 "-", |
| 563 first_byte_position); |
| 564 } else if (last_byte_position > kPositionNotSpecified) { |
| 565 NOTIMPLEMENTED() << "Suffix range not implemented"; |
| 566 } |
| 567 return header; |
| 568 } |
| 569 |
| 570 void BufferedResourceLoader::DoneRead(int error) { |
| 571 read_callback_->RunWithParams(Tuple1<int>(error)); |
| 572 read_callback_.reset(); |
| 573 read_position_ = 0; |
| 574 read_size_ = 0; |
| 575 read_buffer_ = NULL; |
| 576 first_offset_ = 0; |
| 577 last_offset_ = 0; |
| 578 } |
| 579 |
| 580 void BufferedResourceLoader::DoneStart(int error) { |
| 581 start_callback_->RunWithParams(Tuple1<int>(error)); |
| 582 start_callback_.reset(); |
| 583 } |
| 584 |
| 585 void BufferedResourceLoader::NotifyNetworkEvent() { |
| 586 if (event_callback_.get()) |
| 587 event_callback_->Run(); |
| 588 } |
| 589 |
| 590 } // namespace webkit_glue |
OLD | NEW |