Switch global error menu icon to vectorized MD asset
[chromium-blink-merge.git] / net / http / partial_data.cc
blob8e1eb0ab84658cc4f1e1f06c1abc289cfd19b7d7
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 PartialData::PartialData()
31 : current_range_start_(0),
32 current_range_end_(0),
33 cached_start_(0),
34 resource_size_(0),
35 cached_min_len_(0),
36 range_present_(false),
37 final_range_(false),
38 sparse_entry_(true),
39 truncated_(false),
40 initial_validation_(false),
41 weak_factory_(this) {
44 PartialData::~PartialData() {
47 bool PartialData::Init(const HttpRequestHeaders& headers) {
48 std::string range_header;
49 if (!headers.GetHeader(HttpRequestHeaders::kRange, &range_header))
50 return false;
52 std::vector<HttpByteRange> ranges;
53 if (!HttpUtil::ParseRangeHeader(range_header, &ranges) || ranges.size() != 1)
54 return false;
56 // We can handle this range request.
57 byte_range_ = ranges[0];
58 if (!byte_range_.IsValid())
59 return false;
61 current_range_start_ = byte_range_.first_byte_position();
63 DVLOG(1) << "Range start: " << current_range_start_ << " end: " <<
64 byte_range_.last_byte_position();
65 return true;
68 void PartialData::SetHeaders(const HttpRequestHeaders& headers) {
69 DCHECK(extra_headers_.IsEmpty());
70 extra_headers_.CopyFrom(headers);
73 void PartialData::RestoreHeaders(HttpRequestHeaders* headers) const {
74 DCHECK(current_range_start_ >= 0 || byte_range_.IsSuffixByteRange());
75 int64 end = byte_range_.IsSuffixByteRange() ?
76 byte_range_.suffix_length() : byte_range_.last_byte_position();
78 headers->CopyFrom(extra_headers_);
79 if (truncated_ || !byte_range_.IsValid())
80 return;
82 if (current_range_start_ < 0) {
83 headers->SetHeader(HttpRequestHeaders::kRange,
84 HttpByteRange::Suffix(end).GetHeaderValue());
85 } else {
86 headers->SetHeader(HttpRequestHeaders::kRange,
87 HttpByteRange::Bounded(
88 current_range_start_, end).GetHeaderValue());
92 int PartialData::ShouldValidateCache(disk_cache::Entry* entry,
93 const CompletionCallback& callback) {
94 DCHECK_GE(current_range_start_, 0);
96 // Scan the disk cache for the first cached portion within this range.
97 int len = GetNextRangeLen();
98 if (!len)
99 return 0;
101 DVLOG(3) << "ShouldValidateCache len: " << len;
103 if (sparse_entry_) {
104 DCHECK(callback_.is_null());
105 int64* start = new int64;
106 // This callback now owns "start". We make sure to keep it
107 // in a local variable since we want to use it later.
108 CompletionCallback cb =
109 base::Bind(&PartialData::GetAvailableRangeCompleted,
110 weak_factory_.GetWeakPtr(), base::Owned(start));
111 cached_min_len_ =
112 entry->GetAvailableRange(current_range_start_, len, start, cb);
114 if (cached_min_len_ == ERR_IO_PENDING) {
115 callback_ = callback;
116 return ERR_IO_PENDING;
117 } else {
118 cached_start_ = *start;
120 } else if (!truncated_) {
121 if (byte_range_.HasFirstBytePosition() &&
122 byte_range_.first_byte_position() >= resource_size_) {
123 // The caller should take care of this condition because we should have
124 // failed IsRequestedRangeOK(), but it's better to be consistent here.
125 len = 0;
127 cached_min_len_ = len;
128 cached_start_ = current_range_start_;
131 if (cached_min_len_ < 0)
132 return cached_min_len_;
134 // Return a positive number to indicate success (versus error or finished).
135 return 1;
138 void PartialData::PrepareCacheValidation(disk_cache::Entry* entry,
139 HttpRequestHeaders* headers) {
140 DCHECK_GE(current_range_start_, 0);
141 DCHECK_GE(cached_min_len_, 0);
143 int len = GetNextRangeLen();
144 DCHECK_NE(0, len);
145 range_present_ = false;
147 headers->CopyFrom(extra_headers_);
149 if (!cached_min_len_) {
150 // We don't have anything else stored.
151 final_range_ = true;
152 cached_start_ =
153 byte_range_.HasLastBytePosition() ? current_range_start_ + len : 0;
156 if (current_range_start_ == cached_start_) {
157 // The data lives in the cache.
158 range_present_ = true;
159 current_range_end_ = cached_start_ + cached_min_len_ - 1;
160 if (len == cached_min_len_)
161 final_range_ = true;
162 } else {
163 // This range is not in the cache.
164 current_range_end_ = cached_start_ - 1;
166 headers->SetHeader(
167 HttpRequestHeaders::kRange,
168 HttpByteRange::Bounded(current_range_start_, current_range_end_)
169 .GetHeaderValue());
172 bool PartialData::IsCurrentRangeCached() const {
173 return range_present_;
176 bool PartialData::IsLastRange() const {
177 return final_range_;
180 bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers,
181 disk_cache::Entry* entry,
182 bool truncated) {
183 resource_size_ = 0;
184 if (truncated) {
185 DCHECK_EQ(headers->response_code(), 200);
186 // We don't have the real length and the user may be trying to create a
187 // sparse entry so let's not write to this entry.
188 if (byte_range_.IsValid())
189 return false;
191 if (!headers->HasStrongValidators())
192 return false;
194 // Now we avoid resume if there is no content length, but that was not
195 // always the case so double check here.
196 int64 total_length = headers->GetContentLength();
197 if (total_length <= 0)
198 return false;
200 truncated_ = true;
201 initial_validation_ = true;
202 sparse_entry_ = false;
203 int current_len = entry->GetDataSize(kDataStream);
204 byte_range_.set_first_byte_position(current_len);
205 resource_size_ = total_length;
206 current_range_start_ = current_len;
207 cached_min_len_ = current_len;
208 cached_start_ = current_len + 1;
209 return true;
212 if (headers->response_code() != 206) {
213 DCHECK(byte_range_.IsValid());
214 sparse_entry_ = false;
215 resource_size_ = entry->GetDataSize(kDataStream);
216 DVLOG(2) << "UpdateFromStoredHeaders size: " << resource_size_;
217 return true;
220 if (!headers->HasStrongValidators())
221 return false;
223 int64 length_value = headers->GetContentLength();
224 if (length_value <= 0)
225 return false; // We must have stored the resource length.
227 resource_size_ = length_value;
229 // Make sure that this is really a sparse entry.
230 return entry->CouldBeSparse();
233 void PartialData::SetRangeToStartDownload() {
234 DCHECK(truncated_);
235 DCHECK(!sparse_entry_);
236 current_range_start_ = 0;
237 cached_start_ = 0;
238 initial_validation_ = false;
241 bool PartialData::IsRequestedRangeOK() {
242 if (byte_range_.IsValid()) {
243 if (!byte_range_.ComputeBounds(resource_size_))
244 return false;
245 if (truncated_)
246 return true;
248 if (current_range_start_ < 0)
249 current_range_start_ = byte_range_.first_byte_position();
250 } else {
251 // This is not a range request but we have partial data stored.
252 current_range_start_ = 0;
253 byte_range_.set_last_byte_position(resource_size_ - 1);
256 bool rv = current_range_start_ >= 0;
257 if (!rv)
258 current_range_start_ = 0;
260 return rv;
263 bool PartialData::ResponseHeadersOK(const HttpResponseHeaders* headers) {
264 if (headers->response_code() == 304) {
265 if (!byte_range_.IsValid() || truncated_)
266 return true;
268 // We must have a complete range here.
269 return byte_range_.HasFirstBytePosition() &&
270 byte_range_.HasLastBytePosition();
273 int64 start, end, total_length;
274 if (!headers->GetContentRange(&start, &end, &total_length))
275 return false;
276 if (total_length <= 0)
277 return false;
279 DCHECK_EQ(headers->response_code(), 206);
281 // A server should return a valid content length with a 206 (per the standard)
282 // but relax the requirement because some servers don't do that.
283 int64 content_length = headers->GetContentLength();
284 if (content_length > 0 && content_length != end - start + 1)
285 return false;
287 if (!resource_size_) {
288 // First response. Update our values with the ones provided by the server.
289 resource_size_ = total_length;
290 if (!byte_range_.HasFirstBytePosition()) {
291 byte_range_.set_first_byte_position(start);
292 current_range_start_ = start;
294 if (!byte_range_.HasLastBytePosition())
295 byte_range_.set_last_byte_position(end);
296 } else if (resource_size_ != total_length) {
297 return false;
300 if (truncated_) {
301 if (!byte_range_.HasLastBytePosition())
302 byte_range_.set_last_byte_position(end);
305 if (start != current_range_start_)
306 return false;
308 if (!current_range_end_) {
309 // There is nothing in the cache.
310 DCHECK(byte_range_.HasLastBytePosition());
311 current_range_end_ = byte_range_.last_byte_position();
312 if (current_range_end_ >= resource_size_) {
313 // We didn't know the real file size, and the server is saying that the
314 // requested range goes beyond the size. Fix it.
315 current_range_end_ = end;
316 byte_range_.set_last_byte_position(end);
320 // If we received a range, but it's not exactly the range we asked for, avoid
321 // trouble and signal an error.
322 if (end != current_range_end_)
323 return false;
325 return true;
328 // We are making multiple requests to complete the range requested by the user.
329 // Just assume that everything is fine and say that we are returning what was
330 // requested.
331 void PartialData::FixResponseHeaders(HttpResponseHeaders* headers,
332 bool success) {
333 if (truncated_)
334 return;
336 if (byte_range_.IsValid() && success) {
337 headers->UpdateWithNewRange(byte_range_, resource_size_, !sparse_entry_);
338 return;
341 headers->RemoveHeader(kLengthHeader);
342 headers->RemoveHeader(kRangeHeader);
344 if (byte_range_.IsValid()) {
345 headers->ReplaceStatusLine("HTTP/1.1 416 Requested Range Not Satisfiable");
346 headers->AddHeader(base::StringPrintf("%s: bytes 0-0/%" PRId64,
347 kRangeHeader, resource_size_));
348 headers->AddHeader(base::StringPrintf("%s: 0", kLengthHeader));
349 } else {
350 // TODO(rvargas): Is it safe to change the protocol version?
351 headers->ReplaceStatusLine("HTTP/1.1 200 OK");
352 DCHECK_NE(resource_size_, 0);
353 headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader,
354 resource_size_));
358 void PartialData::FixContentLength(HttpResponseHeaders* headers) {
359 headers->RemoveHeader(kLengthHeader);
360 headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader,
361 resource_size_));
364 int PartialData::CacheRead(disk_cache::Entry* entry,
365 IOBuffer* data,
366 int data_len,
367 const CompletionCallback& callback) {
368 int read_len = std::min(data_len, cached_min_len_);
369 if (!read_len)
370 return 0;
372 int rv = 0;
373 if (sparse_entry_) {
374 rv = entry->ReadSparseData(current_range_start_, data, read_len,
375 callback);
376 } else {
377 if (current_range_start_ > kint32max)
378 return ERR_INVALID_ARGUMENT;
380 rv = entry->ReadData(kDataStream, static_cast<int>(current_range_start_),
381 data, read_len, callback);
383 return rv;
386 int PartialData::CacheWrite(disk_cache::Entry* entry,
387 IOBuffer* data,
388 int data_len,
389 const CompletionCallback& callback) {
390 DVLOG(3) << "To write: " << data_len;
391 if (sparse_entry_) {
392 return entry->WriteSparseData(
393 current_range_start_, data, data_len, callback);
394 } else {
395 if (current_range_start_ > kint32max)
396 return ERR_INVALID_ARGUMENT;
398 return entry->WriteData(kDataStream, static_cast<int>(current_range_start_),
399 data, data_len, callback, true);
403 void PartialData::OnCacheReadCompleted(int result) {
404 DVLOG(3) << "Read: " << result;
405 if (result > 0) {
406 current_range_start_ += result;
407 cached_min_len_ -= result;
408 DCHECK_GE(cached_min_len_, 0);
412 void PartialData::OnNetworkReadCompleted(int result) {
413 if (result > 0)
414 current_range_start_ += result;
417 int PartialData::GetNextRangeLen() {
418 int64 range_len =
419 byte_range_.HasLastBytePosition() ?
420 byte_range_.last_byte_position() - current_range_start_ + 1 :
421 kint32max;
422 if (range_len > kint32max)
423 range_len = kint32max;
424 return static_cast<int32>(range_len);
427 void PartialData::GetAvailableRangeCompleted(int64* start, int result) {
428 DCHECK(!callback_.is_null());
429 DCHECK_NE(ERR_IO_PENDING, result);
431 cached_start_ = *start;
432 cached_min_len_ = result;
433 if (result >= 0)
434 result = 1; // Return success, go ahead and validate the entry.
436 CompletionCallback cb = callback_;
437 callback_.Reset();
438 cb.Run(result);
441 } // namespace net