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