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