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