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 "net/http/partial_data.h"
6
7#include "base/format_macros.h"
8#include "base/logging.h"
9#include "base/string_number_conversions.h"
10#include "base/string_util.h"
11#include "base/stringprintf.h"
12#include "net/base/net_errors.h"
13#include "net/disk_cache/disk_cache.h"
14#include "net/http/http_response_headers.h"
15#include "net/http/http_util.h"
16
17namespace net {
18
19namespace {
20
21// The headers that we have to process.
22const char kLengthHeader[] = "Content-Length";
23const char kRangeHeader[] = "Content-Range";
24const int kDataStream = 1;
25
26void AddRangeHeader(int64 start, int64 end, HttpRequestHeaders* headers) {
27  DCHECK(start >= 0 || end >= 0);
28  std::string my_start, my_end;
29  if (start >= 0)
30    my_start = base::Int64ToString(start);
31  if (end >= 0)
32    my_end = base::Int64ToString(end);
33
34  headers->SetHeader(
35      HttpRequestHeaders::kRange,
36      base::StringPrintf("bytes=%s-%s", my_start.c_str(), my_end.c_str()));
37}
38
39}  // namespace
40
41// A core object that can be detached from the Partialdata object at destruction
42// so that asynchronous operations cleanup can be performed.
43class PartialData::Core {
44 public:
45  // Build a new core object. Lifetime management is automatic.
46  static Core* CreateCore(PartialData* owner) {
47    return new Core(owner);
48  }
49
50  // Wrapper for Entry::GetAvailableRange. If this method returns ERR_IO_PENDING
51  // PartialData::GetAvailableRangeCompleted() will be invoked on the owner
52  // object when finished (unless Cancel() is called first).
53  int GetAvailableRange(disk_cache::Entry* entry, int64 offset, int len,
54                        int64* start);
55
56  // Cancels a pending operation. It is a mistake to call this method if there
57  // is no operation in progress; in fact, there will be no object to do so.
58  void Cancel();
59
60 private:
61  explicit Core(PartialData* owner);
62  ~Core();
63
64  // Pending io completion routine.
65  void OnIOComplete(int result);
66
67  PartialData* owner_;
68  int64 start_;
69  net::CompletionCallbackImpl<Core> callback_;
70  DISALLOW_COPY_AND_ASSIGN(Core);
71};
72
73PartialData::Core::Core(PartialData* owner)
74    : owner_(owner),
75      ALLOW_THIS_IN_INITIALIZER_LIST(callback_(this, &Core::OnIOComplete)) {
76  DCHECK(!owner_->core_);
77  owner_->core_ = this;
78}
79
80PartialData::Core::~Core() {
81  if (owner_)
82    owner_->core_ = NULL;
83}
84
85void PartialData::Core::Cancel() {
86  DCHECK(owner_);
87  owner_ = NULL;
88}
89
90int PartialData::Core::GetAvailableRange(disk_cache::Entry* entry, int64 offset,
91                                         int len, int64* start) {
92  int rv = entry->GetAvailableRange(offset, len, &start_, &callback_);
93  if (rv != net::ERR_IO_PENDING) {
94    // The callback will not be invoked. Lets cleanup.
95    *start = start_;
96    delete this;
97  }
98  return rv;
99}
100
101void PartialData::Core::OnIOComplete(int result) {
102  if (owner_)
103    owner_->GetAvailableRangeCompleted(result, start_);
104  delete this;
105}
106
107// -----------------------------------------------------------------------------
108
109PartialData::PartialData()
110    : range_present_(false),
111      final_range_(false),
112      sparse_entry_(true),
113      truncated_(false),
114      initial_validation_(false),
115      core_(NULL),
116      callback_(NULL) {
117}
118
119PartialData::~PartialData() {
120  if (core_)
121    core_->Cancel();
122}
123
124bool PartialData::Init(const HttpRequestHeaders& headers) {
125  std::string range_header;
126  if (!headers.GetHeader(HttpRequestHeaders::kRange, &range_header))
127    return false;
128
129  std::vector<HttpByteRange> ranges;
130  if (!HttpUtil::ParseRangeHeader(range_header, &ranges) || ranges.size() != 1)
131    return false;
132
133  // We can handle this range request.
134  byte_range_ = ranges[0];
135  if (!byte_range_.IsValid())
136    return false;
137
138  resource_size_ = 0;
139  current_range_start_ = byte_range_.first_byte_position();
140
141  DVLOG(1) << "Range start: " << current_range_start_ << " end: " <<
142               byte_range_.last_byte_position();
143  return true;
144}
145
146void PartialData::SetHeaders(const HttpRequestHeaders& headers) {
147  DCHECK(extra_headers_.IsEmpty());
148  extra_headers_.CopyFrom(headers);
149}
150
151void PartialData::RestoreHeaders(HttpRequestHeaders* headers) const {
152  DCHECK(current_range_start_ >= 0 || byte_range_.IsSuffixByteRange());
153  int64 end = byte_range_.IsSuffixByteRange() ?
154              byte_range_.suffix_length() : byte_range_.last_byte_position();
155
156  headers->CopyFrom(extra_headers_);
157  if (!truncated_ && byte_range_.IsValid())
158    AddRangeHeader(current_range_start_, end, headers);
159}
160
161int PartialData::ShouldValidateCache(disk_cache::Entry* entry,
162                                     CompletionCallback* callback) {
163  DCHECK_GE(current_range_start_, 0);
164
165  // Scan the disk cache for the first cached portion within this range.
166  int len = GetNextRangeLen();
167  if (!len)
168    return 0;
169
170  DVLOG(3) << "ShouldValidateCache len: " << len;
171
172  if (sparse_entry_) {
173    DCHECK(!callback_);
174    Core* core = Core::CreateCore(this);
175    cached_min_len_ = core->GetAvailableRange(entry, current_range_start_, len,
176                                              &cached_start_);
177
178    if (cached_min_len_ == ERR_IO_PENDING) {
179      callback_ = callback;
180      return ERR_IO_PENDING;
181    }
182  } else if (!truncated_) {
183    if (byte_range_.HasFirstBytePosition() &&
184        byte_range_.first_byte_position() >= resource_size_) {
185      // The caller should take care of this condition because we should have
186      // failed IsRequestedRangeOK(), but it's better to be consistent here.
187      len = 0;
188    }
189    cached_min_len_ = len;
190    cached_start_ = current_range_start_;
191  }
192
193  if (cached_min_len_ < 0)
194    return cached_min_len_;
195
196  // Return a positive number to indicate success (versus error or finished).
197  return 1;
198}
199
200void PartialData::PrepareCacheValidation(disk_cache::Entry* entry,
201                                         HttpRequestHeaders* headers) {
202  DCHECK_GE(current_range_start_, 0);
203  DCHECK_GE(cached_min_len_, 0);
204
205  int len = GetNextRangeLen();
206  DCHECK_NE(0, len);
207  range_present_ = false;
208
209  headers->CopyFrom(extra_headers_);
210
211  if (!cached_min_len_) {
212    // We don't have anything else stored.
213    final_range_ = true;
214    cached_start_ =
215        byte_range_.HasLastBytePosition() ? current_range_start_  + len : 0;
216  }
217
218  if (current_range_start_ == cached_start_) {
219    // The data lives in the cache.
220    range_present_ = true;
221    if (len == cached_min_len_)
222      final_range_ = true;
223    AddRangeHeader(current_range_start_, cached_start_ + cached_min_len_ - 1,
224                   headers);
225  } else {
226    // This range is not in the cache.
227    AddRangeHeader(current_range_start_, cached_start_ - 1, headers);
228  }
229}
230
231bool PartialData::IsCurrentRangeCached() const {
232  return range_present_;
233}
234
235bool PartialData::IsLastRange() const {
236  return final_range_;
237}
238
239bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers,
240                                          disk_cache::Entry* entry,
241                                          bool truncated) {
242  resource_size_ = 0;
243  if (truncated) {
244    DCHECK_EQ(headers->response_code(), 200);
245    // We don't have the real length and the user may be trying to create a
246    // sparse entry so let's not write to this entry.
247    if (byte_range_.IsValid())
248      return false;
249
250    // Now we avoid resume if there is no content length, but that was not
251    // always the case so double check here.
252    int64 total_length = headers->GetContentLength();
253    if (total_length <= 0 || !headers->HasStrongValidators())
254      return false;
255
256    truncated_ = true;
257    initial_validation_ = true;
258    sparse_entry_ = false;
259    int current_len = entry->GetDataSize(kDataStream);
260    byte_range_.set_first_byte_position(current_len);
261    resource_size_ = total_length;
262    current_range_start_ = current_len;
263    cached_min_len_ = current_len;
264    cached_start_ = current_len + 1;
265    return true;
266  }
267
268  if (headers->response_code() == 200) {
269    DCHECK(byte_range_.IsValid());
270    sparse_entry_ = false;
271    resource_size_ = entry->GetDataSize(kDataStream);
272    DVLOG(2) << "UpdateFromStoredHeaders size: " << resource_size_;
273    return true;
274  }
275
276  if (!headers->HasStrongValidators())
277    return false;
278
279  int64 length_value = headers->GetContentLength();
280  if (length_value <= 0)
281    return false;  // We must have stored the resource length.
282
283  resource_size_ = length_value;
284
285  // Make sure that this is really a sparse entry.
286  return entry->CouldBeSparse();
287}
288
289void PartialData::SetRangeToStartDownload() {
290  DCHECK(truncated_);
291  DCHECK(!sparse_entry_);
292  current_range_start_ = 0;
293  cached_start_ = 0;
294  initial_validation_ = false;
295}
296
297bool PartialData::IsRequestedRangeOK() {
298  if (byte_range_.IsValid()) {
299    if (!byte_range_.ComputeBounds(resource_size_))
300      return false;
301    if (truncated_)
302      return true;
303
304    if (current_range_start_ < 0)
305      current_range_start_ = byte_range_.first_byte_position();
306  } else {
307    // This is not a range request but we have partial data stored.
308    current_range_start_ = 0;
309    byte_range_.set_last_byte_position(resource_size_ - 1);
310  }
311
312  bool rv = current_range_start_ >= 0;
313  if (!rv)
314    current_range_start_ = 0;
315
316  return rv;
317}
318
319bool PartialData::ResponseHeadersOK(const HttpResponseHeaders* headers) {
320  if (headers->response_code() == 304) {
321    if (!byte_range_.IsValid() || truncated_)
322      return true;
323
324    // We must have a complete range here.
325    return byte_range_.HasFirstBytePosition() &&
326        byte_range_.HasLastBytePosition();
327  }
328
329  int64 start, end, total_length;
330  if (!headers->GetContentRange(&start, &end, &total_length))
331    return false;
332  if (total_length <= 0)
333    return false;
334
335  int64 content_length = headers->GetContentLength();
336  if (content_length < 0 || content_length != end - start + 1)
337    return false;
338
339  if (!resource_size_) {
340    // First response. Update our values with the ones provided by the server.
341    resource_size_ = total_length;
342    if (!byte_range_.HasFirstBytePosition()) {
343      byte_range_.set_first_byte_position(start);
344      current_range_start_ = start;
345    }
346    if (!byte_range_.HasLastBytePosition())
347      byte_range_.set_last_byte_position(end);
348  } else if (resource_size_ != total_length) {
349    return false;
350  }
351
352  if (truncated_) {
353    if (!byte_range_.HasLastBytePosition())
354      byte_range_.set_last_byte_position(end);
355  }
356
357  if (start != current_range_start_)
358    return false;
359
360  if (byte_range_.IsValid() && end > byte_range_.last_byte_position())
361    return false;
362
363  return true;
364}
365
366// We are making multiple requests to complete the range requested by the user.
367// Just assume that everything is fine and say that we are returning what was
368// requested.
369void PartialData::FixResponseHeaders(HttpResponseHeaders* headers,
370                                     bool success) {
371  if (truncated_)
372    return;
373
374  headers->RemoveHeader(kLengthHeader);
375  headers->RemoveHeader(kRangeHeader);
376
377  int64 range_len, start, end;
378  if (byte_range_.IsValid()) {
379    if (success) {
380      if (!sparse_entry_)
381        headers->ReplaceStatusLine("HTTP/1.1 206 Partial Content");
382
383      DCHECK(byte_range_.HasFirstBytePosition());
384      DCHECK(byte_range_.HasLastBytePosition());
385      start = byte_range_.first_byte_position();
386      end = byte_range_.last_byte_position();
387      range_len = end - start + 1;
388    } else {
389      headers->ReplaceStatusLine(
390          "HTTP/1.1 416 Requested Range Not Satisfiable");
391      start = 0;
392      end = 0;
393      range_len = 0;
394    }
395
396    headers->AddHeader(
397        base::StringPrintf("%s: bytes %" PRId64 "-%" PRId64 "/%" PRId64,
398                           kRangeHeader, start, end, resource_size_));
399  } else {
400    // TODO(rvargas): Is it safe to change the protocol version?
401    headers->ReplaceStatusLine("HTTP/1.1 200 OK");
402    DCHECK_NE(resource_size_, 0);
403    range_len = resource_size_;
404  }
405
406  headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader,
407                                        range_len));
408}
409
410void PartialData::FixContentLength(HttpResponseHeaders* headers) {
411  headers->RemoveHeader(kLengthHeader);
412  headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader,
413                                        resource_size_));
414}
415
416int PartialData::CacheRead(disk_cache::Entry* entry, IOBuffer* data,
417                           int data_len, CompletionCallback* callback) {
418  int read_len = std::min(data_len, cached_min_len_);
419  if (!read_len)
420    return 0;
421
422  int rv = 0;
423  if (sparse_entry_) {
424    rv = entry->ReadSparseData(current_range_start_, data, read_len,
425                               callback);
426  } else {
427    if (current_range_start_ > kint32max)
428      return ERR_INVALID_ARGUMENT;
429
430    rv = entry->ReadData(kDataStream, static_cast<int>(current_range_start_),
431                         data, read_len, callback);
432  }
433  return rv;
434}
435
436int PartialData::CacheWrite(disk_cache::Entry* entry, IOBuffer* data,
437                            int data_len, CompletionCallback* callback) {
438  DVLOG(3) << "To write: " << data_len;
439  if (sparse_entry_) {
440    return entry->WriteSparseData(current_range_start_, data, data_len,
441                                  callback);
442  } else  {
443    if (current_range_start_ > kint32max)
444      return ERR_INVALID_ARGUMENT;
445
446    return entry->WriteData(kDataStream, static_cast<int>(current_range_start_),
447                            data, data_len, callback, true);
448  }
449}
450
451void PartialData::OnCacheReadCompleted(int result) {
452  DVLOG(3) << "Read: " << result;
453  if (result > 0) {
454    current_range_start_ += result;
455    cached_min_len_ -= result;
456    DCHECK_GE(cached_min_len_, 0);
457  }
458}
459
460void PartialData::OnNetworkReadCompleted(int result) {
461  if (result > 0)
462    current_range_start_ += result;
463}
464
465int PartialData::GetNextRangeLen() {
466  int64 range_len =
467      byte_range_.HasLastBytePosition() ?
468      byte_range_.last_byte_position() - current_range_start_ + 1 :
469      kint32max;
470  if (range_len > kint32max)
471    range_len = kint32max;
472  return static_cast<int32>(range_len);
473}
474
475void PartialData::GetAvailableRangeCompleted(int result, int64 start) {
476  DCHECK(callback_);
477  DCHECK_NE(ERR_IO_PENDING, result);
478
479  cached_start_ = start;
480  cached_min_len_ = result;
481  if (result >= 0)
482    result = 1;  // Return success, go ahead and validate the entry.
483
484  CompletionCallback* cb = callback_;
485  callback_ = NULL;
486  cb->Run(result);
487}
488
489}  // namespace net
490