| OLD | NEW |
| (Empty) |
| 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. | |
| 4 | |
| 5 #include "net/http/partial_data.h" | |
| 6 | |
| 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" | |
| 19 | |
| 20 namespace net { | |
| 21 | |
| 22 namespace { | |
| 23 | |
| 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; | |
| 28 | |
| 29 } // namespace | |
| 30 | |
| 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); | |
| 38 } | |
| 39 | |
| 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); | |
| 45 | |
| 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(); | |
| 49 | |
| 50 private: | |
| 51 explicit Core(PartialData* owner); | |
| 52 ~Core(); | |
| 53 | |
| 54 // Pending io completion routine. | |
| 55 void OnIOComplete(int result); | |
| 56 | |
| 57 PartialData* owner_; | |
| 58 int64 start_; | |
| 59 | |
| 60 DISALLOW_COPY_AND_ASSIGN(Core); | |
| 61 }; | |
| 62 | |
| 63 PartialData::Core::Core(PartialData* owner) | |
| 64 : owner_(owner), start_(0) { | |
| 65 DCHECK(!owner_->core_); | |
| 66 owner_->core_ = this; | |
| 67 } | |
| 68 | |
| 69 PartialData::Core::~Core() { | |
| 70 if (owner_) | |
| 71 owner_->core_ = NULL; | |
| 72 } | |
| 73 | |
| 74 void PartialData::Core::Cancel() { | |
| 75 DCHECK(owner_); | |
| 76 owner_ = NULL; | |
| 77 } | |
| 78 | |
| 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; | |
| 88 } | |
| 89 return rv; | |
| 90 } | |
| 91 | |
| 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")); | |
| 97 | |
| 98 if (owner_) | |
| 99 owner_->GetAvailableRangeCompleted(result, start_); | |
| 100 delete this; | |
| 101 } | |
| 102 | |
| 103 // ----------------------------------------------------------------------------- | |
| 104 | |
| 105 PartialData::PartialData() | |
| 106 : current_range_start_(0), | |
| 107 current_range_end_(0), | |
| 108 cached_start_(0), | |
| 109 resource_size_(0), | |
| 110 cached_min_len_(0), | |
| 111 range_present_(false), | |
| 112 final_range_(false), | |
| 113 sparse_entry_(true), | |
| 114 truncated_(false), | |
| 115 initial_validation_(false), | |
| 116 core_(NULL) { | |
| 117 } | |
| 118 | |
| 119 PartialData::~PartialData() { | |
| 120 if (core_) | |
| 121 core_->Cancel(); | |
| 122 } | |
| 123 | |
| 124 bool 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 current_range_start_ = byte_range_.first_byte_position(); | |
| 139 | |
| 140 DVLOG(1) << "Range start: " << current_range_start_ << " end: " << | |
| 141 byte_range_.last_byte_position(); | |
| 142 return true; | |
| 143 } | |
| 144 | |
| 145 void PartialData::SetHeaders(const HttpRequestHeaders& headers) { | |
| 146 DCHECK(extra_headers_.IsEmpty()); | |
| 147 extra_headers_.CopyFrom(headers); | |
| 148 } | |
| 149 | |
| 150 void PartialData::RestoreHeaders(HttpRequestHeaders* headers) const { | |
| 151 DCHECK(current_range_start_ >= 0 || byte_range_.IsSuffixByteRange()); | |
| 152 int64 end = byte_range_.IsSuffixByteRange() ? | |
| 153 byte_range_.suffix_length() : byte_range_.last_byte_position(); | |
| 154 | |
| 155 headers->CopyFrom(extra_headers_); | |
| 156 if (truncated_ || !byte_range_.IsValid()) | |
| 157 return; | |
| 158 | |
| 159 if (current_range_start_ < 0) { | |
| 160 headers->SetHeader(HttpRequestHeaders::kRange, | |
| 161 HttpByteRange::Suffix(end).GetHeaderValue()); | |
| 162 } else { | |
| 163 headers->SetHeader(HttpRequestHeaders::kRange, | |
| 164 HttpByteRange::Bounded( | |
| 165 current_range_start_, end).GetHeaderValue()); | |
| 166 } | |
| 167 } | |
| 168 | |
| 169 int PartialData::ShouldValidateCache(disk_cache::Entry* entry, | |
| 170 const CompletionCallback& callback) { | |
| 171 DCHECK_GE(current_range_start_, 0); | |
| 172 | |
| 173 // Scan the disk cache for the first cached portion within this range. | |
| 174 int len = GetNextRangeLen(); | |
| 175 if (!len) | |
| 176 return 0; | |
| 177 | |
| 178 DVLOG(3) << "ShouldValidateCache len: " << len; | |
| 179 | |
| 180 if (sparse_entry_) { | |
| 181 DCHECK(callback_.is_null()); | |
| 182 Core* core = Core::CreateCore(this); | |
| 183 cached_min_len_ = core->GetAvailableRange(entry, current_range_start_, len, | |
| 184 &cached_start_); | |
| 185 | |
| 186 if (cached_min_len_ == ERR_IO_PENDING) { | |
| 187 callback_ = callback; | |
| 188 return ERR_IO_PENDING; | |
| 189 } | |
| 190 } else if (!truncated_) { | |
| 191 if (byte_range_.HasFirstBytePosition() && | |
| 192 byte_range_.first_byte_position() >= resource_size_) { | |
| 193 // The caller should take care of this condition because we should have | |
| 194 // failed IsRequestedRangeOK(), but it's better to be consistent here. | |
| 195 len = 0; | |
| 196 } | |
| 197 cached_min_len_ = len; | |
| 198 cached_start_ = current_range_start_; | |
| 199 } | |
| 200 | |
| 201 if (cached_min_len_ < 0) | |
| 202 return cached_min_len_; | |
| 203 | |
| 204 // Return a positive number to indicate success (versus error or finished). | |
| 205 return 1; | |
| 206 } | |
| 207 | |
| 208 void PartialData::PrepareCacheValidation(disk_cache::Entry* entry, | |
| 209 HttpRequestHeaders* headers) { | |
| 210 DCHECK_GE(current_range_start_, 0); | |
| 211 DCHECK_GE(cached_min_len_, 0); | |
| 212 | |
| 213 int len = GetNextRangeLen(); | |
| 214 DCHECK_NE(0, len); | |
| 215 range_present_ = false; | |
| 216 | |
| 217 headers->CopyFrom(extra_headers_); | |
| 218 | |
| 219 if (!cached_min_len_) { | |
| 220 // We don't have anything else stored. | |
| 221 final_range_ = true; | |
| 222 cached_start_ = | |
| 223 byte_range_.HasLastBytePosition() ? current_range_start_ + len : 0; | |
| 224 } | |
| 225 | |
| 226 if (current_range_start_ == cached_start_) { | |
| 227 // The data lives in the cache. | |
| 228 range_present_ = true; | |
| 229 current_range_end_ = cached_start_ + cached_min_len_ - 1; | |
| 230 if (len == cached_min_len_) | |
| 231 final_range_ = true; | |
| 232 headers->SetHeader( | |
| 233 HttpRequestHeaders::kRange, | |
| 234 HttpByteRange::Bounded(current_range_start_, current_range_end_) | |
| 235 .GetHeaderValue()); | |
| 236 } else { | |
| 237 // This range is not in the cache. | |
| 238 current_range_end_ = cached_start_ - 1; | |
| 239 headers->SetHeader( | |
| 240 HttpRequestHeaders::kRange, | |
| 241 HttpByteRange::Bounded(current_range_start_, current_range_end_) | |
| 242 .GetHeaderValue()); | |
| 243 } | |
| 244 } | |
| 245 | |
| 246 bool PartialData::IsCurrentRangeCached() const { | |
| 247 return range_present_; | |
| 248 } | |
| 249 | |
| 250 bool PartialData::IsLastRange() const { | |
| 251 return final_range_; | |
| 252 } | |
| 253 | |
| 254 bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers, | |
| 255 disk_cache::Entry* entry, | |
| 256 bool truncated) { | |
| 257 resource_size_ = 0; | |
| 258 if (truncated) { | |
| 259 DCHECK_EQ(headers->response_code(), 200); | |
| 260 // We don't have the real length and the user may be trying to create a | |
| 261 // sparse entry so let's not write to this entry. | |
| 262 if (byte_range_.IsValid()) | |
| 263 return false; | |
| 264 | |
| 265 if (!headers->HasStrongValidators()) | |
| 266 return false; | |
| 267 | |
| 268 // Now we avoid resume if there is no content length, but that was not | |
| 269 // always the case so double check here. | |
| 270 int64 total_length = headers->GetContentLength(); | |
| 271 if (total_length <= 0) | |
| 272 return false; | |
| 273 | |
| 274 truncated_ = true; | |
| 275 initial_validation_ = true; | |
| 276 sparse_entry_ = false; | |
| 277 int current_len = entry->GetDataSize(kDataStream); | |
| 278 byte_range_.set_first_byte_position(current_len); | |
| 279 resource_size_ = total_length; | |
| 280 current_range_start_ = current_len; | |
| 281 cached_min_len_ = current_len; | |
| 282 cached_start_ = current_len + 1; | |
| 283 return true; | |
| 284 } | |
| 285 | |
| 286 if (headers->response_code() != 206) { | |
| 287 DCHECK(byte_range_.IsValid()); | |
| 288 sparse_entry_ = false; | |
| 289 resource_size_ = entry->GetDataSize(kDataStream); | |
| 290 DVLOG(2) << "UpdateFromStoredHeaders size: " << resource_size_; | |
| 291 return true; | |
| 292 } | |
| 293 | |
| 294 if (!headers->HasStrongValidators()) | |
| 295 return false; | |
| 296 | |
| 297 int64 length_value = headers->GetContentLength(); | |
| 298 if (length_value <= 0) | |
| 299 return false; // We must have stored the resource length. | |
| 300 | |
| 301 resource_size_ = length_value; | |
| 302 | |
| 303 // Make sure that this is really a sparse entry. | |
| 304 return entry->CouldBeSparse(); | |
| 305 } | |
| 306 | |
| 307 void PartialData::SetRangeToStartDownload() { | |
| 308 DCHECK(truncated_); | |
| 309 DCHECK(!sparse_entry_); | |
| 310 current_range_start_ = 0; | |
| 311 cached_start_ = 0; | |
| 312 initial_validation_ = false; | |
| 313 } | |
| 314 | |
| 315 bool PartialData::IsRequestedRangeOK() { | |
| 316 if (byte_range_.IsValid()) { | |
| 317 if (!byte_range_.ComputeBounds(resource_size_)) | |
| 318 return false; | |
| 319 if (truncated_) | |
| 320 return true; | |
| 321 | |
| 322 if (current_range_start_ < 0) | |
| 323 current_range_start_ = byte_range_.first_byte_position(); | |
| 324 } else { | |
| 325 // This is not a range request but we have partial data stored. | |
| 326 current_range_start_ = 0; | |
| 327 byte_range_.set_last_byte_position(resource_size_ - 1); | |
| 328 } | |
| 329 | |
| 330 bool rv = current_range_start_ >= 0; | |
| 331 if (!rv) | |
| 332 current_range_start_ = 0; | |
| 333 | |
| 334 return rv; | |
| 335 } | |
| 336 | |
| 337 bool PartialData::ResponseHeadersOK(const HttpResponseHeaders* headers) { | |
| 338 if (headers->response_code() == 304) { | |
| 339 if (!byte_range_.IsValid() || truncated_) | |
| 340 return true; | |
| 341 | |
| 342 // We must have a complete range here. | |
| 343 return byte_range_.HasFirstBytePosition() && | |
| 344 byte_range_.HasLastBytePosition(); | |
| 345 } | |
| 346 | |
| 347 int64 start, end, total_length; | |
| 348 if (!headers->GetContentRange(&start, &end, &total_length)) | |
| 349 return false; | |
| 350 if (total_length <= 0) | |
| 351 return false; | |
| 352 | |
| 353 DCHECK_EQ(headers->response_code(), 206); | |
| 354 | |
| 355 // A server should return a valid content length with a 206 (per the standard) | |
| 356 // but relax the requirement because some servers don't do that. | |
| 357 int64 content_length = headers->GetContentLength(); | |
| 358 if (content_length > 0 && content_length != end - start + 1) | |
| 359 return false; | |
| 360 | |
| 361 if (!resource_size_) { | |
| 362 // First response. Update our values with the ones provided by the server. | |
| 363 resource_size_ = total_length; | |
| 364 if (!byte_range_.HasFirstBytePosition()) { | |
| 365 byte_range_.set_first_byte_position(start); | |
| 366 current_range_start_ = start; | |
| 367 } | |
| 368 if (!byte_range_.HasLastBytePosition()) | |
| 369 byte_range_.set_last_byte_position(end); | |
| 370 } else if (resource_size_ != total_length) { | |
| 371 return false; | |
| 372 } | |
| 373 | |
| 374 if (truncated_) { | |
| 375 if (!byte_range_.HasLastBytePosition()) | |
| 376 byte_range_.set_last_byte_position(end); | |
| 377 } | |
| 378 | |
| 379 if (start != current_range_start_) | |
| 380 return false; | |
| 381 | |
| 382 if (!current_range_end_) { | |
| 383 // There is nothing in the cache. | |
| 384 DCHECK(byte_range_.HasLastBytePosition()); | |
| 385 current_range_end_ = byte_range_.last_byte_position(); | |
| 386 if (current_range_end_ >= resource_size_) { | |
| 387 // We didn't know the real file size, and the server is saying that the | |
| 388 // requested range goes beyond the size. Fix it. | |
| 389 current_range_end_ = end; | |
| 390 byte_range_.set_last_byte_position(end); | |
| 391 } | |
| 392 } | |
| 393 | |
| 394 // If we received a range, but it's not exactly the range we asked for, avoid | |
| 395 // trouble and signal an error. | |
| 396 if (end != current_range_end_) | |
| 397 return false; | |
| 398 | |
| 399 return true; | |
| 400 } | |
| 401 | |
| 402 // We are making multiple requests to complete the range requested by the user. | |
| 403 // Just assume that everything is fine and say that we are returning what was | |
| 404 // requested. | |
| 405 void PartialData::FixResponseHeaders(HttpResponseHeaders* headers, | |
| 406 bool success) { | |
| 407 if (truncated_) | |
| 408 return; | |
| 409 | |
| 410 if (byte_range_.IsValid() && success) { | |
| 411 headers->UpdateWithNewRange(byte_range_, resource_size_, !sparse_entry_); | |
| 412 return; | |
| 413 } | |
| 414 | |
| 415 headers->RemoveHeader(kLengthHeader); | |
| 416 headers->RemoveHeader(kRangeHeader); | |
| 417 | |
| 418 if (byte_range_.IsValid()) { | |
| 419 headers->ReplaceStatusLine("HTTP/1.1 416 Requested Range Not Satisfiable"); | |
| 420 headers->AddHeader(base::StringPrintf("%s: bytes 0-0/%" PRId64, | |
| 421 kRangeHeader, resource_size_)); | |
| 422 headers->AddHeader(base::StringPrintf("%s: 0", kLengthHeader)); | |
| 423 } else { | |
| 424 // TODO(rvargas): Is it safe to change the protocol version? | |
| 425 headers->ReplaceStatusLine("HTTP/1.1 200 OK"); | |
| 426 DCHECK_NE(resource_size_, 0); | |
| 427 headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader, | |
| 428 resource_size_)); | |
| 429 } | |
| 430 } | |
| 431 | |
| 432 void PartialData::FixContentLength(HttpResponseHeaders* headers) { | |
| 433 headers->RemoveHeader(kLengthHeader); | |
| 434 headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader, | |
| 435 resource_size_)); | |
| 436 } | |
| 437 | |
| 438 int PartialData::CacheRead( | |
| 439 disk_cache::Entry* entry, IOBuffer* data, int data_len, | |
| 440 const net::CompletionCallback& callback) { | |
| 441 int read_len = std::min(data_len, cached_min_len_); | |
| 442 if (!read_len) | |
| 443 return 0; | |
| 444 | |
| 445 int rv = 0; | |
| 446 if (sparse_entry_) { | |
| 447 rv = entry->ReadSparseData(current_range_start_, data, read_len, | |
| 448 callback); | |
| 449 } else { | |
| 450 if (current_range_start_ > kint32max) | |
| 451 return ERR_INVALID_ARGUMENT; | |
| 452 | |
| 453 rv = entry->ReadData(kDataStream, static_cast<int>(current_range_start_), | |
| 454 data, read_len, callback); | |
| 455 } | |
| 456 return rv; | |
| 457 } | |
| 458 | |
| 459 int PartialData::CacheWrite( | |
| 460 disk_cache::Entry* entry, IOBuffer* data, int data_len, | |
| 461 const net::CompletionCallback& callback) { | |
| 462 DVLOG(3) << "To write: " << data_len; | |
| 463 if (sparse_entry_) { | |
| 464 return entry->WriteSparseData( | |
| 465 current_range_start_, data, data_len, callback); | |
| 466 } else { | |
| 467 if (current_range_start_ > kint32max) | |
| 468 return ERR_INVALID_ARGUMENT; | |
| 469 | |
| 470 return entry->WriteData(kDataStream, static_cast<int>(current_range_start_), | |
| 471 data, data_len, callback, true); | |
| 472 } | |
| 473 } | |
| 474 | |
| 475 void PartialData::OnCacheReadCompleted(int result) { | |
| 476 DVLOG(3) << "Read: " << result; | |
| 477 if (result > 0) { | |
| 478 current_range_start_ += result; | |
| 479 cached_min_len_ -= result; | |
| 480 DCHECK_GE(cached_min_len_, 0); | |
| 481 } | |
| 482 } | |
| 483 | |
| 484 void PartialData::OnNetworkReadCompleted(int result) { | |
| 485 if (result > 0) | |
| 486 current_range_start_ += result; | |
| 487 } | |
| 488 | |
| 489 int PartialData::GetNextRangeLen() { | |
| 490 int64 range_len = | |
| 491 byte_range_.HasLastBytePosition() ? | |
| 492 byte_range_.last_byte_position() - current_range_start_ + 1 : | |
| 493 kint32max; | |
| 494 if (range_len > kint32max) | |
| 495 range_len = kint32max; | |
| 496 return static_cast<int32>(range_len); | |
| 497 } | |
| 498 | |
| 499 void PartialData::GetAvailableRangeCompleted(int result, int64 start) { | |
| 500 DCHECK(!callback_.is_null()); | |
| 501 DCHECK_NE(ERR_IO_PENDING, result); | |
| 502 | |
| 503 cached_start_ = start; | |
| 504 cached_min_len_ = result; | |
| 505 if (result >= 0) | |
| 506 result = 1; // Return success, go ahead and validate the entry. | |
| 507 | |
| 508 CompletionCallback cb = callback_; | |
| 509 callback_.Reset(); | |
| 510 cb.Run(result); | |
| 511 } | |
| 512 | |
| 513 } // namespace net | |
| OLD | NEW |