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/metrics/histogram.h" 10#include "base/strings/string_number_conversions.h" 11#include "base/strings/string_util.h" 12#include "content/public/common/url_constants.h" 13#include "content/renderer/media/cache_util.h" 14#include "media/base/media_log.h" 15#include "net/http/http_byte_range.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 23using blink::WebFrame; 24using blink::WebString; 25using blink::WebURLError; 26using blink::WebURLLoader; 27using blink::WebURLLoaderOptions; 28using blink::WebURLRequest; 29using blink::WebURLResponse; 30 31namespace content { 32 33static const int kHttpOK = 200; 34static const int kHttpPartialContent = 206; 35 36// Define the number of bytes in a megabyte. 37static 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. 42static 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. 47static 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. 53static 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. 58static 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 98BufferedResourceLoader::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 135BufferedResourceLoader::~BufferedResourceLoader() {} 136 137void 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(net::HttpByteRange::Bounded( 169 first_byte_position_, last_byte_position_).GetHeaderValue())); 170 } 171 172 frame->setReferrerForRequest(request, blink::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 // The author header set is empty, no preflight should go ahead. 192 options.preflightPolicy = WebURLLoaderOptions::PreventPreflight; 193 options.crossOriginRequestPolicy = 194 WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl; 195 if (cors_mode_ == kUseCredentials) 196 options.allowCredentials = true; 197 } 198 loader.reset(frame->createAssociatedURLLoader(options)); 199 } 200 201 // Start the resource loading. 202 loader->loadAsynchronously(request, this); 203 active_loader_.reset(new ActiveLoader(loader.Pass())); 204 loading_cb_.Run(kLoading); 205} 206 207void BufferedResourceLoader::Stop() { 208 // Reset callbacks. 209 start_cb_.Reset(); 210 loading_cb_.Reset(); 211 progress_cb_.Reset(); 212 read_cb_.Reset(); 213 214 // Cancel and reset any active loaders. 215 active_loader_.reset(); 216} 217 218void BufferedResourceLoader::Read( 219 int64 position, 220 int read_size, 221 uint8* buffer, 222 const ReadCB& read_cb) { 223 DCHECK(start_cb_.is_null()); 224 DCHECK(read_cb_.is_null()); 225 DCHECK(!read_cb.is_null()); 226 DCHECK(buffer); 227 DCHECK_GT(read_size, 0); 228 229 // Save the parameter of reading. 230 read_cb_ = read_cb; 231 read_position_ = position; 232 read_size_ = read_size; 233 read_buffer_ = buffer; 234 235 // Reads should immediately fail if the loader also failed. 236 if (loader_failed_) { 237 DoneRead(kFailed, 0); 238 return; 239 } 240 241 // If we're attempting to read past the end of the file, return a zero 242 // indicating EOF. 243 // 244 // This can happen with callees that read in fixed-sized amounts for parsing 245 // or at the end of chunked 200 responses when we discover the actual length 246 // of the file. 247 if (instance_size_ != kPositionNotSpecified && 248 instance_size_ <= read_position_) { 249 DVLOG(1) << "Appear to have seeked beyond EOS; returning 0."; 250 DoneRead(kOk, 0); 251 return; 252 } 253 254 // Make sure |offset_| and |read_position_| does not differ by a large 255 // amount. 256 if (read_position_ > offset_ + kint32max || 257 read_position_ < offset_ + kint32min) { 258 DoneRead(kCacheMiss, 0); 259 return; 260 } 261 262 // Make sure |read_size_| is not too large for the buffer to ever be able to 263 // fulfill the read request. 264 if (read_size_ > kMaxBufferCapacity) { 265 DoneRead(kFailed, 0); 266 return; 267 } 268 269 // Prepare the parameters. 270 first_offset_ = read_position_ - offset_; 271 last_offset_ = first_offset_ + read_size_; 272 273 // If we can serve the request now, do the actual read. 274 if (CanFulfillRead()) { 275 ReadInternal(); 276 UpdateDeferBehavior(); 277 return; 278 } 279 280 // If we expect the read request to be fulfilled later, expand capacity as 281 // necessary and disable deferring. 282 if (WillFulfillRead()) { 283 // Advance offset as much as possible to create additional capacity. 284 int advance = std::min(first_offset_, buffer_.forward_bytes()); 285 bool ret = buffer_.Seek(advance); 286 DCHECK(ret); 287 288 offset_ += advance; 289 first_offset_ -= advance; 290 last_offset_ -= advance; 291 292 // Expand capacity to accomodate a read that extends past the normal 293 // capacity. 294 // 295 // This can happen when reading in a large seek index or when the 296 // first byte of a read request falls within kForwardWaitThreshold. 297 if (last_offset_ > buffer_.forward_capacity()) { 298 saved_forward_capacity_ = buffer_.forward_capacity(); 299 buffer_.set_forward_capacity(last_offset_); 300 } 301 302 // Make sure we stop deferring now that there's additional capacity. 303 DCHECK(!ShouldDefer()) 304 << "Capacity was not adjusted properly to prevent deferring."; 305 UpdateDeferBehavior(); 306 307 return; 308 } 309 310 // Make a callback to report failure. 311 DoneRead(kCacheMiss, 0); 312} 313 314int64 BufferedResourceLoader::content_length() { 315 return content_length_; 316} 317 318int64 BufferedResourceLoader::instance_size() { 319 return instance_size_; 320} 321 322bool BufferedResourceLoader::range_supported() { 323 return range_supported_; 324} 325 326///////////////////////////////////////////////////////////////////////////// 327// blink::WebURLLoaderClient implementation. 328void BufferedResourceLoader::willSendRequest( 329 WebURLLoader* loader, 330 WebURLRequest& newRequest, 331 const WebURLResponse& redirectResponse) { 332 333 // The load may have been stopped and |start_cb| is destroyed. 334 // In this case we shouldn't do anything. 335 if (start_cb_.is_null()) { 336 // Set the url in the request to an invalid value (empty url). 337 newRequest.setURL(blink::WebURL()); 338 return; 339 } 340 341 // Only allow |single_origin_| if we haven't seen a different origin yet. 342 if (single_origin_) 343 single_origin_ = url_.GetOrigin() == GURL(newRequest.url()).GetOrigin(); 344 345 url_ = newRequest.url(); 346} 347 348void BufferedResourceLoader::didSendData( 349 WebURLLoader* loader, 350 unsigned long long bytes_sent, 351 unsigned long long total_bytes_to_be_sent) { 352 NOTIMPLEMENTED(); 353} 354 355void BufferedResourceLoader::didReceiveResponse( 356 WebURLLoader* loader, 357 const WebURLResponse& response) { 358 DVLOG(1) << "didReceiveResponse: HTTP/" 359 << (response.httpVersion() == WebURLResponse::HTTP_0_9 ? "0.9" : 360 response.httpVersion() == WebURLResponse::HTTP_1_0 ? "1.0" : 361 response.httpVersion() == WebURLResponse::HTTP_1_1 ? "1.1" : 362 "Unknown") 363 << " " << response.httpStatusCode(); 364 DCHECK(active_loader_.get()); 365 366 // The loader may have been stopped and |start_cb| is destroyed. 367 // In this case we shouldn't do anything. 368 if (start_cb_.is_null()) 369 return; 370 371 uint32 reasons = GetReasonsForUncacheability(response); 372 might_be_reused_from_cache_in_future_ = reasons == 0; 373 UMA_HISTOGRAM_BOOLEAN("Media.CacheUseful", reasons == 0); 374 int shift = 0; 375 int max_enum = base::bits::Log2Ceiling(kMaxReason); 376 while (reasons) { 377 DCHECK_LT(shift, max_enum); // Sanity check. 378 if (reasons & 0x1) 379 UMA_HISTOGRAM_ENUMERATION("Media.UncacheableReason", shift, max_enum); 380 reasons >>= 1; 381 ++shift; 382 } 383 384 // Expected content length can be |kPositionNotSpecified|, in that case 385 // |content_length_| is not specified and this is a streaming response. 386 content_length_ = response.expectedContentLength(); 387 388 // We make a strong assumption that when we reach here we have either 389 // received a response from HTTP/HTTPS protocol or the request was 390 // successful (in particular range request). So we only verify the partial 391 // response for HTTP and HTTPS protocol. 392 if (url_.SchemeIsHTTPOrHTTPS()) { 393 bool partial_response = (response.httpStatusCode() == kHttpPartialContent); 394 bool ok_response = (response.httpStatusCode() == kHttpOK); 395 396 if (IsRangeRequest()) { 397 // Check to see whether the server supports byte ranges. 398 std::string accept_ranges = 399 response.httpHeaderField("Accept-Ranges").utf8(); 400 range_supported_ = (accept_ranges.find("bytes") != std::string::npos); 401 402 // If we have verified the partial response and it is correct, we will 403 // return kOk. It's also possible for a server to support range requests 404 // without advertising "Accept-Ranges: bytes". 405 if (partial_response && VerifyPartialResponse(response)) { 406 range_supported_ = true; 407 } else if (ok_response && first_byte_position_ == 0 && 408 last_byte_position_ == kPositionNotSpecified) { 409 // We accept a 200 response for a Range:0- request, trusting the 410 // Accept-Ranges header, because Apache thinks that's a reasonable thing 411 // to return. 412 instance_size_ = content_length_; 413 } else { 414 DoneStart(kFailed); 415 return; 416 } 417 } else { 418 instance_size_ = content_length_; 419 if (response.httpStatusCode() != kHttpOK) { 420 // We didn't request a range but server didn't reply with "200 OK". 421 DoneStart(kFailed); 422 return; 423 } 424 } 425 426 } else { 427 CHECK_EQ(instance_size_, kPositionNotSpecified); 428 if (content_length_ != kPositionNotSpecified) { 429 if (first_byte_position_ == kPositionNotSpecified) 430 instance_size_ = content_length_; 431 else if (last_byte_position_ == kPositionNotSpecified) 432 instance_size_ = content_length_ + first_byte_position_; 433 } 434 } 435 436 // Calls with a successful response. 437 DoneStart(kOk); 438} 439 440void BufferedResourceLoader::didReceiveData( 441 WebURLLoader* loader, 442 const char* data, 443 int data_length, 444 int encoded_data_length) { 445 DVLOG(1) << "didReceiveData: " << data_length << " bytes"; 446 DCHECK(active_loader_.get()); 447 DCHECK_GT(data_length, 0); 448 449 buffer_.Append(reinterpret_cast<const uint8*>(data), data_length); 450 451 // If there is an active read request, try to fulfill the request. 452 if (HasPendingRead() && CanFulfillRead()) 453 ReadInternal(); 454 455 // At last see if the buffer is full and we need to defer the downloading. 456 UpdateDeferBehavior(); 457 458 // Consume excess bytes from our in-memory buffer if necessary. 459 if (buffer_.forward_bytes() > buffer_.forward_capacity()) { 460 int excess = buffer_.forward_bytes() - buffer_.forward_capacity(); 461 bool success = buffer_.Seek(excess); 462 DCHECK(success); 463 offset_ += first_offset_ + excess; 464 } 465 466 // Notify latest progress and buffered offset. 467 progress_cb_.Run(offset_ + buffer_.forward_bytes() - 1); 468 Log(); 469} 470 471void BufferedResourceLoader::didDownloadData( 472 blink::WebURLLoader* loader, 473 int dataLength, 474 int encoded_data_length) { 475 NOTIMPLEMENTED(); 476} 477 478void BufferedResourceLoader::didReceiveCachedMetadata( 479 WebURLLoader* loader, 480 const char* data, 481 int data_length) { 482 NOTIMPLEMENTED(); 483} 484 485void BufferedResourceLoader::didFinishLoading( 486 WebURLLoader* loader, 487 double finishTime, 488 int64_t total_encoded_data_length) { 489 DVLOG(1) << "didFinishLoading"; 490 DCHECK(active_loader_.get()); 491 492 // We're done with the loader. 493 active_loader_.reset(); 494 loading_cb_.Run(kLoadingFinished); 495 496 // If we didn't know the |instance_size_| we do now. 497 if (instance_size_ == kPositionNotSpecified) { 498 instance_size_ = offset_ + buffer_.forward_bytes(); 499 } 500 501 // If there is a start callback, run it. 502 if (!start_cb_.is_null()) { 503 DCHECK(read_cb_.is_null()) 504 << "Shouldn't have a read callback during start"; 505 DoneStart(kOk); 506 return; 507 } 508 509 // Don't leave read callbacks hanging around. 510 if (HasPendingRead()) { 511 // Try to fulfill with what is in the buffer. 512 if (CanFulfillRead()) 513 ReadInternal(); 514 else 515 DoneRead(kCacheMiss, 0); 516 } 517} 518 519void BufferedResourceLoader::didFail( 520 WebURLLoader* loader, 521 const WebURLError& error) { 522 DVLOG(1) << "didFail: reason=" << error.reason 523 << ", isCancellation=" << error.isCancellation 524 << ", domain=" << error.domain.utf8().data() 525 << ", localizedDescription=" 526 << error.localizedDescription.utf8().data(); 527 DCHECK(active_loader_.get()); 528 529 // We don't need to continue loading after failure. 530 // 531 // Keep it alive until we exit this method so that |error| remains valid. 532 scoped_ptr<ActiveLoader> active_loader = active_loader_.Pass(); 533 loader_failed_ = true; 534 loading_cb_.Run(kLoadingFailed); 535 536 // Don't leave start callbacks hanging around. 537 if (!start_cb_.is_null()) { 538 DCHECK(read_cb_.is_null()) 539 << "Shouldn't have a read callback during start"; 540 DoneStart(kFailed); 541 return; 542 } 543 544 // Don't leave read callbacks hanging around. 545 if (HasPendingRead()) { 546 DoneRead(kFailed, 0); 547 } 548} 549 550bool BufferedResourceLoader::HasSingleOrigin() const { 551 DCHECK(start_cb_.is_null()) 552 << "Start() must complete before calling HasSingleOrigin()"; 553 return single_origin_; 554} 555 556bool BufferedResourceLoader::DidPassCORSAccessCheck() const { 557 DCHECK(start_cb_.is_null()) 558 << "Start() must complete before calling DidPassCORSAccessCheck()"; 559 return !loader_failed_ && cors_mode_ != kUnspecified; 560} 561 562void BufferedResourceLoader::UpdateDeferStrategy(DeferStrategy strategy) { 563 if (!might_be_reused_from_cache_in_future_ && strategy == kNeverDefer) 564 strategy = kCapacityDefer; 565 defer_strategy_ = strategy; 566 UpdateDeferBehavior(); 567} 568 569void BufferedResourceLoader::SetPlaybackRate(float playback_rate) { 570 playback_rate_ = playback_rate; 571 572 // This is a pause so don't bother updating the buffer window as we'll likely 573 // get unpaused in the future. 574 if (playback_rate_ == 0.0) 575 return; 576 577 UpdateBufferWindow(); 578} 579 580void BufferedResourceLoader::SetBitrate(int bitrate) { 581 DCHECK(bitrate >= 0); 582 bitrate_ = bitrate; 583 UpdateBufferWindow(); 584} 585 586///////////////////////////////////////////////////////////////////////////// 587// Helper methods. 588 589void BufferedResourceLoader::UpdateBufferWindow() { 590 int backward_capacity; 591 int forward_capacity; 592 ComputeTargetBufferWindow( 593 playback_rate_, bitrate_, &backward_capacity, &forward_capacity); 594 595 // This does not evict data from the buffer if the new capacities are less 596 // than the current capacities; the new limits will be enforced after the 597 // existing excess buffered data is consumed. 598 buffer_.set_backward_capacity(backward_capacity); 599 buffer_.set_forward_capacity(forward_capacity); 600} 601 602void BufferedResourceLoader::UpdateDeferBehavior() { 603 if (!active_loader_) 604 return; 605 606 SetDeferred(ShouldDefer()); 607} 608 609void BufferedResourceLoader::SetDeferred(bool deferred) { 610 if (active_loader_->deferred() == deferred) 611 return; 612 613 active_loader_->SetDeferred(deferred); 614 loading_cb_.Run(deferred ? kLoadingDeferred : kLoading); 615} 616 617bool BufferedResourceLoader::ShouldDefer() const { 618 switch(defer_strategy_) { 619 case kNeverDefer: 620 return false; 621 622 case kReadThenDefer: 623 DCHECK(read_cb_.is_null() || last_offset_ > buffer_.forward_bytes()) 624 << "We shouldn't stop deferring if we can fulfill the read"; 625 return read_cb_.is_null(); 626 627 case kCapacityDefer: 628 return buffer_.forward_bytes() >= buffer_.forward_capacity(); 629 } 630 NOTREACHED(); 631 return false; 632} 633 634bool BufferedResourceLoader::CanFulfillRead() const { 635 // If we are reading too far in the backward direction. 636 if (first_offset_ < 0 && (first_offset_ + buffer_.backward_bytes()) < 0) 637 return false; 638 639 // If the start offset is too far ahead. 640 if (first_offset_ >= buffer_.forward_bytes()) 641 return false; 642 643 // At the point, we verified that first byte requested is within the buffer. 644 // If the request has completed, then just returns with what we have now. 645 if (!active_loader_) 646 return true; 647 648 // If the resource request is still active, make sure the whole requested 649 // range is covered. 650 if (last_offset_ > buffer_.forward_bytes()) 651 return false; 652 653 return true; 654} 655 656bool BufferedResourceLoader::WillFulfillRead() const { 657 // Trying to read too far behind. 658 if (first_offset_ < 0 && (first_offset_ + buffer_.backward_bytes()) < 0) 659 return false; 660 661 // Trying to read too far ahead. 662 if ((first_offset_ - buffer_.forward_bytes()) >= kForwardWaitThreshold) 663 return false; 664 665 // The resource request has completed, there's no way we can fulfill the 666 // read request. 667 if (!active_loader_) 668 return false; 669 670 return true; 671} 672 673void BufferedResourceLoader::ReadInternal() { 674 // Seek to the first byte requested. 675 bool ret = buffer_.Seek(first_offset_); 676 DCHECK(ret); 677 678 // Then do the read. 679 int read = buffer_.Read(read_buffer_, read_size_); 680 offset_ += first_offset_ + read; 681 682 // And report with what we have read. 683 DoneRead(kOk, read); 684} 685 686int64 BufferedResourceLoader::first_byte_position() const { 687 return first_byte_position_; 688} 689 690// static 691bool BufferedResourceLoader::ParseContentRange( 692 const std::string& content_range_str, int64* first_byte_position, 693 int64* last_byte_position, int64* instance_size) { 694 const std::string kUpThroughBytesUnit = "bytes "; 695 if (content_range_str.find(kUpThroughBytesUnit) != 0) 696 return false; 697 std::string range_spec = 698 content_range_str.substr(kUpThroughBytesUnit.length()); 699 size_t dash_offset = range_spec.find("-"); 700 size_t slash_offset = range_spec.find("/"); 701 702 if (dash_offset == std::string::npos || slash_offset == std::string::npos || 703 slash_offset < dash_offset || slash_offset + 1 == range_spec.length()) { 704 return false; 705 } 706 if (!base::StringToInt64(range_spec.substr(0, dash_offset), 707 first_byte_position) || 708 !base::StringToInt64(range_spec.substr(dash_offset + 1, 709 slash_offset - dash_offset - 1), 710 last_byte_position)) { 711 return false; 712 } 713 if (slash_offset == range_spec.length() - 2 && 714 range_spec[slash_offset + 1] == '*') { 715 *instance_size = kPositionNotSpecified; 716 } else { 717 if (!base::StringToInt64(range_spec.substr(slash_offset + 1), 718 instance_size)) { 719 return false; 720 } 721 } 722 if (*last_byte_position < *first_byte_position || 723 (*instance_size != kPositionNotSpecified && 724 *last_byte_position >= *instance_size)) { 725 return false; 726 } 727 728 return true; 729} 730 731bool BufferedResourceLoader::VerifyPartialResponse( 732 const WebURLResponse& response) { 733 int64 first_byte_position, last_byte_position, instance_size; 734 if (!ParseContentRange(response.httpHeaderField("Content-Range").utf8(), 735 &first_byte_position, &last_byte_position, 736 &instance_size)) { 737 return false; 738 } 739 740 if (instance_size != kPositionNotSpecified) { 741 instance_size_ = instance_size; 742 } 743 744 if (first_byte_position_ != kPositionNotSpecified && 745 first_byte_position_ != first_byte_position) { 746 return false; 747 } 748 749 // TODO(hclam): I should also check |last_byte_position|, but since 750 // we will never make such a request that it is ok to leave it unimplemented. 751 return true; 752} 753 754void BufferedResourceLoader::DoneRead(Status status, int bytes_read) { 755 if (saved_forward_capacity_) { 756 buffer_.set_forward_capacity(saved_forward_capacity_); 757 saved_forward_capacity_ = 0; 758 } 759 read_position_ = 0; 760 read_size_ = 0; 761 read_buffer_ = NULL; 762 first_offset_ = 0; 763 last_offset_ = 0; 764 Log(); 765 766 base::ResetAndReturn(&read_cb_).Run(status, bytes_read); 767} 768 769 770void BufferedResourceLoader::DoneStart(Status status) { 771 base::ResetAndReturn(&start_cb_).Run(status); 772} 773 774bool BufferedResourceLoader::IsRangeRequest() const { 775 return first_byte_position_ != kPositionNotSpecified; 776} 777 778void BufferedResourceLoader::Log() { 779 media_log_->AddEvent( 780 media_log_->CreateBufferedExtentsChangedEvent( 781 offset_ - buffer_.backward_bytes(), 782 offset_, 783 offset_ + buffer_.forward_bytes())); 784} 785 786} // namespace content 787