• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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 "webkit/glue/media/buffered_resource_loader.h"
6 
7 #include "base/format_macros.h"
8 #include "base/string_util.h"
9 #include "net/base/net_errors.h"
10 #include "third_party/WebKit/Source/WebKit/chromium/public/WebKit.h"
11 #include "third_party/WebKit/Source/WebKit/chromium/public/WebKitClient.h"
12 #include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h"
13 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLError.h"
14 #include "webkit/glue/multipart_response_delegate.h"
15 #include "webkit/glue/webkit_glue.h"
16 
17 using WebKit::WebFrame;
18 using WebKit::WebString;
19 using WebKit::WebURLError;
20 using WebKit::WebURLLoader;
21 using WebKit::WebURLRequest;
22 using WebKit::WebURLResponse;
23 using webkit_glue::MultipartResponseDelegate;
24 
25 namespace webkit_glue {
26 
27 static const int kHttpOK = 200;
28 static const int kHttpPartialContent = 206;
29 
30 // Define the number of bytes in a megabyte.
31 static const size_t kMegabyte = 1024 * 1024;
32 
33 // Backward capacity of the buffer, by default 2MB.
34 static const size_t kBackwardCapcity = 2 * kMegabyte;
35 
36 // Forward capacity of the buffer, by default 10MB.
37 static const size_t kForwardCapacity = 10 * kMegabyte;
38 
39 // The threshold of bytes that we should wait until the data arrives in the
40 // future instead of restarting a new connection. This number is defined in the
41 // number of bytes, we should determine this value from typical connection speed
42 // and amount of time for a suitable wait. Now I just make a guess for this
43 // number to be 2MB.
44 // TODO(hclam): determine a better value for this.
45 static const int kForwardWaitThreshold = 2 * kMegabyte;
46 
BufferedResourceLoader(const GURL & url,int64 first_byte_position,int64 last_byte_position)47 BufferedResourceLoader::BufferedResourceLoader(
48     const GURL& url,
49     int64 first_byte_position,
50     int64 last_byte_position)
51     : buffer_(new media::SeekableBuffer(kBackwardCapcity, kForwardCapacity)),
52       deferred_(false),
53       defer_strategy_(kReadThenDefer),
54       completed_(false),
55       range_requested_(false),
56       range_supported_(false),
57       url_(url),
58       first_byte_position_(first_byte_position),
59       last_byte_position_(last_byte_position),
60       single_origin_(true),
61       start_callback_(NULL),
62       offset_(0),
63       content_length_(kPositionNotSpecified),
64       instance_size_(kPositionNotSpecified),
65       read_callback_(NULL),
66       read_position_(0),
67       read_size_(0),
68       read_buffer_(NULL),
69       first_offset_(0),
70       last_offset_(0),
71       keep_test_loader_(false) {
72 }
73 
~BufferedResourceLoader()74 BufferedResourceLoader::~BufferedResourceLoader() {
75   if (!completed_ && url_loader_.get())
76     url_loader_->cancel();
77 }
78 
Start(net::CompletionCallback * start_callback,NetworkEventCallback * event_callback,WebFrame * frame)79 void BufferedResourceLoader::Start(net::CompletionCallback* start_callback,
80                                    NetworkEventCallback* event_callback,
81                                    WebFrame* frame) {
82   // Make sure we have not started.
83   DCHECK(!start_callback_.get());
84   DCHECK(!event_callback_.get());
85   DCHECK(start_callback);
86   DCHECK(event_callback);
87   CHECK(frame);
88 
89   start_callback_.reset(start_callback);
90   event_callback_.reset(event_callback);
91 
92   if (first_byte_position_ != kPositionNotSpecified) {
93     // TODO(hclam): server may not support range request so |offset_| may not
94     // equal to |first_byte_position_|.
95     offset_ = first_byte_position_;
96   }
97 
98   // Increment the reference count right before we start the request. This
99   // reference will be release when this request has ended.
100   AddRef();
101 
102   // Prepare the request.
103   WebURLRequest request(url_);
104   request.setTargetType(WebURLRequest::TargetIsMedia);
105 
106   if (IsRangeRequest()) {
107     range_requested_ = true;
108     request.setHTTPHeaderField(WebString::fromUTF8("Range"),
109                                WebString::fromUTF8(GenerateHeaders(
110                                    first_byte_position_,
111                                    last_byte_position_)));
112   }
113   frame->setReferrerForRequest(request, WebKit::WebURL());
114 
115   // This flag is for unittests as we don't want to reset |url_loader|
116   if (!keep_test_loader_)
117     url_loader_.reset(frame->createAssociatedURLLoader());
118 
119   // Start the resource loading.
120   url_loader_->loadAsynchronously(request, this);
121 }
122 
Stop()123 void BufferedResourceLoader::Stop() {
124   // Reset callbacks.
125   start_callback_.reset();
126   event_callback_.reset();
127   read_callback_.reset();
128 
129   // Use the internal buffer to signal that we have been stopped.
130   // TODO(hclam): Not so pretty to do this.
131   if (!buffer_.get())
132     return;
133 
134   // Destroy internal buffer.
135   buffer_.reset();
136 
137   if (url_loader_.get()) {
138     if (deferred_)
139       url_loader_->setDefersLoading(false);
140     deferred_ = false;
141 
142     if (!completed_) {
143       url_loader_->cancel();
144       completed_ = true;
145     }
146   }
147 }
148 
Read(int64 position,int read_size,uint8 * buffer,net::CompletionCallback * read_callback)149 void BufferedResourceLoader::Read(int64 position,
150                                   int read_size,
151                                   uint8* buffer,
152                                   net::CompletionCallback* read_callback) {
153   DCHECK(!read_callback_.get());
154   DCHECK(buffer_.get());
155   DCHECK(read_callback);
156   DCHECK(buffer);
157 
158   // Save the parameter of reading.
159   read_callback_.reset(read_callback);
160   read_position_ = position;
161   read_size_ = read_size;
162   read_buffer_ = buffer;
163 
164   // If read position is beyond the instance size, we cannot read there.
165   if (instance_size_ != kPositionNotSpecified &&
166       instance_size_ <= read_position_) {
167     DoneRead(0);
168     return;
169   }
170 
171   // Make sure |offset_| and |read_position_| does not differ by a large
172   // amount.
173   if (read_position_ > offset_ + kint32max ||
174       read_position_ < offset_ + kint32min) {
175     DoneRead(net::ERR_CACHE_MISS);
176     return;
177   }
178 
179   // Prepare the parameters.
180   first_offset_ = static_cast<int>(read_position_ - offset_);
181   last_offset_ = first_offset_ + read_size_;
182 
183   // If we can serve the request now, do the actual read.
184   if (CanFulfillRead()) {
185     ReadInternal();
186     UpdateDeferBehavior();
187     return;
188   }
189 
190   // If you're deferred and you can't fulfill the read because you don't have
191   // enough data, you will never fulfill the read.
192   // Update defer behavior to re-enable deferring if need be.
193   UpdateDeferBehavior();
194 
195   // If we expected the read request to be fulfilled later, returns
196   // immediately and let more data to flow in.
197   if (WillFulfillRead())
198     return;
199 
200   // Make a callback to report failure.
201   DoneRead(net::ERR_CACHE_MISS);
202 }
203 
GetBufferedPosition()204 int64 BufferedResourceLoader::GetBufferedPosition() {
205   if (buffer_.get())
206     return offset_ + static_cast<int>(buffer_->forward_bytes()) - 1;
207   return kPositionNotSpecified;
208 }
209 
content_length()210 int64 BufferedResourceLoader::content_length() {
211   return content_length_;
212 }
213 
instance_size()214 int64 BufferedResourceLoader::instance_size() {
215   return instance_size_;
216 }
217 
range_supported()218 bool BufferedResourceLoader::range_supported() {
219   return range_supported_;
220 }
221 
network_activity()222 bool BufferedResourceLoader::network_activity() {
223   return !completed_ && !deferred_;
224 }
225 
url()226 const GURL& BufferedResourceLoader::url() {
227   return url_;
228 }
229 
SetURLLoaderForTest(WebURLLoader * mock_loader)230 void BufferedResourceLoader::SetURLLoaderForTest(WebURLLoader* mock_loader) {
231   url_loader_.reset(mock_loader);
232   keep_test_loader_ = true;
233 }
234 
235 /////////////////////////////////////////////////////////////////////////////
236 // WebKit::WebURLLoaderClient implementation.
willSendRequest(WebURLLoader * loader,WebURLRequest & newRequest,const WebURLResponse & redirectResponse)237 void BufferedResourceLoader::willSendRequest(
238     WebURLLoader* loader,
239     WebURLRequest& newRequest,
240     const WebURLResponse& redirectResponse) {
241 
242   // The load may have been stopped and |start_callback| is destroyed.
243   // In this case we shouldn't do anything.
244   if (!start_callback_.get()) {
245     // Set the url in the request to an invalid value (empty url).
246     newRequest.setURL(WebKit::WebURL());
247     return;
248   }
249 
250   // Only allow |single_origin_| if we haven't seen a different origin yet.
251   if (single_origin_)
252     single_origin_ = url_.GetOrigin() == GURL(newRequest.url()).GetOrigin();
253 
254   if (!IsProtocolSupportedForMedia(newRequest.url())) {
255     // Set the url in the request to an invalid value (empty url).
256     newRequest.setURL(WebKit::WebURL());
257     DoneStart(net::ERR_ADDRESS_INVALID);
258     return;
259   }
260 
261   url_ = newRequest.url();
262 }
263 
didSendData(WebURLLoader * loader,unsigned long long bytes_sent,unsigned long long total_bytes_to_be_sent)264 void BufferedResourceLoader::didSendData(
265     WebURLLoader* loader,
266     unsigned long long bytes_sent,
267     unsigned long long total_bytes_to_be_sent) {
268   NOTIMPLEMENTED();
269 }
270 
didReceiveResponse(WebURLLoader * loader,const WebURLResponse & response)271 void BufferedResourceLoader::didReceiveResponse(
272     WebURLLoader* loader,
273     const WebURLResponse& response) {
274 
275   // The loader may have been stopped and |start_callback| is destroyed.
276   // In this case we shouldn't do anything.
277   if (!start_callback_.get())
278     return;
279 
280   bool partial_response = false;
281 
282   // We make a strong assumption that when we reach here we have either
283   // received a response from HTTP/HTTPS protocol or the request was
284   // successful (in particular range request). So we only verify the partial
285   // response for HTTP and HTTPS protocol.
286   if (url_.SchemeIs(kHttpScheme) || url_.SchemeIs(kHttpsScheme)) {
287     int error = net::OK;
288 
289     // Check to see whether the server supports byte ranges.
290     std::string accept_ranges =
291         response.httpHeaderField("Accept-Ranges").utf8();
292     range_supported_ = (accept_ranges.find("bytes") != std::string::npos);
293 
294     partial_response = (response.httpStatusCode() == kHttpPartialContent);
295 
296     if (range_requested_) {
297       // If we have verified the partial response and it is correct, we will
298       // return net::OK. It's also possible for a server to support range
299       // requests without advertising Accept-Ranges: bytes.
300       if (partial_response && VerifyPartialResponse(response))
301         range_supported_ = true;
302       else
303         error = net::ERR_INVALID_RESPONSE;
304     } else if (response.httpStatusCode() != kHttpOK) {
305       // We didn't request a range but server didn't reply with "200 OK".
306       error = net::ERR_FAILED;
307     }
308 
309     if (error != net::OK) {
310       DoneStart(error);
311       Stop();
312       return;
313     }
314   } else {
315     // For any protocol other than HTTP and HTTPS, assume range request is
316     // always fulfilled.
317     partial_response = range_requested_;
318   }
319 
320   // Expected content length can be |kPositionNotSpecified|, in that case
321   // |content_length_| is not specified and this is a streaming response.
322   content_length_ = response.expectedContentLength();
323 
324   // If we have not requested a range, then the size of the instance is equal
325   // to the content length.
326   if (!partial_response)
327     instance_size_ = content_length_;
328 
329   // Calls with a successful response.
330   DoneStart(net::OK);
331 }
332 
didReceiveData(WebURLLoader * loader,const char * data,int data_length,int encoded_data_length)333 void BufferedResourceLoader::didReceiveData(
334     WebURLLoader* loader,
335     const char* data,
336     int data_length,
337     int encoded_data_length) {
338   DCHECK(!completed_);
339   DCHECK_GT(data_length, 0);
340 
341   // If this loader has been stopped, |buffer_| would be destroyed.
342   // In this case we shouldn't do anything.
343   if (!buffer_.get())
344     return;
345 
346   // Writes more data to |buffer_|.
347   buffer_->Append(reinterpret_cast<const uint8*>(data), data_length);
348 
349   // If there is an active read request, try to fulfill the request.
350   if (HasPendingRead() && CanFulfillRead())
351     ReadInternal();
352 
353   // At last see if the buffer is full and we need to defer the downloading.
354   UpdateDeferBehavior();
355 
356   // Consume excess bytes from our in-memory buffer if necessary.
357   if (buffer_->forward_bytes() > buffer_->forward_capacity()) {
358     size_t excess = buffer_->forward_bytes() - buffer_->forward_capacity();
359     bool success = buffer_->Seek(excess);
360     DCHECK(success);
361     offset_ += first_offset_ + excess;
362   }
363 
364   // Notify that we have received some data.
365   NotifyNetworkEvent();
366 }
367 
didDownloadData(WebKit::WebURLLoader * loader,int dataLength)368 void BufferedResourceLoader::didDownloadData(
369     WebKit::WebURLLoader* loader,
370     int dataLength) {
371   NOTIMPLEMENTED();
372 }
373 
didReceiveCachedMetadata(WebURLLoader * loader,const char * data,int data_length)374 void BufferedResourceLoader::didReceiveCachedMetadata(
375     WebURLLoader* loader,
376     const char* data,
377     int data_length) {
378   NOTIMPLEMENTED();
379 }
380 
didFinishLoading(WebURLLoader * loader,double finishTime)381 void BufferedResourceLoader::didFinishLoading(
382     WebURLLoader* loader,
383     double finishTime) {
384   DCHECK(!completed_);
385   completed_ = true;
386 
387   // If we didn't know the |instance_size_| we do now.
388   if (instance_size_ == kPositionNotSpecified) {
389     instance_size_ = offset_ + buffer_->forward_bytes();
390   }
391 
392   // If there is a start callback, calls it.
393   if (start_callback_.get()) {
394     DoneStart(net::OK);
395   }
396 
397   // If there is a pending read but the request has ended, returns with what
398   // we have.
399   if (HasPendingRead()) {
400     // Make sure we have a valid buffer before we satisfy a read request.
401     DCHECK(buffer_.get());
402 
403     // Try to fulfill with what is in the buffer.
404     if (CanFulfillRead())
405       ReadInternal();
406     else
407       DoneRead(net::ERR_CACHE_MISS);
408   }
409 
410   // There must not be any outstanding read request.
411   DCHECK(!HasPendingRead());
412 
413   // Notify that network response is completed.
414   NotifyNetworkEvent();
415 
416   url_loader_.reset();
417   Release();
418 }
419 
didFail(WebURLLoader * loader,const WebURLError & error)420 void BufferedResourceLoader::didFail(
421     WebURLLoader* loader,
422     const WebURLError& error) {
423   DCHECK(!completed_);
424   completed_ = true;
425 
426   // If there is a start callback, calls it.
427   if (start_callback_.get()) {
428     DoneStart(error.reason);
429   }
430 
431   // If there is a pending read but the request failed, return with the
432   // reason for the error.
433   if (HasPendingRead()) {
434     DoneRead(error.reason);
435   }
436 
437   // Notify that network response is completed.
438   NotifyNetworkEvent();
439 
440   url_loader_.reset();
441   Release();
442 }
443 
HasSingleOrigin() const444 bool BufferedResourceLoader::HasSingleOrigin() const {
445   return single_origin_;
446 }
447 
448 /////////////////////////////////////////////////////////////////////////////
449 // Helper methods.
UpdateDeferBehavior()450 void BufferedResourceLoader::UpdateDeferBehavior() {
451   if (!url_loader_.get() || !buffer_.get())
452     return;
453 
454   if ((deferred_ && ShouldDisableDefer()) ||
455       (!deferred_ && ShouldEnableDefer())) {
456     bool eventOccurred = ToggleDeferring();
457     if (eventOccurred)
458       NotifyNetworkEvent();
459   }
460 }
461 
UpdateDeferStrategy(DeferStrategy strategy)462 void BufferedResourceLoader::UpdateDeferStrategy(DeferStrategy strategy) {
463   defer_strategy_ = strategy;
464   UpdateDeferBehavior();
465 }
466 
ShouldEnableDefer()467 bool BufferedResourceLoader::ShouldEnableDefer() {
468   // If we're already deferring, then enabling makes no sense.
469   if (deferred_)
470     return false;
471 
472   switch(defer_strategy_) {
473     // Never defer at all, so never enable defer.
474     case kNeverDefer:
475       return false;
476 
477     // Defer if nothing is being requested.
478     case kReadThenDefer:
479       return !read_callback_.get();
480 
481     // Defer if we've reached the max capacity of the threshold.
482     case kThresholdDefer:
483       return buffer_->forward_bytes() >= buffer_->forward_capacity();
484   }
485   // Otherwise don't enable defer.
486   return false;
487 }
488 
ShouldDisableDefer()489 bool BufferedResourceLoader::ShouldDisableDefer() {
490   // If we're not deferring, then disabling makes no sense.
491   if (!deferred_)
492     return false;
493 
494   switch(defer_strategy_) {
495     // Always disable deferring.
496     case kNeverDefer:
497       return true;
498 
499     // We have an outstanding read request, and we have not buffered enough
500     // yet to fulfill the request; disable defer to get more data.
501     case kReadThenDefer: {
502       size_t amount_buffered = buffer_->forward_bytes();
503       size_t amount_to_read = static_cast<size_t>(read_size_);
504       return read_callback_.get() && amount_buffered < amount_to_read;
505     }
506 
507     // We have less than half the capacity of our threshold, so
508     // disable defer to get more data.
509     case kThresholdDefer: {
510       size_t amount_buffered = buffer_->forward_bytes();
511       size_t half_capacity = buffer_->forward_capacity() / 2;
512       return amount_buffered < half_capacity;
513     }
514   }
515 
516   // Otherwise keep deferring.
517   return false;
518 }
519 
ToggleDeferring()520 bool BufferedResourceLoader::ToggleDeferring() {
521   deferred_ = !deferred_;
522   if (url_loader_.get()) {
523     url_loader_->setDefersLoading(deferred_);
524     return true;
525   }
526   return false;
527 }
528 
CanFulfillRead()529 bool BufferedResourceLoader::CanFulfillRead() {
530   // If we are reading too far in the backward direction.
531   if (first_offset_ < 0 &&
532       first_offset_ + static_cast<int>(buffer_->backward_bytes()) < 0)
533     return false;
534 
535   // If the start offset is too far ahead.
536   if (first_offset_ >= static_cast<int>(buffer_->forward_bytes()))
537     return false;
538 
539   // At the point, we verified that first byte requested is within the buffer.
540   // If the request has completed, then just returns with what we have now.
541   if (completed_)
542     return true;
543 
544   // If the resource request is still active, make sure the whole requested
545   // range is covered.
546   if (last_offset_ > static_cast<int>(buffer_->forward_bytes()))
547     return false;
548 
549   return true;
550 }
551 
WillFulfillRead()552 bool BufferedResourceLoader::WillFulfillRead() {
553   // Reading too far in the backward direction.
554   if (first_offset_ < 0 &&
555       first_offset_ + static_cast<int>(buffer_->backward_bytes()) < 0)
556     return false;
557 
558   // Try to read too far ahead.
559   if (last_offset_ > kForwardWaitThreshold)
560     return false;
561 
562   // The resource request has completed, there's no way we can fulfill the
563   // read request.
564   if (completed_)
565     return false;
566 
567   return true;
568 }
569 
ReadInternal()570 void BufferedResourceLoader::ReadInternal() {
571   // Seek to the first byte requested.
572   bool ret = buffer_->Seek(first_offset_);
573   DCHECK(ret);
574 
575   // Then do the read.
576   int read = static_cast<int>(buffer_->Read(read_buffer_, read_size_));
577   offset_ += first_offset_ + read;
578 
579   // And report with what we have read.
580   DoneRead(read);
581 }
582 
VerifyPartialResponse(const WebURLResponse & response)583 bool BufferedResourceLoader::VerifyPartialResponse(
584     const WebURLResponse& response) {
585   int first_byte_position, last_byte_position, instance_size;
586 
587   if (!MultipartResponseDelegate::ReadContentRanges(response,
588                          &first_byte_position,
589                          &last_byte_position,
590                          &instance_size)) {
591     return false;
592   }
593 
594   if (instance_size != kPositionNotSpecified) {
595     instance_size_ = instance_size;
596   }
597 
598   if (first_byte_position_ != kPositionNotSpecified &&
599       first_byte_position_ != first_byte_position) {
600     return false;
601   }
602 
603   // TODO(hclam): I should also check |last_byte_position|, but since
604   // we will never make such a request that it is ok to leave it unimplemented.
605   return true;
606 }
607 
GenerateHeaders(int64 first_byte_position,int64 last_byte_position)608 std::string BufferedResourceLoader::GenerateHeaders(
609     int64 first_byte_position,
610     int64 last_byte_position) {
611   // Construct the value for the range header.
612   std::string header;
613   if (first_byte_position > kPositionNotSpecified &&
614       last_byte_position > kPositionNotSpecified) {
615     if (first_byte_position <= last_byte_position) {
616       header = base::StringPrintf("bytes=%" PRId64 "-%" PRId64,
617                                   first_byte_position,
618                                   last_byte_position);
619     }
620   } else if (first_byte_position > kPositionNotSpecified) {
621     header = base::StringPrintf("bytes=%" PRId64 "-",
622                                 first_byte_position);
623   } else if (last_byte_position > kPositionNotSpecified) {
624     NOTIMPLEMENTED() << "Suffix range not implemented";
625   }
626   return header;
627 }
628 
DoneRead(int error)629 void BufferedResourceLoader::DoneRead(int error) {
630   read_callback_->RunWithParams(Tuple1<int>(error));
631   read_callback_.reset();
632   read_position_ = 0;
633   read_size_ = 0;
634   read_buffer_ = NULL;
635   first_offset_ = 0;
636   last_offset_ = 0;
637 }
638 
DoneStart(int error)639 void BufferedResourceLoader::DoneStart(int error) {
640   start_callback_->RunWithParams(Tuple1<int>(error));
641   start_callback_.reset();
642 }
643 
NotifyNetworkEvent()644 void BufferedResourceLoader::NotifyNetworkEvent() {
645   if (event_callback_.get())
646     event_callback_->Run();
647 }
648 
IsRangeRequest() const649 bool BufferedResourceLoader::IsRangeRequest() const {
650   return first_byte_position_ != kPositionNotSpecified;
651 }
652 
653 }  // namespace webkit_glue
654