Roll src/third_party/WebKit 8b42d1d:744641d (svn 186770:186771)
[chromium-blink-merge.git] / net / http / partial_data.cc
blob247d76b5e935a5da6eea91ee2062f99d6bf52029
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/profiler/scoped_tracker.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "net/base/net_errors.h"
16 #include "net/disk_cache/disk_cache.h"
17 #include "net/http/http_response_headers.h"
18 #include "net/http/http_util.h"
20 namespace net {
22 namespace {
24 // The headers that we have to process.
25 const char kLengthHeader[] = "Content-Length";
26 const char kRangeHeader[] = "Content-Range";
27 const int kDataStream = 1;
29 } // namespace
31 // A core object that can be detached from the Partialdata object at destruction
32 // so that asynchronous operations cleanup can be performed.
33 class PartialData::Core {
34 public:
35 // Build a new core object. Lifetime management is automatic.
36 static Core* CreateCore(PartialData* owner) {
37 return new Core(owner);
40 // Wrapper for Entry::GetAvailableRange. If this method returns ERR_IO_PENDING
41 // PartialData::GetAvailableRangeCompleted() will be invoked on the owner
42 // object when finished (unless Cancel() is called first).
43 int GetAvailableRange(disk_cache::Entry* entry, int64 offset, int len,
44 int64* start);
46 // Cancels a pending operation. It is a mistake to call this method if there
47 // is no operation in progress; in fact, there will be no object to do so.
48 void Cancel();
50 private:
51 explicit Core(PartialData* owner);
52 ~Core();
54 // Pending io completion routine.
55 void OnIOComplete(int result);
57 PartialData* owner_;
58 int64 start_;
60 DISALLOW_COPY_AND_ASSIGN(Core);
63 PartialData::Core::Core(PartialData* owner)
64 : owner_(owner), start_(0) {
65 DCHECK(!owner_->core_);
66 owner_->core_ = this;
69 PartialData::Core::~Core() {
70 if (owner_)
71 owner_->core_ = NULL;
74 void PartialData::Core::Cancel() {
75 DCHECK(owner_);
76 owner_ = NULL;
79 int PartialData::Core::GetAvailableRange(disk_cache::Entry* entry, int64 offset,
80 int len, int64* start) {
81 int rv = entry->GetAvailableRange(
82 offset, len, &start_, base::Bind(&PartialData::Core::OnIOComplete,
83 base::Unretained(this)));
84 if (rv != net::ERR_IO_PENDING) {
85 // The callback will not be invoked. Lets cleanup.
86 *start = start_;
87 delete this;
89 return rv;
92 void PartialData::Core::OnIOComplete(int result) {
93 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
94 tracked_objects::ScopedTracker tracking_profile(
95 FROM_HERE_WITH_EXPLICIT_FUNCTION(
96 "422516 PartialData::Core::OnIOComplete"));
98 if (owner_)
99 owner_->GetAvailableRangeCompleted(result, start_);
100 delete this;
103 // -----------------------------------------------------------------------------
105 PartialData::PartialData()
106 : range_present_(false),
107 final_range_(false),
108 sparse_entry_(true),
109 truncated_(false),
110 initial_validation_(false),
111 core_(NULL) {
114 PartialData::~PartialData() {
115 if (core_)
116 core_->Cancel();
119 bool PartialData::Init(const HttpRequestHeaders& headers) {
120 std::string range_header;
121 if (!headers.GetHeader(HttpRequestHeaders::kRange, &range_header))
122 return false;
124 std::vector<HttpByteRange> ranges;
125 if (!HttpUtil::ParseRangeHeader(range_header, &ranges) || ranges.size() != 1)
126 return false;
128 // We can handle this range request.
129 byte_range_ = ranges[0];
130 if (!byte_range_.IsValid())
131 return false;
133 resource_size_ = 0;
134 current_range_start_ = byte_range_.first_byte_position();
136 DVLOG(1) << "Range start: " << current_range_start_ << " end: " <<
137 byte_range_.last_byte_position();
138 return true;
141 void PartialData::SetHeaders(const HttpRequestHeaders& headers) {
142 DCHECK(extra_headers_.IsEmpty());
143 extra_headers_.CopyFrom(headers);
146 void PartialData::RestoreHeaders(HttpRequestHeaders* headers) const {
147 DCHECK(current_range_start_ >= 0 || byte_range_.IsSuffixByteRange());
148 int64 end = byte_range_.IsSuffixByteRange() ?
149 byte_range_.suffix_length() : byte_range_.last_byte_position();
151 headers->CopyFrom(extra_headers_);
152 if (truncated_ || !byte_range_.IsValid())
153 return;
155 if (current_range_start_ < 0) {
156 headers->SetHeader(HttpRequestHeaders::kRange,
157 HttpByteRange::Suffix(end).GetHeaderValue());
158 } else {
159 headers->SetHeader(HttpRequestHeaders::kRange,
160 HttpByteRange::Bounded(
161 current_range_start_, end).GetHeaderValue());
165 int PartialData::ShouldValidateCache(disk_cache::Entry* entry,
166 const CompletionCallback& callback) {
167 DCHECK_GE(current_range_start_, 0);
169 // Scan the disk cache for the first cached portion within this range.
170 int len = GetNextRangeLen();
171 if (!len)
172 return 0;
174 DVLOG(3) << "ShouldValidateCache len: " << len;
176 if (sparse_entry_) {
177 DCHECK(callback_.is_null());
178 Core* core = Core::CreateCore(this);
179 cached_min_len_ = core->GetAvailableRange(entry, current_range_start_, len,
180 &cached_start_);
182 if (cached_min_len_ == ERR_IO_PENDING) {
183 callback_ = callback;
184 return ERR_IO_PENDING;
186 } else if (!truncated_) {
187 if (byte_range_.HasFirstBytePosition() &&
188 byte_range_.first_byte_position() >= resource_size_) {
189 // The caller should take care of this condition because we should have
190 // failed IsRequestedRangeOK(), but it's better to be consistent here.
191 len = 0;
193 cached_min_len_ = len;
194 cached_start_ = current_range_start_;
197 if (cached_min_len_ < 0)
198 return cached_min_len_;
200 // Return a positive number to indicate success (versus error or finished).
201 return 1;
204 void PartialData::PrepareCacheValidation(disk_cache::Entry* entry,
205 HttpRequestHeaders* headers) {
206 DCHECK_GE(current_range_start_, 0);
207 DCHECK_GE(cached_min_len_, 0);
209 int len = GetNextRangeLen();
210 DCHECK_NE(0, len);
211 range_present_ = false;
213 headers->CopyFrom(extra_headers_);
215 if (!cached_min_len_) {
216 // We don't have anything else stored.
217 final_range_ = true;
218 cached_start_ =
219 byte_range_.HasLastBytePosition() ? current_range_start_ + len : 0;
222 if (current_range_start_ == cached_start_) {
223 // The data lives in the cache.
224 range_present_ = true;
225 if (len == cached_min_len_)
226 final_range_ = true;
227 headers->SetHeader(
228 HttpRequestHeaders::kRange,
229 net::HttpByteRange::Bounded(
230 current_range_start_,
231 cached_start_ + cached_min_len_ - 1).GetHeaderValue());
232 } else {
233 // This range is not in the cache.
234 headers->SetHeader(
235 HttpRequestHeaders::kRange,
236 net::HttpByteRange::Bounded(
237 current_range_start_, cached_start_ - 1).GetHeaderValue());
241 bool PartialData::IsCurrentRangeCached() const {
242 return range_present_;
245 bool PartialData::IsLastRange() const {
246 return final_range_;
249 bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers,
250 disk_cache::Entry* entry,
251 bool truncated) {
252 resource_size_ = 0;
253 if (truncated) {
254 DCHECK_EQ(headers->response_code(), 200);
255 // We don't have the real length and the user may be trying to create a
256 // sparse entry so let's not write to this entry.
257 if (byte_range_.IsValid())
258 return false;
260 if (!headers->HasStrongValidators())
261 return false;
263 // Now we avoid resume if there is no content length, but that was not
264 // always the case so double check here.
265 int64 total_length = headers->GetContentLength();
266 if (total_length <= 0)
267 return false;
269 truncated_ = true;
270 initial_validation_ = true;
271 sparse_entry_ = false;
272 int current_len = entry->GetDataSize(kDataStream);
273 byte_range_.set_first_byte_position(current_len);
274 resource_size_ = total_length;
275 current_range_start_ = current_len;
276 cached_min_len_ = current_len;
277 cached_start_ = current_len + 1;
278 return true;
281 if (headers->response_code() != 206) {
282 DCHECK(byte_range_.IsValid());
283 sparse_entry_ = false;
284 resource_size_ = entry->GetDataSize(kDataStream);
285 DVLOG(2) << "UpdateFromStoredHeaders size: " << resource_size_;
286 return true;
289 int64 length_value = headers->GetContentLength();
290 if (length_value <= 0)
291 return false; // We must have stored the resource length.
293 resource_size_ = length_value;
295 // Make sure that this is really a sparse entry.
296 return entry->CouldBeSparse();
299 void PartialData::SetRangeToStartDownload() {
300 DCHECK(truncated_);
301 DCHECK(!sparse_entry_);
302 current_range_start_ = 0;
303 cached_start_ = 0;
304 initial_validation_ = false;
307 bool PartialData::IsRequestedRangeOK() {
308 if (byte_range_.IsValid()) {
309 if (!byte_range_.ComputeBounds(resource_size_))
310 return false;
311 if (truncated_)
312 return true;
314 if (current_range_start_ < 0)
315 current_range_start_ = byte_range_.first_byte_position();
316 } else {
317 // This is not a range request but we have partial data stored.
318 current_range_start_ = 0;
319 byte_range_.set_last_byte_position(resource_size_ - 1);
322 bool rv = current_range_start_ >= 0;
323 if (!rv)
324 current_range_start_ = 0;
326 return rv;
329 bool PartialData::ResponseHeadersOK(const HttpResponseHeaders* headers) {
330 if (headers->response_code() == 304) {
331 if (!byte_range_.IsValid() || truncated_)
332 return true;
334 // We must have a complete range here.
335 return byte_range_.HasFirstBytePosition() &&
336 byte_range_.HasLastBytePosition();
339 int64 start, end, total_length;
340 if (!headers->GetContentRange(&start, &end, &total_length))
341 return false;
342 if (total_length <= 0)
343 return false;
345 DCHECK_EQ(headers->response_code(), 206);
347 // A server should return a valid content length with a 206 (per the standard)
348 // but relax the requirement because some servers don't do that.
349 int64 content_length = headers->GetContentLength();
350 if (content_length > 0 && content_length != end - start + 1)
351 return false;
353 if (!resource_size_) {
354 // First response. Update our values with the ones provided by the server.
355 resource_size_ = total_length;
356 if (!byte_range_.HasFirstBytePosition()) {
357 byte_range_.set_first_byte_position(start);
358 current_range_start_ = start;
360 if (!byte_range_.HasLastBytePosition())
361 byte_range_.set_last_byte_position(end);
362 } else if (resource_size_ != total_length) {
363 return false;
366 if (truncated_) {
367 if (!byte_range_.HasLastBytePosition())
368 byte_range_.set_last_byte_position(end);
371 if (start != current_range_start_)
372 return false;
374 if (byte_range_.IsValid() && end > byte_range_.last_byte_position())
375 return false;
377 return true;
380 // We are making multiple requests to complete the range requested by the user.
381 // Just assume that everything is fine and say that we are returning what was
382 // requested.
383 void PartialData::FixResponseHeaders(HttpResponseHeaders* headers,
384 bool success) {
385 if (truncated_)
386 return;
388 if (byte_range_.IsValid() && success) {
389 headers->UpdateWithNewRange(byte_range_, resource_size_, !sparse_entry_);
390 return;
393 headers->RemoveHeader(kLengthHeader);
394 headers->RemoveHeader(kRangeHeader);
396 if (byte_range_.IsValid()) {
397 headers->ReplaceStatusLine("HTTP/1.1 416 Requested Range Not Satisfiable");
398 headers->AddHeader(base::StringPrintf("%s: bytes 0-0/%" PRId64,
399 kRangeHeader, resource_size_));
400 headers->AddHeader(base::StringPrintf("%s: 0", kLengthHeader));
401 } else {
402 // TODO(rvargas): Is it safe to change the protocol version?
403 headers->ReplaceStatusLine("HTTP/1.1 200 OK");
404 DCHECK_NE(resource_size_, 0);
405 headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader,
406 resource_size_));
410 void PartialData::FixContentLength(HttpResponseHeaders* headers) {
411 headers->RemoveHeader(kLengthHeader);
412 headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader,
413 resource_size_));
416 int PartialData::CacheRead(
417 disk_cache::Entry* entry, IOBuffer* data, int data_len,
418 const net::CompletionCallback& callback) {
419 int read_len = std::min(data_len, cached_min_len_);
420 if (!read_len)
421 return 0;
423 int rv = 0;
424 if (sparse_entry_) {
425 rv = entry->ReadSparseData(current_range_start_, data, read_len,
426 callback);
427 } else {
428 if (current_range_start_ > kint32max)
429 return ERR_INVALID_ARGUMENT;
431 rv = entry->ReadData(kDataStream, static_cast<int>(current_range_start_),
432 data, read_len, callback);
434 return rv;
437 int PartialData::CacheWrite(
438 disk_cache::Entry* entry, IOBuffer* data, int data_len,
439 const net::CompletionCallback& callback) {
440 DVLOG(3) << "To write: " << data_len;
441 if (sparse_entry_) {
442 return entry->WriteSparseData(
443 current_range_start_, data, data_len, callback);
444 } else {
445 if (current_range_start_ > kint32max)
446 return ERR_INVALID_ARGUMENT;
448 return entry->WriteData(kDataStream, static_cast<int>(current_range_start_),
449 data, data_len, callback, true);
453 void PartialData::OnCacheReadCompleted(int result) {
454 DVLOG(3) << "Read: " << result;
455 if (result > 0) {
456 current_range_start_ += result;
457 cached_min_len_ -= result;
458 DCHECK_GE(cached_min_len_, 0);
462 void PartialData::OnNetworkReadCompleted(int result) {
463 if (result > 0)
464 current_range_start_ += result;
467 int PartialData::GetNextRangeLen() {
468 int64 range_len =
469 byte_range_.HasLastBytePosition() ?
470 byte_range_.last_byte_position() - current_range_start_ + 1 :
471 kint32max;
472 if (range_len > kint32max)
473 range_len = kint32max;
474 return static_cast<int32>(range_len);
477 void PartialData::GetAvailableRangeCompleted(int result, int64 start) {
478 DCHECK(!callback_.is_null());
479 DCHECK_NE(ERR_IO_PENDING, result);
481 cached_start_ = start;
482 cached_min_len_ = result;
483 if (result >= 0)
484 result = 1; // Return success, go ahead and validate the entry.
486 CompletionCallback cb = callback_;
487 callback_.Reset();
488 cb.Run(result);
491 } // namespace net