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
17using WebKit::WebFrame;
18using WebKit::WebString;
19using WebKit::WebURLError;
20using WebKit::WebURLLoader;
21using WebKit::WebURLRequest;
22using WebKit::WebURLResponse;
23using webkit_glue::MultipartResponseDelegate;
24
25namespace webkit_glue {
26
27static const int kHttpOK = 200;
28static const int kHttpPartialContent = 206;
29
30// Define the number of bytes in a megabyte.
31static const size_t kMegabyte = 1024 * 1024;
32
33// Backward capacity of the buffer, by default 2MB.
34static const size_t kBackwardCapcity = 2 * kMegabyte;
35
36// Forward capacity of the buffer, by default 10MB.
37static 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.
45static const int kForwardWaitThreshold = 2 * kMegabyte;
46
47BufferedResourceLoader::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
74BufferedResourceLoader::~BufferedResourceLoader() {
75  if (!completed_ && url_loader_.get())
76    url_loader_->cancel();
77}
78
79void 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
123void 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
149void 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
204int64 BufferedResourceLoader::GetBufferedPosition() {
205  if (buffer_.get())
206    return offset_ + static_cast<int>(buffer_->forward_bytes()) - 1;
207  return kPositionNotSpecified;
208}
209
210int64 BufferedResourceLoader::content_length() {
211  return content_length_;
212}
213
214int64 BufferedResourceLoader::instance_size() {
215  return instance_size_;
216}
217
218bool BufferedResourceLoader::range_supported() {
219  return range_supported_;
220}
221
222bool BufferedResourceLoader::network_activity() {
223  return !completed_ && !deferred_;
224}
225
226const GURL& BufferedResourceLoader::url() {
227  return url_;
228}
229
230void BufferedResourceLoader::SetURLLoaderForTest(WebURLLoader* mock_loader) {
231  url_loader_.reset(mock_loader);
232  keep_test_loader_ = true;
233}
234
235/////////////////////////////////////////////////////////////////////////////
236// WebKit::WebURLLoaderClient implementation.
237void 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
264void BufferedResourceLoader::didSendData(
265    WebURLLoader* loader,
266    unsigned long long bytes_sent,
267    unsigned long long total_bytes_to_be_sent) {
268  NOTIMPLEMENTED();
269}
270
271void 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
333void 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
368void BufferedResourceLoader::didDownloadData(
369    WebKit::WebURLLoader* loader,
370    int dataLength) {
371  NOTIMPLEMENTED();
372}
373
374void BufferedResourceLoader::didReceiveCachedMetadata(
375    WebURLLoader* loader,
376    const char* data,
377    int data_length) {
378  NOTIMPLEMENTED();
379}
380
381void 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
420void 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
444bool BufferedResourceLoader::HasSingleOrigin() const {
445  return single_origin_;
446}
447
448/////////////////////////////////////////////////////////////////////////////
449// Helper methods.
450void 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
462void BufferedResourceLoader::UpdateDeferStrategy(DeferStrategy strategy) {
463  defer_strategy_ = strategy;
464  UpdateDeferBehavior();
465}
466
467bool 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
489bool 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
520bool BufferedResourceLoader::ToggleDeferring() {
521  deferred_ = !deferred_;
522  if (url_loader_.get()) {
523    url_loader_->setDefersLoading(deferred_);
524    return true;
525  }
526  return false;
527}
528
529bool 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
552bool 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
570void 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
583bool 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
608std::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
629void 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
639void BufferedResourceLoader::DoneStart(int error) {
640  start_callback_->RunWithParams(Tuple1<int>(error));
641  start_callback_.reset();
642}
643
644void BufferedResourceLoader::NotifyNetworkEvent() {
645  if (event_callback_.get())
646    event_callback_->Run();
647}
648
649bool BufferedResourceLoader::IsRangeRequest() const {
650  return first_byte_position_ != kPositionNotSpecified;
651}
652
653}  // namespace webkit_glue
654