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
22 using blink::WebFrame;
23 using blink::WebString;
24 using blink::WebURLError;
25 using blink::WebURLLoader;
26 using blink::WebURLLoaderOptions;
27 using blink::WebURLRequest;
28 using blink::WebURLResponse;
29
30 namespace content {
31
32 static const int kHttpOK = 200;
33 static const int kHttpPartialContent = 206;
34
35 // Define the number of bytes in a megabyte.
36 static 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.
41 static 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.
46 static 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.
52 static 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.
ComputeTargetBufferWindow(float playback_rate,int bitrate,int * out_backward_capacity,int * out_forward_capacity)57 static 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
BufferedResourceLoader(const GURL & url,CORSMode cors_mode,int64 first_byte_position,int64 last_byte_position,DeferStrategy strategy,int bitrate,float playback_rate,media::MediaLog * media_log)97 BufferedResourceLoader::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
~BufferedResourceLoader()134 BufferedResourceLoader::~BufferedResourceLoader() {}
135
Start(const StartCB & start_cb,const LoadingStateChangedCB & loading_cb,const ProgressCB & progress_cb,WebFrame * frame)136 void 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
Stop()206 void 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
Read(int64 position,int read_size,uint8 * buffer,const ReadCB & read_cb)217 void 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
content_length()313 int64 BufferedResourceLoader::content_length() {
314 return content_length_;
315 }
316
instance_size()317 int64 BufferedResourceLoader::instance_size() {
318 return instance_size_;
319 }
320
range_supported()321 bool BufferedResourceLoader::range_supported() {
322 return range_supported_;
323 }
324
325 /////////////////////////////////////////////////////////////////////////////
326 // blink::WebURLLoaderClient implementation.
willSendRequest(WebURLLoader * loader,WebURLRequest & newRequest,const WebURLResponse & redirectResponse)327 void 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
didSendData(WebURLLoader * loader,unsigned long long bytes_sent,unsigned long long total_bytes_to_be_sent)347 void BufferedResourceLoader::didSendData(
348 WebURLLoader* loader,
349 unsigned long long bytes_sent,
350 unsigned long long total_bytes_to_be_sent) {
351 NOTIMPLEMENTED();
352 }
353
didReceiveResponse(WebURLLoader * loader,const WebURLResponse & response)354 void 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
didReceiveData(WebURLLoader * loader,const char * data,int data_length,int encoded_data_length)439 void 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
didDownloadData(blink::WebURLLoader * loader,int dataLength,int encoded_data_length)470 void BufferedResourceLoader::didDownloadData(
471 blink::WebURLLoader* loader,
472 int dataLength,
473 int encoded_data_length) {
474 NOTIMPLEMENTED();
475 }
476
didReceiveCachedMetadata(WebURLLoader * loader,const char * data,int data_length)477 void BufferedResourceLoader::didReceiveCachedMetadata(
478 WebURLLoader* loader,
479 const char* data,
480 int data_length) {
481 NOTIMPLEMENTED();
482 }
483
didFinishLoading(WebURLLoader * loader,double finishTime)484 void 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
didFail(WebURLLoader * loader,const WebURLError & error)517 void 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
HasSingleOrigin() const548 bool BufferedResourceLoader::HasSingleOrigin() const {
549 DCHECK(start_cb_.is_null())
550 << "Start() must complete before calling HasSingleOrigin()";
551 return single_origin_;
552 }
553
DidPassCORSAccessCheck() const554 bool 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
UpdateDeferStrategy(DeferStrategy strategy)560 void 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
SetPlaybackRate(float playback_rate)567 void 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
SetBitrate(int bitrate)578 void BufferedResourceLoader::SetBitrate(int bitrate) {
579 DCHECK(bitrate >= 0);
580 bitrate_ = bitrate;
581 UpdateBufferWindow();
582 }
583
584 /////////////////////////////////////////////////////////////////////////////
585 // Helper methods.
586
UpdateBufferWindow()587 void 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
UpdateDeferBehavior()600 void BufferedResourceLoader::UpdateDeferBehavior() {
601 if (!active_loader_)
602 return;
603
604 SetDeferred(ShouldDefer());
605 }
606
SetDeferred(bool deferred)607 void 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
ShouldDefer() const615 bool 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
CanFulfillRead() const632 bool 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
WillFulfillRead() const654 bool 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
ReadInternal()671 void 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
first_byte_position() const684 int64 BufferedResourceLoader::first_byte_position() const {
685 return first_byte_position_;
686 }
687
688 // static
ParseContentRange(const std::string & content_range_str,int64 * first_byte_position,int64 * last_byte_position,int64 * instance_size)689 bool 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
VerifyPartialResponse(const WebURLResponse & response)729 bool 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
DoneRead(Status status,int bytes_read)752 void 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
DoneStart(Status status)768 void BufferedResourceLoader::DoneStart(Status status) {
769 base::ResetAndReturn(&start_cb_).Run(status);
770 }
771
IsRangeRequest() const772 bool BufferedResourceLoader::IsRangeRequest() const {
773 return first_byte_position_ != kPositionNotSpecified;
774 }
775
Log()776 void 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