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