1 // Copyright 2013 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 "content/renderer/media/buffered_resource_loader.h" 6 7 #include "base/bits.h" 8 #include "base/callback_helpers.h" 9 #include "base/format_macros.h" 10 #include "base/metrics/histogram.h" 11 #include "base/strings/string_number_conversions.h" 12 #include "base/strings/string_util.h" 13 #include "base/strings/stringprintf.h" 14 #include "content/renderer/media/cache_util.h" 15 #include "media/base/media_log.h" 16 #include "net/http/http_request_headers.h" 17 #include "third_party/WebKit/public/platform/WebString.h" 18 #include "third_party/WebKit/public/platform/WebURLError.h" 19 #include "third_party/WebKit/public/platform/WebURLResponse.h" 20 #include "third_party/WebKit/public/web/WebKit.h" 21 #include "third_party/WebKit/public/web/WebURLLoaderOptions.h" 22 23 using WebKit::WebFrame; 24 using WebKit::WebString; 25 using WebKit::WebURLError; 26 using WebKit::WebURLLoader; 27 using WebKit::WebURLLoaderOptions; 28 using WebKit::WebURLRequest; 29 using WebKit::WebURLResponse; 30 31 namespace content { 32 33 static const int kHttpOK = 200; 34 static const int kHttpPartialContent = 206; 35 36 // Define the number of bytes in a megabyte. 37 static const int kMegabyte = 1024 * 1024; 38 39 // Minimum capacity of the buffer in forward or backward direction. 40 // 41 // 2MB is an arbitrary limit; it just seems to be "good enough" in practice. 42 static const int kMinBufferCapacity = 2 * kMegabyte; 43 44 // Maximum capacity of the buffer in forward or backward direction. This is 45 // effectively the largest single read the code path can handle. 46 // 20MB is an arbitrary limit; it just seems to be "good enough" in practice. 47 static const int kMaxBufferCapacity = 20 * kMegabyte; 48 49 // Maximum number of bytes outside the buffer we will wait for in order to 50 // fulfill a read. If a read starts more than 2MB away from the data we 51 // currently have in the buffer, we will not wait for buffer to reach the read's 52 // location and will instead reset the request. 53 static const int kForwardWaitThreshold = 2 * kMegabyte; 54 55 // Computes the suggested backward and forward capacity for the buffer 56 // if one wants to play at |playback_rate| * the natural playback speed. 57 // Use a value of 0 for |bitrate| if it is unknown. 58 static void ComputeTargetBufferWindow(float playback_rate, int bitrate, 59 int* out_backward_capacity, 60 int* out_forward_capacity) { 61 static const int kDefaultBitrate = 200 * 1024 * 8; // 200 Kbps. 62 static const int kMaxBitrate = 20 * kMegabyte * 8; // 20 Mbps. 63 static const float kMaxPlaybackRate = 25.0; 64 static const int kTargetSecondsBufferedAhead = 10; 65 static const int kTargetSecondsBufferedBehind = 2; 66 67 // Use a default bit rate if unknown and clamp to prevent overflow. 68 if (bitrate <= 0) 69 bitrate = kDefaultBitrate; 70 bitrate = std::min(bitrate, kMaxBitrate); 71 72 // Only scale the buffer window for playback rates greater than 1.0 in 73 // magnitude and clamp to prevent overflow. 74 bool backward_playback = false; 75 if (playback_rate < 0.0f) { 76 backward_playback = true; 77 playback_rate *= -1.0f; 78 } 79 80 playback_rate = std::max(playback_rate, 1.0f); 81 playback_rate = std::min(playback_rate, kMaxPlaybackRate); 82 83 int bytes_per_second = (bitrate / 8.0) * playback_rate; 84 85 // Clamp between kMinBufferCapacity and kMaxBufferCapacity. 86 *out_forward_capacity = std::max( 87 kTargetSecondsBufferedAhead * bytes_per_second, kMinBufferCapacity); 88 *out_backward_capacity = std::max( 89 kTargetSecondsBufferedBehind * bytes_per_second, kMinBufferCapacity); 90 91 *out_forward_capacity = std::min(*out_forward_capacity, kMaxBufferCapacity); 92 *out_backward_capacity = std::min(*out_backward_capacity, kMaxBufferCapacity); 93 94 if (backward_playback) 95 std::swap(*out_forward_capacity, *out_backward_capacity); 96 } 97 98 BufferedResourceLoader::BufferedResourceLoader( 99 const GURL& url, 100 CORSMode cors_mode, 101 int64 first_byte_position, 102 int64 last_byte_position, 103 DeferStrategy strategy, 104 int bitrate, 105 float playback_rate, 106 media::MediaLog* media_log) 107 : buffer_(kMinBufferCapacity, kMinBufferCapacity), 108 loader_failed_(false), 109 defer_strategy_(strategy), 110 might_be_reused_from_cache_in_future_(true), 111 range_supported_(false), 112 saved_forward_capacity_(0), 113 url_(url), 114 cors_mode_(cors_mode), 115 first_byte_position_(first_byte_position), 116 last_byte_position_(last_byte_position), 117 single_origin_(true), 118 offset_(0), 119 content_length_(kPositionNotSpecified), 120 instance_size_(kPositionNotSpecified), 121 read_position_(0), 122 read_size_(0), 123 read_buffer_(NULL), 124 first_offset_(0), 125 last_offset_(0), 126 bitrate_(bitrate), 127 playback_rate_(playback_rate), 128 media_log_(media_log) { 129 130 // Set the initial capacity of |buffer_| based on |bitrate_| and 131 // |playback_rate_|. 132 UpdateBufferWindow(); 133 } 134 135 BufferedResourceLoader::~BufferedResourceLoader() {} 136 137 void BufferedResourceLoader::Start( 138 const StartCB& start_cb, 139 const LoadingStateChangedCB& loading_cb, 140 const ProgressCB& progress_cb, 141 WebFrame* frame) { 142 // Make sure we have not started. 143 DCHECK(start_cb_.is_null()); 144 DCHECK(loading_cb_.is_null()); 145 DCHECK(progress_cb_.is_null()); 146 DCHECK(!start_cb.is_null()); 147 DCHECK(!loading_cb.is_null()); 148 DCHECK(!progress_cb.is_null()); 149 CHECK(frame); 150 151 start_cb_ = start_cb; 152 loading_cb_ = loading_cb; 153 progress_cb_ = progress_cb; 154 155 if (first_byte_position_ != kPositionNotSpecified) { 156 // TODO(hclam): server may not support range request so |offset_| may not 157 // equal to |first_byte_position_|. 158 offset_ = first_byte_position_; 159 } 160 161 // Prepare the request. 162 WebURLRequest request(url_); 163 request.setTargetType(WebURLRequest::TargetIsMedia); 164 165 if (IsRangeRequest()) { 166 request.setHTTPHeaderField( 167 WebString::fromUTF8(net::HttpRequestHeaders::kRange), 168 WebString::fromUTF8(GenerateHeaders(first_byte_position_, 169 last_byte_position_))); 170 } 171 172 frame->setReferrerForRequest(request, WebKit::WebURL()); 173 174 // Disable compression, compression for audio/video doesn't make sense... 175 request.setHTTPHeaderField( 176 WebString::fromUTF8(net::HttpRequestHeaders::kAcceptEncoding), 177 WebString::fromUTF8("identity;q=1, *;q=0")); 178 179 // Check for our test WebURLLoader. 180 scoped_ptr<WebURLLoader> loader; 181 if (test_loader_) { 182 loader = test_loader_.Pass(); 183 } else { 184 WebURLLoaderOptions options; 185 if (cors_mode_ == kUnspecified) { 186 options.allowCredentials = true; 187 options.crossOriginRequestPolicy = 188 WebURLLoaderOptions::CrossOriginRequestPolicyAllow; 189 } else { 190 options.exposeAllResponseHeaders = true; 191 options.crossOriginRequestPolicy = 192 WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl; 193 if (cors_mode_ == kUseCredentials) 194 options.allowCredentials = true; 195 } 196 loader.reset(frame->createAssociatedURLLoader(options)); 197 } 198 199 // Start the resource loading. 200 loader->loadAsynchronously(request, this); 201 active_loader_.reset(new ActiveLoader(loader.Pass())); 202 loading_cb_.Run(kLoading); 203 } 204 205 void BufferedResourceLoader::Stop() { 206 // Reset callbacks. 207 start_cb_.Reset(); 208 loading_cb_.Reset(); 209 progress_cb_.Reset(); 210 read_cb_.Reset(); 211 212 // Cancel and reset any active loaders. 213 active_loader_.reset(); 214 } 215 216 void BufferedResourceLoader::Read( 217 int64 position, 218 int read_size, 219 uint8* buffer, 220 const ReadCB& read_cb) { 221 DCHECK(start_cb_.is_null()); 222 DCHECK(read_cb_.is_null()); 223 DCHECK(!read_cb.is_null()); 224 DCHECK(buffer); 225 DCHECK_GT(read_size, 0); 226 227 // Save the parameter of reading. 228 read_cb_ = read_cb; 229 read_position_ = position; 230 read_size_ = read_size; 231 read_buffer_ = buffer; 232 233 // Reads should immediately fail if the loader also failed. 234 if (loader_failed_) { 235 DoneRead(kFailed, 0); 236 return; 237 } 238 239 // If we're attempting to read past the end of the file, return a zero 240 // indicating EOF. 241 // 242 // This can happen with callees that read in fixed-sized amounts for parsing 243 // or at the end of chunked 200 responses when we discover the actual length 244 // of the file. 245 if (instance_size_ != kPositionNotSpecified && 246 instance_size_ <= read_position_) { 247 DVLOG(1) << "Appear to have seeked beyond EOS; returning 0."; 248 DoneRead(kOk, 0); 249 return; 250 } 251 252 // Make sure |offset_| and |read_position_| does not differ by a large 253 // amount. 254 if (read_position_ > offset_ + kint32max || 255 read_position_ < offset_ + kint32min) { 256 DoneRead(kCacheMiss, 0); 257 return; 258 } 259 260 // Make sure |read_size_| is not too large for the buffer to ever be able to 261 // fulfill the read request. 262 if (read_size_ > kMaxBufferCapacity) { 263 DoneRead(kFailed, 0); 264 return; 265 } 266 267 // Prepare the parameters. 268 first_offset_ = read_position_ - offset_; 269 last_offset_ = first_offset_ + read_size_; 270 271 // If we can serve the request now, do the actual read. 272 if (CanFulfillRead()) { 273 ReadInternal(); 274 UpdateDeferBehavior(); 275 return; 276 } 277 278 // If we expect the read request to be fulfilled later, expand capacity as 279 // necessary and disable deferring. 280 if (WillFulfillRead()) { 281 // Advance offset as much as possible to create additional capacity. 282 int advance = std::min(first_offset_, buffer_.forward_bytes()); 283 bool ret = buffer_.Seek(advance); 284 DCHECK(ret); 285 286 offset_ += advance; 287 first_offset_ -= advance; 288 last_offset_ -= advance; 289 290 // Expand capacity to accomodate a read that extends past the normal 291 // capacity. 292 // 293 // This can happen when reading in a large seek index or when the 294 // first byte of a read request falls within kForwardWaitThreshold. 295 if (last_offset_ > buffer_.forward_capacity()) { 296 saved_forward_capacity_ = buffer_.forward_capacity(); 297 buffer_.set_forward_capacity(last_offset_); 298 } 299 300 // Make sure we stop deferring now that there's additional capacity. 301 DCHECK(!ShouldDefer()) 302 << "Capacity was not adjusted properly to prevent deferring."; 303 UpdateDeferBehavior(); 304 305 return; 306 } 307 308 // Make a callback to report failure. 309 DoneRead(kCacheMiss, 0); 310 } 311 312 int64 BufferedResourceLoader::content_length() { 313 return content_length_; 314 } 315 316 int64 BufferedResourceLoader::instance_size() { 317 return instance_size_; 318 } 319 320 bool BufferedResourceLoader::range_supported() { 321 return range_supported_; 322 } 323 324 ///////////////////////////////////////////////////////////////////////////// 325 // WebKit::WebURLLoaderClient implementation. 326 void BufferedResourceLoader::willSendRequest( 327 WebURLLoader* loader, 328 WebURLRequest& newRequest, 329 const WebURLResponse& redirectResponse) { 330 331 // The load may have been stopped and |start_cb| is destroyed. 332 // In this case we shouldn't do anything. 333 if (start_cb_.is_null()) { 334 // Set the url in the request to an invalid value (empty url). 335 newRequest.setURL(WebKit::WebURL()); 336 return; 337 } 338 339 // Only allow |single_origin_| if we haven't seen a different origin yet. 340 if (single_origin_) 341 single_origin_ = url_.GetOrigin() == GURL(newRequest.url()).GetOrigin(); 342 343 url_ = newRequest.url(); 344 } 345 346 void BufferedResourceLoader::didSendData( 347 WebURLLoader* loader, 348 unsigned long long bytes_sent, 349 unsigned long long total_bytes_to_be_sent) { 350 NOTIMPLEMENTED(); 351 } 352 353 void BufferedResourceLoader::didReceiveResponse( 354 WebURLLoader* loader, 355 const WebURLResponse& response) { 356 DVLOG(1) << "didReceiveResponse: HTTP/" 357 << (response.httpVersion() == WebURLResponse::HTTP_0_9 ? "0.9" : 358 response.httpVersion() == WebURLResponse::HTTP_1_0 ? "1.0" : 359 response.httpVersion() == WebURLResponse::HTTP_1_1 ? "1.1" : 360 "Unknown") 361 << " " << response.httpStatusCode(); 362 DCHECK(active_loader_.get()); 363 364 // The loader may have been stopped and |start_cb| is destroyed. 365 // In this case we shouldn't do anything. 366 if (start_cb_.is_null()) 367 return; 368 369 uint32 reasons = GetReasonsForUncacheability(response); 370 might_be_reused_from_cache_in_future_ = reasons == 0; 371 UMA_HISTOGRAM_BOOLEAN("Media.CacheUseful", reasons == 0); 372 int shift = 0; 373 int max_enum = base::bits::Log2Ceiling(kMaxReason); 374 while (reasons) { 375 DCHECK_LT(shift, max_enum); // Sanity check. 376 if (reasons & 0x1) 377 UMA_HISTOGRAM_ENUMERATION("Media.UncacheableReason", shift, max_enum); 378 reasons >>= 1; 379 ++shift; 380 } 381 382 // Expected content length can be |kPositionNotSpecified|, in that case 383 // |content_length_| is not specified and this is a streaming response. 384 content_length_ = response.expectedContentLength(); 385 386 // We make a strong assumption that when we reach here we have either 387 // received a response from HTTP/HTTPS protocol or the request was 388 // successful (in particular range request). So we only verify the partial 389 // response for HTTP and HTTPS protocol. 390 if (url_.SchemeIs(kHttpScheme) || url_.SchemeIs(kHttpsScheme)) { 391 bool partial_response = (response.httpStatusCode() == kHttpPartialContent); 392 bool ok_response = (response.httpStatusCode() == kHttpOK); 393 394 if (IsRangeRequest()) { 395 // Check to see whether the server supports byte ranges. 396 std::string accept_ranges = 397 response.httpHeaderField("Accept-Ranges").utf8(); 398 range_supported_ = (accept_ranges.find("bytes") != std::string::npos); 399 400 // If we have verified the partial response and it is correct, we will 401 // return kOk. It's also possible for a server to support range requests 402 // without advertising "Accept-Ranges: bytes". 403 if (partial_response && VerifyPartialResponse(response)) { 404 range_supported_ = true; 405 } else if (ok_response && first_byte_position_ == 0 && 406 last_byte_position_ == kPositionNotSpecified) { 407 // We accept a 200 response for a Range:0- request, trusting the 408 // Accept-Ranges header, because Apache thinks that's a reasonable thing 409 // to return. 410 instance_size_ = content_length_; 411 } else { 412 DoneStart(kFailed); 413 return; 414 } 415 } else { 416 instance_size_ = content_length_; 417 if (response.httpStatusCode() != kHttpOK) { 418 // We didn't request a range but server didn't reply with "200 OK". 419 DoneStart(kFailed); 420 return; 421 } 422 } 423 424 } else { 425 CHECK_EQ(instance_size_, kPositionNotSpecified); 426 if (content_length_ != kPositionNotSpecified) { 427 if (first_byte_position_ == kPositionNotSpecified) 428 instance_size_ = content_length_; 429 else if (last_byte_position_ == kPositionNotSpecified) 430 instance_size_ = content_length_ + first_byte_position_; 431 } 432 } 433 434 // Calls with a successful response. 435 DoneStart(kOk); 436 } 437 438 void BufferedResourceLoader::didReceiveData( 439 WebURLLoader* loader, 440 const char* data, 441 int data_length, 442 int encoded_data_length) { 443 DVLOG(1) << "didReceiveData: " << data_length << " bytes"; 444 DCHECK(active_loader_.get()); 445 DCHECK_GT(data_length, 0); 446 447 buffer_.Append(reinterpret_cast<const uint8*>(data), data_length); 448 449 // If there is an active read request, try to fulfill the request. 450 if (HasPendingRead() && CanFulfillRead()) 451 ReadInternal(); 452 453 // At last see if the buffer is full and we need to defer the downloading. 454 UpdateDeferBehavior(); 455 456 // Consume excess bytes from our in-memory buffer if necessary. 457 if (buffer_.forward_bytes() > buffer_.forward_capacity()) { 458 int excess = buffer_.forward_bytes() - buffer_.forward_capacity(); 459 bool success = buffer_.Seek(excess); 460 DCHECK(success); 461 offset_ += first_offset_ + excess; 462 } 463 464 // Notify latest progress and buffered offset. 465 progress_cb_.Run(offset_ + buffer_.forward_bytes() - 1); 466 Log(); 467 } 468 469 void BufferedResourceLoader::didDownloadData( 470 WebKit::WebURLLoader* loader, 471 int dataLength) { 472 NOTIMPLEMENTED(); 473 } 474 475 void BufferedResourceLoader::didReceiveCachedMetadata( 476 WebURLLoader* loader, 477 const char* data, 478 int data_length) { 479 NOTIMPLEMENTED(); 480 } 481 482 void BufferedResourceLoader::didFinishLoading( 483 WebURLLoader* loader, 484 double finishTime) { 485 DVLOG(1) << "didFinishLoading"; 486 DCHECK(active_loader_.get()); 487 488 // We're done with the loader. 489 active_loader_.reset(); 490 loading_cb_.Run(kLoadingFinished); 491 492 // If we didn't know the |instance_size_| we do now. 493 if (instance_size_ == kPositionNotSpecified) { 494 instance_size_ = offset_ + buffer_.forward_bytes(); 495 } 496 497 // If there is a start callback, run it. 498 if (!start_cb_.is_null()) { 499 DCHECK(read_cb_.is_null()) 500 << "Shouldn't have a read callback during start"; 501 DoneStart(kOk); 502 return; 503 } 504 505 // Don't leave read callbacks hanging around. 506 if (HasPendingRead()) { 507 // Try to fulfill with what is in the buffer. 508 if (CanFulfillRead()) 509 ReadInternal(); 510 else 511 DoneRead(kCacheMiss, 0); 512 } 513 } 514 515 void BufferedResourceLoader::didFail( 516 WebURLLoader* loader, 517 const WebURLError& error) { 518 DVLOG(1) << "didFail: reason=" << error.reason 519 << ", isCancellation=" << error.isCancellation 520 << ", domain=" << error.domain.utf8().data() 521 << ", localizedDescription=" 522 << error.localizedDescription.utf8().data(); 523 DCHECK(active_loader_.get()); 524 525 // We don't need to continue loading after failure. 526 // 527 // Keep it alive until we exit this method so that |error| remains valid. 528 scoped_ptr<ActiveLoader> active_loader = active_loader_.Pass(); 529 loader_failed_ = true; 530 loading_cb_.Run(kLoadingFailed); 531 532 // Don't leave start callbacks hanging around. 533 if (!start_cb_.is_null()) { 534 DCHECK(read_cb_.is_null()) 535 << "Shouldn't have a read callback during start"; 536 DoneStart(kFailed); 537 return; 538 } 539 540 // Don't leave read callbacks hanging around. 541 if (HasPendingRead()) { 542 DoneRead(kFailed, 0); 543 } 544 } 545 546 bool BufferedResourceLoader::HasSingleOrigin() const { 547 DCHECK(start_cb_.is_null()) 548 << "Start() must complete before calling HasSingleOrigin()"; 549 return single_origin_; 550 } 551 552 bool BufferedResourceLoader::DidPassCORSAccessCheck() const { 553 DCHECK(start_cb_.is_null()) 554 << "Start() must complete before calling DidPassCORSAccessCheck()"; 555 return !loader_failed_ && cors_mode_ != kUnspecified; 556 } 557 558 void BufferedResourceLoader::UpdateDeferStrategy(DeferStrategy strategy) { 559 if (!might_be_reused_from_cache_in_future_ && strategy == kNeverDefer) 560 strategy = kCapacityDefer; 561 defer_strategy_ = strategy; 562 UpdateDeferBehavior(); 563 } 564 565 void BufferedResourceLoader::SetPlaybackRate(float playback_rate) { 566 playback_rate_ = playback_rate; 567 568 // This is a pause so don't bother updating the buffer window as we'll likely 569 // get unpaused in the future. 570 if (playback_rate_ == 0.0) 571 return; 572 573 UpdateBufferWindow(); 574 } 575 576 void BufferedResourceLoader::SetBitrate(int bitrate) { 577 DCHECK(bitrate >= 0); 578 bitrate_ = bitrate; 579 UpdateBufferWindow(); 580 } 581 582 ///////////////////////////////////////////////////////////////////////////// 583 // Helper methods. 584 585 void BufferedResourceLoader::UpdateBufferWindow() { 586 int backward_capacity; 587 int forward_capacity; 588 ComputeTargetBufferWindow( 589 playback_rate_, bitrate_, &backward_capacity, &forward_capacity); 590 591 // This does not evict data from the buffer if the new capacities are less 592 // than the current capacities; the new limits will be enforced after the 593 // existing excess buffered data is consumed. 594 buffer_.set_backward_capacity(backward_capacity); 595 buffer_.set_forward_capacity(forward_capacity); 596 } 597 598 void BufferedResourceLoader::UpdateDeferBehavior() { 599 if (!active_loader_) 600 return; 601 602 SetDeferred(ShouldDefer()); 603 } 604 605 void BufferedResourceLoader::SetDeferred(bool deferred) { 606 if (active_loader_->deferred() == deferred) 607 return; 608 609 active_loader_->SetDeferred(deferred); 610 loading_cb_.Run(deferred ? kLoadingDeferred : kLoading); 611 } 612 613 bool BufferedResourceLoader::ShouldDefer() const { 614 switch(defer_strategy_) { 615 case kNeverDefer: 616 return false; 617 618 case kReadThenDefer: 619 DCHECK(read_cb_.is_null() || last_offset_ > buffer_.forward_bytes()) 620 << "We shouldn't stop deferring if we can fulfill the read"; 621 return read_cb_.is_null(); 622 623 case kCapacityDefer: 624 return buffer_.forward_bytes() >= buffer_.forward_capacity(); 625 } 626 NOTREACHED(); 627 return false; 628 } 629 630 bool BufferedResourceLoader::CanFulfillRead() const { 631 // If we are reading too far in the backward direction. 632 if (first_offset_ < 0 && (first_offset_ + buffer_.backward_bytes()) < 0) 633 return false; 634 635 // If the start offset is too far ahead. 636 if (first_offset_ >= buffer_.forward_bytes()) 637 return false; 638 639 // At the point, we verified that first byte requested is within the buffer. 640 // If the request has completed, then just returns with what we have now. 641 if (!active_loader_) 642 return true; 643 644 // If the resource request is still active, make sure the whole requested 645 // range is covered. 646 if (last_offset_ > buffer_.forward_bytes()) 647 return false; 648 649 return true; 650 } 651 652 bool BufferedResourceLoader::WillFulfillRead() const { 653 // Trying to read too far behind. 654 if (first_offset_ < 0 && (first_offset_ + buffer_.backward_bytes()) < 0) 655 return false; 656 657 // Trying to read too far ahead. 658 if ((first_offset_ - buffer_.forward_bytes()) >= kForwardWaitThreshold) 659 return false; 660 661 // The resource request has completed, there's no way we can fulfill the 662 // read request. 663 if (!active_loader_) 664 return false; 665 666 return true; 667 } 668 669 void BufferedResourceLoader::ReadInternal() { 670 // Seek to the first byte requested. 671 bool ret = buffer_.Seek(first_offset_); 672 DCHECK(ret); 673 674 // Then do the read. 675 int read = buffer_.Read(read_buffer_, read_size_); 676 offset_ += first_offset_ + read; 677 678 // And report with what we have read. 679 DoneRead(kOk, read); 680 } 681 682 int64 BufferedResourceLoader::first_byte_position() const { 683 return first_byte_position_; 684 } 685 686 // static 687 bool BufferedResourceLoader::ParseContentRange( 688 const std::string& content_range_str, int64* first_byte_position, 689 int64* last_byte_position, int64* instance_size) { 690 const std::string kUpThroughBytesUnit = "bytes "; 691 if (content_range_str.find(kUpThroughBytesUnit) != 0) 692 return false; 693 std::string range_spec = 694 content_range_str.substr(kUpThroughBytesUnit.length()); 695 size_t dash_offset = range_spec.find("-"); 696 size_t slash_offset = range_spec.find("/"); 697 698 if (dash_offset == std::string::npos || slash_offset == std::string::npos || 699 slash_offset < dash_offset || slash_offset + 1 == range_spec.length()) { 700 return false; 701 } 702 if (!base::StringToInt64(range_spec.substr(0, dash_offset), 703 first_byte_position) || 704 !base::StringToInt64(range_spec.substr(dash_offset + 1, 705 slash_offset - dash_offset - 1), 706 last_byte_position)) { 707 return false; 708 } 709 if (slash_offset == range_spec.length() - 2 && 710 range_spec[slash_offset + 1] == '*') { 711 *instance_size = kPositionNotSpecified; 712 } else { 713 if (!base::StringToInt64(range_spec.substr(slash_offset + 1), 714 instance_size)) { 715 return false; 716 } 717 } 718 if (*last_byte_position < *first_byte_position || 719 (*instance_size != kPositionNotSpecified && 720 *last_byte_position >= *instance_size)) { 721 return false; 722 } 723 724 return true; 725 } 726 727 bool BufferedResourceLoader::VerifyPartialResponse( 728 const WebURLResponse& response) { 729 int64 first_byte_position, last_byte_position, instance_size; 730 if (!ParseContentRange(response.httpHeaderField("Content-Range").utf8(), 731 &first_byte_position, &last_byte_position, 732 &instance_size)) { 733 return false; 734 } 735 736 if (instance_size != kPositionNotSpecified) { 737 instance_size_ = instance_size; 738 } 739 740 if (first_byte_position_ != kPositionNotSpecified && 741 first_byte_position_ != first_byte_position) { 742 return false; 743 } 744 745 // TODO(hclam): I should also check |last_byte_position|, but since 746 // we will never make such a request that it is ok to leave it unimplemented. 747 return true; 748 } 749 750 std::string BufferedResourceLoader::GenerateHeaders( 751 int64 first_byte_position, 752 int64 last_byte_position) { 753 // Construct the value for the range header. 754 std::string header; 755 if (first_byte_position > kPositionNotSpecified && 756 last_byte_position > kPositionNotSpecified) { 757 if (first_byte_position <= last_byte_position) { 758 header = base::StringPrintf("bytes=%" PRId64 "-%" PRId64, 759 first_byte_position, 760 last_byte_position); 761 } 762 } else if (first_byte_position > kPositionNotSpecified) { 763 header = base::StringPrintf("bytes=%" PRId64 "-", 764 first_byte_position); 765 } else if (last_byte_position > kPositionNotSpecified) { 766 NOTIMPLEMENTED() << "Suffix range not implemented"; 767 } 768 return header; 769 } 770 771 void BufferedResourceLoader::DoneRead(Status status, int bytes_read) { 772 if (saved_forward_capacity_) { 773 buffer_.set_forward_capacity(saved_forward_capacity_); 774 saved_forward_capacity_ = 0; 775 } 776 read_position_ = 0; 777 read_size_ = 0; 778 read_buffer_ = NULL; 779 first_offset_ = 0; 780 last_offset_ = 0; 781 Log(); 782 783 base::ResetAndReturn(&read_cb_).Run(status, bytes_read); 784 } 785 786 787 void BufferedResourceLoader::DoneStart(Status status) { 788 base::ResetAndReturn(&start_cb_).Run(status); 789 } 790 791 bool BufferedResourceLoader::IsRangeRequest() const { 792 return first_byte_position_ != kPositionNotSpecified; 793 } 794 795 void BufferedResourceLoader::Log() { 796 media_log_->AddEvent( 797 media_log_->CreateBufferedExtentsChangedEvent( 798 offset_ - buffer_.backward_bytes(), 799 offset_, 800 offset_ + buffer_.forward_bytes())); 801 } 802 803 } // namespace content 804