Include all dupe types (event when value is zero) in scan stats.
[chromium-blink-merge.git] / net / http / partial_data.cc
blob100eed83d6b762e7d600ea9f2f79949440143fc5
1 // Copyright (c) 2012 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.
5 #include "net/http/partial_data.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/format_macros.h"
10 #include "base/logging.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/stringprintf.h"
14 #include "net/base/net_errors.h"
15 #include "net/disk_cache/disk_cache.h"
16 #include "net/http/http_response_headers.h"
17 #include "net/http/http_util.h"
19 namespace net {
21 namespace {
23 // The headers that we have to process.
24 const char kLengthHeader[] = "Content-Length";
25 const char kRangeHeader[] = "Content-Range";
26 const int kDataStream = 1;
28 } // namespace
30 // A core object that can be detached from the Partialdata object at destruction
31 // so that asynchronous operations cleanup can be performed.
32 class PartialData::Core {
33 public:
34 // Build a new core object. Lifetime management is automatic.
35 static Core* CreateCore(PartialData* owner) {
36 return new Core(owner);
39 // Wrapper for Entry::GetAvailableRange. If this method returns ERR_IO_PENDING
40 // PartialData::GetAvailableRangeCompleted() will be invoked on the owner
41 // object when finished (unless Cancel() is called first).
42 int GetAvailableRange(disk_cache::Entry* entry, int64 offset, int len,
43 int64* start);
45 // Cancels a pending operation. It is a mistake to call this method if there
46 // is no operation in progress; in fact, there will be no object to do so.
47 void Cancel();
49 private:
50 explicit Core(PartialData* owner);
51 ~Core();
53 // Pending io completion routine.
54 void OnIOComplete(int result);
56 PartialData* owner_;
57 int64 start_;
59 DISALLOW_COPY_AND_ASSIGN(Core);
62 PartialData::Core::Core(PartialData* owner)
63 : owner_(owner), start_(0) {
64 DCHECK(!owner_->core_);
65 owner_->core_ = this;
68 PartialData::Core::~Core() {
69 if (owner_)
70 owner_->core_ = NULL;
73 void PartialData::Core::Cancel() {
74 DCHECK(owner_);
75 owner_ = NULL;
78 int PartialData::Core::GetAvailableRange(disk_cache::Entry* entry, int64 offset,
79 int len, int64* start) {
80 int rv = entry->GetAvailableRange(
81 offset, len, &start_, base::Bind(&PartialData::Core::OnIOComplete,
82 base::Unretained(this)));
83 if (rv != ERR_IO_PENDING) {
84 // The callback will not be invoked. Lets cleanup.
85 *start = start_;
86 delete this;
88 return rv;
91 void PartialData::Core::OnIOComplete(int result) {
92 if (owner_)
93 owner_->GetAvailableRangeCompleted(result, start_);
94 delete this;
97 // -----------------------------------------------------------------------------
99 PartialData::PartialData()
100 : current_range_start_(0),
101 current_range_end_(0),
102 cached_start_(0),
103 resource_size_(0),
104 cached_min_len_(0),
105 range_present_(false),
106 final_range_(false),
107 sparse_entry_(true),
108 truncated_(false),
109 initial_validation_(false),
110 core_(NULL) {
113 PartialData::~PartialData() {
114 if (core_)
115 core_->Cancel();
118 bool PartialData::Init(const HttpRequestHeaders& headers) {
119 std::string range_header;
120 if (!headers.GetHeader(HttpRequestHeaders::kRange, &range_header))
121 return false;
123 std::vector<HttpByteRange> ranges;
124 if (!HttpUtil::ParseRangeHeader(range_header, &ranges) || ranges.size() != 1)
125 return false;
127 // We can handle this range request.
128 byte_range_ = ranges[0];
129 if (!byte_range_.IsValid())
130 return false;
132 current_range_start_ = byte_range_.first_byte_position();
134 DVLOG(1) << "Range start: " << current_range_start_ << " end: " <<
135 byte_range_.last_byte_position();
136 return true;
139 void PartialData::SetHeaders(const HttpRequestHeaders& headers) {
140 DCHECK(extra_headers_.IsEmpty());
141 extra_headers_.CopyFrom(headers);
144 void PartialData::RestoreHeaders(HttpRequestHeaders* headers) const {
145 DCHECK(current_range_start_ >= 0 || byte_range_.IsSuffixByteRange());
146 int64 end = byte_range_.IsSuffixByteRange() ?
147 byte_range_.suffix_length() : byte_range_.last_byte_position();
149 headers->CopyFrom(extra_headers_);
150 if (truncated_ || !byte_range_.IsValid())
151 return;
153 if (current_range_start_ < 0) {
154 headers->SetHeader(HttpRequestHeaders::kRange,
155 HttpByteRange::Suffix(end).GetHeaderValue());
156 } else {
157 headers->SetHeader(HttpRequestHeaders::kRange,
158 HttpByteRange::Bounded(
159 current_range_start_, end).GetHeaderValue());
163 int PartialData::ShouldValidateCache(disk_cache::Entry* entry,
164 const CompletionCallback& callback) {
165 DCHECK_GE(current_range_start_, 0);
167 // Scan the disk cache for the first cached portion within this range.
168 int len = GetNextRangeLen();
169 if (!len)
170 return 0;
172 DVLOG(3) << "ShouldValidateCache len: " << len;
174 if (sparse_entry_) {
175 DCHECK(callback_.is_null());
176 Core* core = Core::CreateCore(this);
177 cached_min_len_ = core->GetAvailableRange(entry, current_range_start_, len,
178 &cached_start_);
180 if (cached_min_len_ == ERR_IO_PENDING) {
181 callback_ = callback;
182 return ERR_IO_PENDING;
184 } else if (!truncated_) {
185 if (byte_range_.HasFirstBytePosition() &&
186 byte_range_.first_byte_position() >= resource_size_) {
187 // The caller should take care of this condition because we should have
188 // failed IsRequestedRangeOK(), but it's better to be consistent here.
189 len = 0;
191 cached_min_len_ = len;
192 cached_start_ = current_range_start_;
195 if (cached_min_len_ < 0)
196 return cached_min_len_;
198 // Return a positive number to indicate success (versus error or finished).
199 return 1;
202 void PartialData::PrepareCacheValidation(disk_cache::Entry* entry,
203 HttpRequestHeaders* headers) {
204 DCHECK_GE(current_range_start_, 0);
205 DCHECK_GE(cached_min_len_, 0);
207 int len = GetNextRangeLen();
208 DCHECK_NE(0, len);
209 range_present_ = false;
211 headers->CopyFrom(extra_headers_);
213 if (!cached_min_len_) {
214 // We don't have anything else stored.
215 final_range_ = true;
216 cached_start_ =
217 byte_range_.HasLastBytePosition() ? current_range_start_ + len : 0;
220 if (current_range_start_ == cached_start_) {
221 // The data lives in the cache.
222 range_present_ = true;
223 current_range_end_ = cached_start_ + cached_min_len_ - 1;
224 if (len == cached_min_len_)
225 final_range_ = true;
226 headers->SetHeader(
227 HttpRequestHeaders::kRange,
228 HttpByteRange::Bounded(current_range_start_, current_range_end_)
229 .GetHeaderValue());
230 } else {
231 // This range is not in the cache.
232 current_range_end_ = cached_start_ - 1;
233 headers->SetHeader(
234 HttpRequestHeaders::kRange,
235 HttpByteRange::Bounded(current_range_start_, current_range_end_)
236 .GetHeaderValue());
240 bool PartialData::IsCurrentRangeCached() const {
241 return range_present_;
244 bool PartialData::IsLastRange() const {
245 return final_range_;
248 bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers,
249 disk_cache::Entry* entry,
250 bool truncated) {
251 resource_size_ = 0;
252 if (truncated) {
253 DCHECK_EQ(headers->response_code(), 200);
254 // We don't have the real length and the user may be trying to create a
255 // sparse entry so let's not write to this entry.
256 if (byte_range_.IsValid())
257 return false;
259 if (!headers->HasStrongValidators())
260 return false;
262 // Now we avoid resume if there is no content length, but that was not
263 // always the case so double check here.
264 int64 total_length = headers->GetContentLength();
265 if (total_length <= 0)
266 return false;
268 truncated_ = true;
269 initial_validation_ = true;
270 sparse_entry_ = false;
271 int current_len = entry->GetDataSize(kDataStream);
272 byte_range_.set_first_byte_position(current_len);
273 resource_size_ = total_length;
274 current_range_start_ = current_len;
275 cached_min_len_ = current_len;
276 cached_start_ = current_len + 1;
277 return true;
280 if (headers->response_code() != 206) {
281 DCHECK(byte_range_.IsValid());
282 sparse_entry_ = false;
283 resource_size_ = entry->GetDataSize(kDataStream);
284 DVLOG(2) << "UpdateFromStoredHeaders size: " << resource_size_;
285 return true;
288 if (!headers->HasStrongValidators())
289 return false;
291 int64 length_value = headers->GetContentLength();
292 if (length_value <= 0)
293 return false; // We must have stored the resource length.
295 resource_size_ = length_value;
297 // Make sure that this is really a sparse entry.
298 return entry->CouldBeSparse();
301 void PartialData::SetRangeToStartDownload() {
302 DCHECK(truncated_);
303 DCHECK(!sparse_entry_);
304 current_range_start_ = 0;
305 cached_start_ = 0;
306 initial_validation_ = false;
309 bool PartialData::IsRequestedRangeOK() {
310 if (byte_range_.IsValid()) {
311 if (!byte_range_.ComputeBounds(resource_size_))
312 return false;
313 if (truncated_)
314 return true;
316 if (current_range_start_ < 0)
317 current_range_start_ = byte_range_.first_byte_position();
318 } else {
319 // This is not a range request but we have partial data stored.
320 current_range_start_ = 0;
321 byte_range_.set_last_byte_position(resource_size_ - 1);
324 bool rv = current_range_start_ >= 0;
325 if (!rv)
326 current_range_start_ = 0;
328 return rv;
331 bool PartialData::ResponseHeadersOK(const HttpResponseHeaders* headers) {
332 if (headers->response_code() == 304) {
333 if (!byte_range_.IsValid() || truncated_)
334 return true;
336 // We must have a complete range here.
337 return byte_range_.HasFirstBytePosition() &&
338 byte_range_.HasLastBytePosition();
341 int64 start, end, total_length;
342 if (!headers->GetContentRange(&start, &end, &total_length))
343 return false;
344 if (total_length <= 0)
345 return false;
347 DCHECK_EQ(headers->response_code(), 206);
349 // A server should return a valid content length with a 206 (per the standard)
350 // but relax the requirement because some servers don't do that.
351 int64 content_length = headers->GetContentLength();
352 if (content_length > 0 && content_length != end - start + 1)
353 return false;
355 if (!resource_size_) {
356 // First response. Update our values with the ones provided by the server.
357 resource_size_ = total_length;
358 if (!byte_range_.HasFirstBytePosition()) {
359 byte_range_.set_first_byte_position(start);
360 current_range_start_ = start;
362 if (!byte_range_.HasLastBytePosition())
363 byte_range_.set_last_byte_position(end);
364 } else if (resource_size_ != total_length) {
365 return false;
368 if (truncated_) {
369 if (!byte_range_.HasLastBytePosition())
370 byte_range_.set_last_byte_position(end);
373 if (start != current_range_start_)
374 return false;
376 if (!current_range_end_) {
377 // There is nothing in the cache.
378 DCHECK(byte_range_.HasLastBytePosition());
379 current_range_end_ = byte_range_.last_byte_position();
380 if (current_range_end_ >= resource_size_) {
381 // We didn't know the real file size, and the server is saying that the
382 // requested range goes beyond the size. Fix it.
383 current_range_end_ = end;
384 byte_range_.set_last_byte_position(end);
388 // If we received a range, but it's not exactly the range we asked for, avoid
389 // trouble and signal an error.
390 if (end != current_range_end_)
391 return false;
393 return true;
396 // We are making multiple requests to complete the range requested by the user.
397 // Just assume that everything is fine and say that we are returning what was
398 // requested.
399 void PartialData::FixResponseHeaders(HttpResponseHeaders* headers,
400 bool success) {
401 if (truncated_)
402 return;
404 if (byte_range_.IsValid() && success) {
405 headers->UpdateWithNewRange(byte_range_, resource_size_, !sparse_entry_);
406 return;
409 headers->RemoveHeader(kLengthHeader);
410 headers->RemoveHeader(kRangeHeader);
412 if (byte_range_.IsValid()) {
413 headers->ReplaceStatusLine("HTTP/1.1 416 Requested Range Not Satisfiable");
414 headers->AddHeader(base::StringPrintf("%s: bytes 0-0/%" PRId64,
415 kRangeHeader, resource_size_));
416 headers->AddHeader(base::StringPrintf("%s: 0", kLengthHeader));
417 } else {
418 // TODO(rvargas): Is it safe to change the protocol version?
419 headers->ReplaceStatusLine("HTTP/1.1 200 OK");
420 DCHECK_NE(resource_size_, 0);
421 headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader,
422 resource_size_));
426 void PartialData::FixContentLength(HttpResponseHeaders* headers) {
427 headers->RemoveHeader(kLengthHeader);
428 headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader,
429 resource_size_));
432 int PartialData::CacheRead(disk_cache::Entry* entry,
433 IOBuffer* data,
434 int data_len,
435 const CompletionCallback& callback) {
436 int read_len = std::min(data_len, cached_min_len_);
437 if (!read_len)
438 return 0;
440 int rv = 0;
441 if (sparse_entry_) {
442 rv = entry->ReadSparseData(current_range_start_, data, read_len,
443 callback);
444 } else {
445 if (current_range_start_ > kint32max)
446 return ERR_INVALID_ARGUMENT;
448 rv = entry->ReadData(kDataStream, static_cast<int>(current_range_start_),
449 data, read_len, callback);
451 return rv;
454 int PartialData::CacheWrite(disk_cache::Entry* entry,
455 IOBuffer* data,
456 int data_len,
457 const CompletionCallback& callback) {
458 DVLOG(3) << "To write: " << data_len;
459 if (sparse_entry_) {
460 return entry->WriteSparseData(
461 current_range_start_, data, data_len, callback);
462 } else {
463 if (current_range_start_ > kint32max)
464 return ERR_INVALID_ARGUMENT;
466 return entry->WriteData(kDataStream, static_cast<int>(current_range_start_),
467 data, data_len, callback, true);
471 void PartialData::OnCacheReadCompleted(int result) {
472 DVLOG(3) << "Read: " << result;
473 if (result > 0) {
474 current_range_start_ += result;
475 cached_min_len_ -= result;
476 DCHECK_GE(cached_min_len_, 0);
480 void PartialData::OnNetworkReadCompleted(int result) {
481 if (result > 0)
482 current_range_start_ += result;
485 int PartialData::GetNextRangeLen() {
486 int64 range_len =
487 byte_range_.HasLastBytePosition() ?
488 byte_range_.last_byte_position() - current_range_start_ + 1 :
489 kint32max;
490 if (range_len > kint32max)
491 range_len = kint32max;
492 return static_cast<int32>(range_len);
495 void PartialData::GetAvailableRangeCompleted(int result, int64 start) {
496 DCHECK(!callback_.is_null());
497 DCHECK_NE(ERR_IO_PENDING, result);
499 cached_start_ = start;
500 cached_min_len_ = result;
501 if (result >= 0)
502 result = 1; // Return success, go ahead and validate the entry.
504 CompletionCallback cb = callback_;
505 callback_.Reset();
506 cb.Run(result);
509 } // namespace net