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 int64 length_value = headers->GetContentLength(); 284 if (length_value <= 0) 285 return false; // We must have stored the resource length. 286 287 resource_size_ = length_value; 288 289 // Make sure that this is really a sparse entry. 290 return entry->CouldBeSparse(); 291 } 292 293 void PartialData::SetRangeToStartDownload() { 294 DCHECK(truncated_); 295 DCHECK(!sparse_entry_); 296 current_range_start_ = 0; 297 cached_start_ = 0; 298 initial_validation_ = false; 299 } 300 301 bool PartialData::IsRequestedRangeOK() { 302 if (byte_range_.IsValid()) { 303 if (!byte_range_.ComputeBounds(resource_size_)) 304 return false; 305 if (truncated_) 306 return true; 307 308 if (current_range_start_ < 0) 309 current_range_start_ = byte_range_.first_byte_position(); 310 } else { 311 // This is not a range request but we have partial data stored. 312 current_range_start_ = 0; 313 byte_range_.set_last_byte_position(resource_size_ - 1); 314 } 315 316 bool rv = current_range_start_ >= 0; 317 if (!rv) 318 current_range_start_ = 0; 319 320 return rv; 321 } 322 323 bool PartialData::ResponseHeadersOK(const HttpResponseHeaders* headers) { 324 if (headers->response_code() == 304) { 325 if (!byte_range_.IsValid() || truncated_) 326 return true; 327 328 // We must have a complete range here. 329 return byte_range_.HasFirstBytePosition() && 330 byte_range_.HasLastBytePosition(); 331 } 332 333 int64 start, end, total_length; 334 if (!headers->GetContentRange(&start, &end, &total_length)) 335 return false; 336 if (total_length <= 0) 337 return false; 338 339 DCHECK_EQ(headers->response_code(), 206); 340 341 // A server should return a valid content length with a 206 (per the standard) 342 // but relax the requirement because some servers don't do that. 343 int64 content_length = headers->GetContentLength(); 344 if (content_length > 0 && content_length != end - start + 1) 345 return false; 346 347 if (!resource_size_) { 348 // First response. Update our values with the ones provided by the server. 349 resource_size_ = total_length; 350 if (!byte_range_.HasFirstBytePosition()) { 351 byte_range_.set_first_byte_position(start); 352 current_range_start_ = start; 353 } 354 if (!byte_range_.HasLastBytePosition()) 355 byte_range_.set_last_byte_position(end); 356 } else if (resource_size_ != total_length) { 357 return false; 358 } 359 360 if (truncated_) { 361 if (!byte_range_.HasLastBytePosition()) 362 byte_range_.set_last_byte_position(end); 363 } 364 365 if (start != current_range_start_) 366 return false; 367 368 if (byte_range_.IsValid() && end > byte_range_.last_byte_position()) 369 return false; 370 371 return true; 372 } 373 374 // We are making multiple requests to complete the range requested by the user. 375 // Just assume that everything is fine and say that we are returning what was 376 // requested. 377 void PartialData::FixResponseHeaders(HttpResponseHeaders* headers, 378 bool success) { 379 if (truncated_) 380 return; 381 382 if (byte_range_.IsValid() && success) { 383 headers->UpdateWithNewRange(byte_range_, resource_size_, !sparse_entry_); 384 return; 385 } 386 387 headers->RemoveHeader(kLengthHeader); 388 headers->RemoveHeader(kRangeHeader); 389 390 if (byte_range_.IsValid()) { 391 headers->ReplaceStatusLine("HTTP/1.1 416 Requested Range Not Satisfiable"); 392 headers->AddHeader(base::StringPrintf("%s: bytes 0-0/%" PRId64, 393 kRangeHeader, resource_size_)); 394 headers->AddHeader(base::StringPrintf("%s: 0", kLengthHeader)); 395 } else { 396 // TODO(rvargas): Is it safe to change the protocol version? 397 headers->ReplaceStatusLine("HTTP/1.1 200 OK"); 398 DCHECK_NE(resource_size_, 0); 399 headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader, 400 resource_size_)); 401 } 402 } 403 404 void PartialData::FixContentLength(HttpResponseHeaders* headers) { 405 headers->RemoveHeader(kLengthHeader); 406 headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader, 407 resource_size_)); 408 } 409 410 int PartialData::CacheRead( 411 disk_cache::Entry* entry, IOBuffer* data, int data_len, 412 const net::CompletionCallback& callback) { 413 int read_len = std::min(data_len, cached_min_len_); 414 if (!read_len) 415 return 0; 416 417 int rv = 0; 418 if (sparse_entry_) { 419 rv = entry->ReadSparseData(current_range_start_, data, read_len, 420 callback); 421 } else { 422 if (current_range_start_ > kint32max) 423 return ERR_INVALID_ARGUMENT; 424 425 rv = entry->ReadData(kDataStream, static_cast<int>(current_range_start_), 426 data, read_len, callback); 427 } 428 return rv; 429 } 430 431 int PartialData::CacheWrite( 432 disk_cache::Entry* entry, IOBuffer* data, int data_len, 433 const net::CompletionCallback& callback) { 434 DVLOG(3) << "To write: " << data_len; 435 if (sparse_entry_) { 436 return entry->WriteSparseData( 437 current_range_start_, data, data_len, callback); 438 } else { 439 if (current_range_start_ > kint32max) 440 return ERR_INVALID_ARGUMENT; 441 442 return entry->WriteData(kDataStream, static_cast<int>(current_range_start_), 443 data, data_len, callback, true); 444 } 445 } 446 447 void PartialData::OnCacheReadCompleted(int result) { 448 DVLOG(3) << "Read: " << result; 449 if (result > 0) { 450 current_range_start_ += result; 451 cached_min_len_ -= result; 452 DCHECK_GE(cached_min_len_, 0); 453 } 454 } 455 456 void PartialData::OnNetworkReadCompleted(int result) { 457 if (result > 0) 458 current_range_start_ += result; 459 } 460 461 int PartialData::GetNextRangeLen() { 462 int64 range_len = 463 byte_range_.HasLastBytePosition() ? 464 byte_range_.last_byte_position() - current_range_start_ + 1 : 465 kint32max; 466 if (range_len > kint32max) 467 range_len = kint32max; 468 return static_cast<int32>(range_len); 469 } 470 471 void PartialData::GetAvailableRangeCompleted(int result, int64 start) { 472 DCHECK(!callback_.is_null()); 473 DCHECK_NE(ERR_IO_PENDING, result); 474 475 cached_start_ = start; 476 cached_min_len_ = result; 477 if (result >= 0) 478 result = 1; // Return success, go ahead and validate the entry. 479 480 CompletionCallback cb = callback_; 481 callback_.Reset(); 482 cb.Run(result); 483 } 484 485 } // namespace net 486