Chromium Code Reviews| OLD | NEW | 
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #include "net/http/partial_data.h" | 5 #include "net/http/partial_data.h" | 
| 6 | 6 | 
| 7 #include <limits> | |
| 8 | |
| 7 #include "base/bind.h" | 9 #include "base/bind.h" | 
| 8 #include "base/bind_helpers.h" | 10 #include "base/bind_helpers.h" | 
| 9 #include "base/format_macros.h" | 11 #include "base/format_macros.h" | 
| 10 #include "base/logging.h" | 12 #include "base/logging.h" | 
| 11 #include "base/strings/string_number_conversions.h" | 13 #include "base/strings/string_number_conversions.h" | 
| 12 #include "base/strings/string_util.h" | 14 #include "base/strings/string_util.h" | 
| 13 #include "base/strings/stringprintf.h" | 15 #include "base/strings/stringprintf.h" | 
| 14 #include "net/base/net_errors.h" | 16 #include "net/base/net_errors.h" | 
| 15 #include "net/disk_cache/disk_cache.h" | 17 #include "net/disk_cache/disk_cache.h" | 
| 16 #include "net/http/http_response_headers.h" | 18 #include "net/http/http_response_headers.h" | 
| 17 #include "net/http/http_util.h" | 19 #include "net/http/http_util.h" | 
| 18 | 20 | 
| 19 namespace net { | 21 namespace net { | 
| 20 | 22 | 
| 21 namespace { | 23 namespace { | 
| 22 | 24 | 
| 23 // The headers that we have to process. | 25 // The headers that we have to process. | 
| 24 const char kLengthHeader[] = "Content-Length"; | 26 const char kLengthHeader[] = "Content-Length"; | 
| 25 const char kRangeHeader[] = "Content-Range"; | 27 const char kRangeHeader[] = "Content-Range"; | 
| 26 const int kDataStream = 1; | 28 const int kDataStream = 1; | 
| 29 const int64 kUnbounded = std::numeric_limits<int64>::max(); | |
| 
 
rvargas (doing something else)
2015/08/19 23:46:39
A recent chr-dev thread suggest this may use an st
 
asanka
2015/09/04 19:09:04
Done. I used basictypes.h instead of sttdint becau
 
 | |
| 27 | 30 | 
| 28 } // namespace | 31 } // namespace | 
| 29 | 32 | 
| 30 PartialData::PartialData() | 33 PartialData::PartialData() | 
| 31 : current_range_start_(0), | 34 : current_range_start_(0), | 
| 32 current_range_end_(0), | 35 current_range_end_(0), | 
| 33 cached_start_(0), | 36 cached_start_(0), | 
| 34 resource_size_(0), | 37 resource_size_(0), | 
| 35 cached_min_len_(0), | 38 cached_min_len_(0), | 
| 36 range_present_(false), | 39 range_present_(false), | 
| (...skipping 16 matching lines...) Expand all Loading... | |
| 53 if (!HttpUtil::ParseRangeHeader(range_header, &ranges) || ranges.size() != 1) | 56 if (!HttpUtil::ParseRangeHeader(range_header, &ranges) || ranges.size() != 1) | 
| 54 return false; | 57 return false; | 
| 55 | 58 | 
| 56 // We can handle this range request. | 59 // We can handle this range request. | 
| 57 byte_range_ = ranges[0]; | 60 byte_range_ = ranges[0]; | 
| 58 if (!byte_range_.IsValid()) | 61 if (!byte_range_.IsValid()) | 
| 59 return false; | 62 return false; | 
| 60 | 63 | 
| 61 current_range_start_ = byte_range_.first_byte_position(); | 64 current_range_start_ = byte_range_.first_byte_position(); | 
| 62 | 65 | 
| 63 DVLOG(1) << "Range start: " << current_range_start_ << " end: " << | 66 DVLOG(1) << "Range start: " << current_range_start_ | 
| 64 byte_range_.last_byte_position(); | 67 << " end: " << byte_range_.last_byte_position(); | 
| 65 return true; | 68 return true; | 
| 66 } | 69 } | 
| 67 | 70 | 
| 68 void PartialData::SetHeaders(const HttpRequestHeaders& headers) { | 71 void PartialData::SetHeaders(const HttpRequestHeaders& headers) { | 
| 69 DCHECK(extra_headers_.IsEmpty()); | 72 DCHECK(extra_headers_.IsEmpty()); | 
| 70 extra_headers_.CopyFrom(headers); | 73 extra_headers_.CopyFrom(headers); | 
| 71 } | 74 } | 
| 72 | 75 | 
| 73 void PartialData::RestoreHeaders(HttpRequestHeaders* headers) const { | 76 void PartialData::RestoreHeaders(HttpRequestHeaders* headers) const { | 
| 74 DCHECK(current_range_start_ >= 0 || byte_range_.IsSuffixByteRange()); | 77 DCHECK(current_range_start_ >= 0 || byte_range_.IsSuffixByteRange()); | 
| (...skipping 12 matching lines...) Expand all Loading... | |
| 87 HttpByteRange::Bounded( | 90 HttpByteRange::Bounded( | 
| 88 current_range_start_, end).GetHeaderValue()); | 91 current_range_start_, end).GetHeaderValue()); | 
| 89 } | 92 } | 
| 90 } | 93 } | 
| 91 | 94 | 
| 92 int PartialData::ShouldValidateCache(disk_cache::Entry* entry, | 95 int PartialData::ShouldValidateCache(disk_cache::Entry* entry, | 
| 93 const CompletionCallback& callback) { | 96 const CompletionCallback& callback) { | 
| 94 DCHECK_GE(current_range_start_, 0); | 97 DCHECK_GE(current_range_start_, 0); | 
| 95 | 98 | 
| 96 // Scan the disk cache for the first cached portion within this range. | 99 // Scan the disk cache for the first cached portion within this range. | 
| 97 int len = GetNextRangeLen(); | 100 int64 len = GetNextRangeLen(); | 
| 98 if (!len) | 101 if (!len) | 
| 99 return 0; | 102 return 0; | 
| 100 | 103 | 
| 101 DVLOG(3) << "ShouldValidateCache len: " << len; | 104 DVLOG(3) << "ShouldValidateCache len: " << len; | 
| 102 | 105 | 
| 103 if (sparse_entry_) { | 106 if (sparse_entry_) { | 
| 104 DCHECK(callback_.is_null()); | 107 DCHECK(callback_.is_null()); | 
| 105 int64* start = new int64; | 108 int64* start = new int64; | 
| 109 // TODO(asanka): Use the full |len| when int64_t is plumbed all the way | |
| 110 // through. | |
| 111 int cached_len = std::min<int64>(std::numeric_limits<int32>::max(), len); | |
| 112 | |
| 106 // This callback now owns "start". We make sure to keep it | 113 // This callback now owns "start". We make sure to keep it | 
| 107 // in a local variable since we want to use it later. | 114 // in a local variable since we want to use it later. | 
| 108 CompletionCallback cb = | 115 CompletionCallback cb = | 
| 109 base::Bind(&PartialData::GetAvailableRangeCompleted, | 116 base::Bind(&PartialData::GetAvailableRangeCompleted, | 
| 110 weak_factory_.GetWeakPtr(), base::Owned(start)); | 117 weak_factory_.GetWeakPtr(), base::Owned(start)); | 
| 111 cached_min_len_ = | 118 cached_min_len_ = | 
| 112 entry->GetAvailableRange(current_range_start_, len, start, cb); | 119 entry->GetAvailableRange(current_range_start_, cached_len, start, cb); | 
| 113 | 120 | 
| 114 if (cached_min_len_ == ERR_IO_PENDING) { | 121 if (cached_min_len_ == ERR_IO_PENDING) { | 
| 115 callback_ = callback; | 122 callback_ = callback; | 
| 116 return ERR_IO_PENDING; | 123 return ERR_IO_PENDING; | 
| 117 } else { | 124 } else { | 
| 118 cached_start_ = *start; | 125 cached_start_ = *start; | 
| 119 } | 126 } | 
| 120 } else if (!truncated_) { | 127 } else if (!truncated_) { | 
| 
 
rvargas (doing something else)
2015/08/19 23:46:39
Aren't we going through this else after SkipCacheF
 
asanka
2015/09/04 19:09:04
Changed SkipCacheForRemainder() to explicitly set
 
 | |
| 121 if (byte_range_.HasFirstBytePosition() && | 128 if (byte_range_.HasFirstBytePosition() && | 
| 122 byte_range_.first_byte_position() >= resource_size_) { | 129 byte_range_.first_byte_position() >= resource_size_) { | 
| 123 // The caller should take care of this condition because we should have | 130 // The caller should take care of this condition because we should have | 
| 124 // failed IsRequestedRangeOK(), but it's better to be consistent here. | 131 // failed IsRequestedRangeOK(), but it's better to be consistent here. | 
| 125 len = 0; | 132 len = 0; | 
| 126 } | 133 } | 
| 127 cached_min_len_ = len; | 134 cached_min_len_ = len; | 
| 128 cached_start_ = current_range_start_; | 135 cached_start_ = current_range_start_; | 
| 129 } | 136 } | 
| 130 | 137 | 
| 131 if (cached_min_len_ < 0) | 138 if (cached_min_len_ < 0) | 
| 132 return cached_min_len_; | 139 return cached_min_len_; | 
| 133 | 140 | 
| 134 // Return a positive number to indicate success (versus error or finished). | 141 // Return a positive number to indicate success (versus error or finished). | 
| 135 return 1; | 142 return 1; | 
| 136 } | 143 } | 
| 137 | 144 | 
| 138 void PartialData::PrepareCacheValidation(disk_cache::Entry* entry, | 145 void PartialData::PrepareCacheValidation(disk_cache::Entry* entry, | 
| 139 HttpRequestHeaders* headers) { | 146 HttpRequestHeaders* headers) { | 
| 140 DCHECK_GE(current_range_start_, 0); | 147 DCHECK_GE(current_range_start_, 0); | 
| 141 DCHECK_GE(cached_min_len_, 0); | 148 DCHECK_GE(cached_min_len_, 0); | 
| 142 | 149 | 
| 143 int len = GetNextRangeLen(); | 150 int64 len = GetNextRangeLen(); | 
| 151 // PrepareCacheValidation shouldn't have been called if ShouldValidateCache() | |
| 152 // returned 0. | |
| 144 DCHECK_NE(0, len); | 153 DCHECK_NE(0, len); | 
| 145 range_present_ = false; | 154 range_present_ = false; | 
| 146 | 155 | 
| 147 headers->CopyFrom(extra_headers_); | 156 headers->CopyFrom(extra_headers_); | 
| 148 | 157 | 
| 149 if (!cached_min_len_) { | 158 if (!cached_min_len_) { | 
| 150 // We don't have anything else stored. | 159 // We don't have anything else stored. | 
| 151 final_range_ = true; | 160 final_range_ = true; | 
| 152 cached_start_ = | 161 cached_start_ = | 
| 153 byte_range_.HasLastBytePosition() ? current_range_start_ + len : 0; | 162 byte_range_.HasLastBytePosition() ? current_range_start_ + len : 0; | 
| 154 } | 163 } | 
| 155 | 164 | 
| 156 if (current_range_start_ == cached_start_) { | 165 if (current_range_start_ == cached_start_) { | 
| 157 // The data lives in the cache. | 166 // The data lives in the cache. | 
| 158 range_present_ = true; | 167 range_present_ = true; | 
| 159 current_range_end_ = cached_start_ + cached_min_len_ - 1; | 168 current_range_end_ = cached_start_ + cached_min_len_ - 1; | 
| 160 if (len == cached_min_len_) | 169 if (len == cached_min_len_) | 
| 161 final_range_ = true; | 170 final_range_ = true; | 
| 162 } else { | 171 } else { | 
| 163 // This range is not in the cache. | 172 // This range is not in the cache. | 
| 164 current_range_end_ = cached_start_ - 1; | 173 current_range_end_ = cached_start_ - 1; | 
| 165 } | 174 } | 
| 175 | |
| 166 headers->SetHeader( | 176 headers->SetHeader( | 
| 167 HttpRequestHeaders::kRange, | 177 HttpRequestHeaders::kRange, | 
| 168 HttpByteRange::Bounded(current_range_start_, current_range_end_) | 178 HttpByteRange::Bounded(current_range_start_, current_range_end_) | 
| 169 .GetHeaderValue()); | 179 .GetHeaderValue()); | 
| 170 } | 180 } | 
| 171 | 181 | 
| 172 bool PartialData::IsCurrentRangeCached() const { | 182 bool PartialData::IsCurrentRangeCached() const { | 
| 173 return range_present_; | 183 return range_present_; | 
| 174 } | 184 } | 
| 175 | 185 | 
| 176 bool PartialData::IsLastRange() const { | 186 bool PartialData::IsLastRange() const { | 
| 177 return final_range_; | 187 return final_range_; | 
| 178 } | 188 } | 
| 179 | 189 | 
| 180 bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers, | 190 bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers, | 
| 181 disk_cache::Entry* entry, | 191 disk_cache::Entry* entry, | 
| 182 bool truncated) { | 192 bool truncated) { | 
| 183 resource_size_ = 0; | 193 resource_size_ = 0; | 
| 184 if (truncated) { | 194 if (truncated) { | 
| 185 DCHECK_EQ(headers->response_code(), 200); | 195 DCHECK_EQ(headers->response_code(), 200); | 
| 186 // We don't have the real length and the user may be trying to create a | 196 // 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. | 197 // sparse entry so let's not write to this entry. | 
| 198 std::string header_string; | |
| 199 headers->GetNormalizedHeaders(&header_string); | |
| 188 if (byte_range_.IsValid()) | 200 if (byte_range_.IsValid()) | 
| 189 return false; | 201 return false; | 
| 190 | 202 | 
| 191 if (!headers->HasStrongValidators()) | 203 if (!headers->HasStrongValidators()) | 
| 192 return false; | 204 return false; | 
| 193 | 205 | 
| 194 // Now we avoid resume if there is no content length, but that was not | 206 // Now we avoid resume if there is no content length, but that was not | 
| 195 // always the case so double check here. | 207 // always the case so double check here. | 
| 196 int64 total_length = headers->GetContentLength(); | 208 int64 total_length = headers->GetContentLength(); | 
| 197 if (total_length <= 0) | 209 if (total_length <= 0) | 
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 231 } | 243 } | 
| 232 | 244 | 
| 233 void PartialData::SetRangeToStartDownload() { | 245 void PartialData::SetRangeToStartDownload() { | 
| 234 DCHECK(truncated_); | 246 DCHECK(truncated_); | 
| 235 DCHECK(!sparse_entry_); | 247 DCHECK(!sparse_entry_); | 
| 236 current_range_start_ = 0; | 248 current_range_start_ = 0; | 
| 237 cached_start_ = 0; | 249 cached_start_ = 0; | 
| 238 initial_validation_ = false; | 250 initial_validation_ = false; | 
| 239 } | 251 } | 
| 240 | 252 | 
| 253 bool PartialData::SkipCacheForRemainder() { | |
| 254 if (GetNextRangeLen() == 0) | |
| 255 return false; | |
| 256 | |
| 257 sparse_entry_ = false; | |
| 258 cached_start_ = 0; | |
| 259 cached_min_len_ = 0; | |
| 260 return true; | |
| 261 } | |
| 262 | |
| 241 bool PartialData::IsRequestedRangeOK() { | 263 bool PartialData::IsRequestedRangeOK() { | 
| 242 if (byte_range_.IsValid()) { | 264 if (byte_range_.IsValid()) { | 
| 243 if (!byte_range_.ComputeBounds(resource_size_)) | 265 if (!byte_range_.ComputeBounds(resource_size_)) | 
| 244 return false; | 266 return false; | 
| 245 if (truncated_) | 267 if (truncated_) | 
| 246 return true; | 268 return true; | 
| 247 | 269 | 
| 248 if (current_range_start_ < 0) | 270 if (current_range_start_ < 0) | 
| 249 current_range_start_ = byte_range_.first_byte_position(); | 271 current_range_start_ = byte_range_.first_byte_position(); | 
| 250 } else { | 272 } else { | 
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 358 void PartialData::FixContentLength(HttpResponseHeaders* headers) { | 380 void PartialData::FixContentLength(HttpResponseHeaders* headers) { | 
| 359 headers->RemoveHeader(kLengthHeader); | 381 headers->RemoveHeader(kLengthHeader); | 
| 360 headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader, | 382 headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader, | 
| 361 resource_size_)); | 383 resource_size_)); | 
| 362 } | 384 } | 
| 363 | 385 | 
| 364 int PartialData::CacheRead(disk_cache::Entry* entry, | 386 int PartialData::CacheRead(disk_cache::Entry* entry, | 
| 365 IOBuffer* data, | 387 IOBuffer* data, | 
| 366 int data_len, | 388 int data_len, | 
| 367 const CompletionCallback& callback) { | 389 const CompletionCallback& callback) { | 
| 368 int read_len = std::min(data_len, cached_min_len_); | 390 int read_len = std::min<int64>(data_len, cached_min_len_); | 
| 369 if (!read_len) | 391 if (!read_len) | 
| 370 return 0; | 392 return 0; | 
| 371 | 393 | 
| 372 int rv = 0; | 394 int rv = 0; | 
| 373 if (sparse_entry_) { | 395 if (sparse_entry_) { | 
| 374 rv = entry->ReadSparseData(current_range_start_, data, read_len, | 396 rv = entry->ReadSparseData(current_range_start_, data, read_len, | 
| 375 callback); | 397 callback); | 
| 376 } else { | 398 } else { | 
| 377 if (current_range_start_ > kint32max) | 399 if (current_range_start_ > kint32max) | 
| 378 return ERR_INVALID_ARGUMENT; | 400 return ERR_INVALID_ARGUMENT; | 
| (...skipping 28 matching lines...) Expand all Loading... | |
| 407 cached_min_len_ -= result; | 429 cached_min_len_ -= result; | 
| 408 DCHECK_GE(cached_min_len_, 0); | 430 DCHECK_GE(cached_min_len_, 0); | 
| 409 } | 431 } | 
| 410 } | 432 } | 
| 411 | 433 | 
| 412 void PartialData::OnNetworkReadCompleted(int result) { | 434 void PartialData::OnNetworkReadCompleted(int result) { | 
| 413 if (result > 0) | 435 if (result > 0) | 
| 414 current_range_start_ += result; | 436 current_range_start_ += result; | 
| 415 } | 437 } | 
| 416 | 438 | 
| 417 int PartialData::GetNextRangeLen() { | 439 int64 PartialData::GetNextRangeLen() { | 
| 418 int64 range_len = | 440 return byte_range_.HasLastBytePosition() | 
| 419 byte_range_.HasLastBytePosition() ? | 441 ? byte_range_.last_byte_position() - current_range_start_ + 1 | 
| 420 byte_range_.last_byte_position() - current_range_start_ + 1 : | 442 : kUnbounded; | 
| 421 kint32max; | |
| 422 if (range_len > kint32max) | |
| 423 range_len = kint32max; | |
| 424 return static_cast<int32>(range_len); | |
| 425 } | 443 } | 
| 426 | 444 | 
| 427 void PartialData::GetAvailableRangeCompleted(int64* start, int result) { | 445 void PartialData::GetAvailableRangeCompleted(int64* start, int result) { | 
| 428 DCHECK(!callback_.is_null()); | 446 DCHECK(!callback_.is_null()); | 
| 429 DCHECK_NE(ERR_IO_PENDING, result); | 447 DCHECK_NE(ERR_IO_PENDING, result); | 
| 430 | 448 | 
| 431 cached_start_ = *start; | 449 cached_start_ = *start; | 
| 432 cached_min_len_ = result; | 450 cached_min_len_ = result; | 
| 433 if (result >= 0) | 451 if (result >= 0) | 
| 434 result = 1; // Return success, go ahead and validate the entry. | 452 result = 1; // Return success, go ahead and validate the entry. | 
| 435 | 453 | 
| 436 CompletionCallback cb = callback_; | 454 CompletionCallback cb = callback_; | 
| 437 callback_.Reset(); | 455 callback_.Reset(); | 
| 438 cb.Run(result); | 456 cb.Run(result); | 
| 439 } | 457 } | 
| 440 | 458 | 
| 441 } // namespace net | 459 } // namespace net | 
| OLD | NEW |