1 // Copyright (c) 2009 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_util.h" 10 #include "net/base/net_errors.h" 11 #include "net/disk_cache/disk_cache.h" 12 #include "net/http/http_response_headers.h" 13 #include "net/http/http_util.h" 14 15 namespace { 16 17 // The headers that we have to process. 18 const char kLengthHeader[] = "Content-Length"; 19 const char kRangeHeader[] = "Content-Range"; 20 const int kDataStream = 1; 21 22 } 23 24 namespace net { 25 26 bool PartialData::Init(const std::string& headers) { 27 std::vector<HttpByteRange> ranges; 28 if (!HttpUtil::ParseRanges(headers, &ranges) || ranges.size() != 1) 29 return false; 30 31 // We can handle this range request. 32 byte_range_ = ranges[0]; 33 if (!byte_range_.IsValid()) 34 return false; 35 36 resource_size_ = 0; 37 current_range_start_ = byte_range_.first_byte_position(); 38 return true; 39 } 40 41 void PartialData::SetHeaders(const std::string& headers) { 42 DCHECK(extra_headers_.empty()); 43 extra_headers_ = headers; 44 } 45 46 void PartialData::RestoreHeaders(std::string* headers) const { 47 DCHECK(current_range_start_ >= 0 || byte_range_.IsSuffixByteRange()); 48 int64 end = byte_range_.IsSuffixByteRange() ? 49 byte_range_.suffix_length() : byte_range_.last_byte_position(); 50 51 headers->assign(extra_headers_); 52 if (byte_range_.IsValid()) 53 AddRangeHeader(current_range_start_, end, headers); 54 } 55 56 int PartialData::PrepareCacheValidation(disk_cache::Entry* entry, 57 std::string* headers) { 58 DCHECK(current_range_start_ >= 0); 59 60 // Scan the disk cache for the first cached portion within this range. 61 int64 range_len = byte_range_.HasLastBytePosition() ? 62 byte_range_.last_byte_position() - current_range_start_ + 1 : kint32max; 63 if (range_len > kint32max) 64 range_len = kint32max; 65 int len = static_cast<int32>(range_len); 66 if (!len) 67 return 0; 68 range_present_ = false; 69 70 if (sparse_entry_) { 71 cached_min_len_ = entry->GetAvailableRange(current_range_start_, len, 72 &cached_start_); 73 } else if (truncated_) { 74 if (!current_range_start_) { 75 // Update the cached range only the first time. 76 cached_min_len_ = static_cast<int32>(byte_range_.first_byte_position()); 77 cached_start_ = 0; 78 } 79 } else { 80 cached_min_len_ = len; 81 cached_start_ = current_range_start_; 82 } 83 84 if (cached_min_len_ < 0) { 85 DCHECK(cached_min_len_ != ERR_IO_PENDING); 86 return cached_min_len_; 87 } 88 89 headers->assign(extra_headers_); 90 91 if (!cached_min_len_) { 92 // We don't have anything else stored. 93 final_range_ = true; 94 cached_start_ = 95 byte_range_.HasLastBytePosition() ? current_range_start_ + len : 0; 96 } 97 98 if (current_range_start_ == cached_start_) { 99 // The data lives in the cache. 100 range_present_ = true; 101 if (len == cached_min_len_) 102 final_range_ = true; 103 AddRangeHeader(current_range_start_, cached_start_ + cached_min_len_ - 1, 104 headers); 105 } else { 106 // This range is not in the cache. 107 AddRangeHeader(current_range_start_, cached_start_ - 1, headers); 108 } 109 110 // Return a positive number to indicate success (versus error or finished). 111 return 1; 112 } 113 114 bool PartialData::IsCurrentRangeCached() const { 115 return range_present_; 116 } 117 118 bool PartialData::IsLastRange() const { 119 return final_range_; 120 } 121 122 bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers, 123 disk_cache::Entry* entry, 124 bool truncated) { 125 resource_size_ = 0; 126 if (truncated) { 127 DCHECK_EQ(headers->response_code(), 200); 128 // We don't have the real length and the user may be trying to create a 129 // sparse entry so let's not write to this entry. 130 if (byte_range_.IsValid()) 131 return false; 132 133 // Now we avoid resume if there is no content length, but that was not 134 // always the case so double check here. 135 int64 total_length = headers->GetContentLength(); 136 if (total_length <= 0 || !headers->HasStrongValidators()) 137 return false; 138 139 truncated_ = true; 140 sparse_entry_ = false; 141 byte_range_.set_first_byte_position(entry->GetDataSize(kDataStream)); 142 resource_size_ = total_length; 143 current_range_start_ = 0; 144 return true; 145 } 146 147 if (headers->response_code() == 200) { 148 DCHECK(byte_range_.IsValid()); 149 sparse_entry_ = false; 150 resource_size_ = entry->GetDataSize(kDataStream); 151 return true; 152 } 153 154 int64 length_value = headers->GetContentLength(); 155 if (length_value <= 0) 156 return false; // We must have stored the resource length. 157 158 resource_size_ = length_value; 159 160 // Make sure that this is really a sparse entry. 161 int64 n; 162 if (ERR_CACHE_OPERATION_NOT_SUPPORTED == entry->GetAvailableRange(0, 5, &n)) 163 return false; 164 165 return true; 166 } 167 168 bool PartialData::IsRequestedRangeOK() { 169 if (byte_range_.IsValid()) { 170 if (truncated_) 171 return true; 172 if (!byte_range_.ComputeBounds(resource_size_)) 173 return false; 174 175 if (current_range_start_ < 0) 176 current_range_start_ = byte_range_.first_byte_position(); 177 } else { 178 // This is not a range request but we have partial data stored. 179 current_range_start_ = 0; 180 byte_range_.set_last_byte_position(resource_size_ - 1); 181 } 182 183 bool rv = current_range_start_ >= 0; 184 if (!rv) 185 current_range_start_ = 0; 186 187 return rv; 188 } 189 190 bool PartialData::ResponseHeadersOK(const HttpResponseHeaders* headers) { 191 if (headers->response_code() == 304) { 192 if (!byte_range_.IsValid() || truncated_) 193 return true; 194 195 // We must have a complete range here. 196 return byte_range_.HasFirstBytePosition() && 197 byte_range_.HasLastBytePosition(); 198 } 199 200 int64 start, end, total_length; 201 if (!headers->GetContentRange(&start, &end, &total_length)) 202 return false; 203 if (total_length <= 0) 204 return false; 205 206 int64 content_length = headers->GetContentLength(); 207 if (content_length < 0 || content_length != end - start + 1) 208 return false; 209 210 if (!resource_size_) { 211 // First response. Update our values with the ones provided by the server. 212 resource_size_ = total_length; 213 if (!byte_range_.HasFirstBytePosition()) { 214 byte_range_.set_first_byte_position(start); 215 current_range_start_ = start; 216 } 217 if (!byte_range_.HasLastBytePosition()) 218 byte_range_.set_last_byte_position(end); 219 } else if (resource_size_ != total_length) { 220 return false; 221 } 222 223 if (truncated_) { 224 if (!byte_range_.HasLastBytePosition()) 225 byte_range_.set_last_byte_position(end); 226 } 227 228 if (start != current_range_start_) 229 return false; 230 231 if (byte_range_.IsValid() && end > byte_range_.last_byte_position()) 232 return false; 233 234 return true; 235 } 236 237 // We are making multiple requests to complete the range requested by the user. 238 // Just assume that everything is fine and say that we are returning what was 239 // requested. 240 void PartialData::FixResponseHeaders(HttpResponseHeaders* headers) { 241 if (truncated_) 242 return; 243 244 headers->RemoveHeader(kLengthHeader); 245 headers->RemoveHeader(kRangeHeader); 246 247 int64 range_len; 248 if (byte_range_.IsValid()) { 249 if (!sparse_entry_) 250 headers->ReplaceStatusLine("HTTP/1.1 206 Partial Content"); 251 252 DCHECK(byte_range_.HasFirstBytePosition()); 253 DCHECK(byte_range_.HasLastBytePosition()); 254 headers->AddHeader( 255 StringPrintf("%s: bytes %" PRId64 "-%" PRId64 "/%" PRId64, 256 kRangeHeader, 257 byte_range_.first_byte_position(), 258 byte_range_.last_byte_position(), 259 resource_size_)); 260 range_len = byte_range_.last_byte_position() - 261 byte_range_.first_byte_position() + 1; 262 } else { 263 // TODO(rvargas): Is it safe to change the protocol version? 264 headers->ReplaceStatusLine("HTTP/1.1 200 OK"); 265 DCHECK_NE(resource_size_, 0); 266 range_len = resource_size_; 267 } 268 269 headers->AddHeader(StringPrintf("%s: %" PRId64, kLengthHeader, range_len)); 270 } 271 272 void PartialData::FixContentLength(HttpResponseHeaders* headers) { 273 headers->RemoveHeader(kLengthHeader); 274 headers->AddHeader(StringPrintf("%s: %" PRId64, kLengthHeader, 275 resource_size_)); 276 } 277 278 int PartialData::CacheRead(disk_cache::Entry* entry, IOBuffer* data, 279 int data_len, CompletionCallback* callback) { 280 int read_len = std::min(data_len, cached_min_len_); 281 if (!read_len) 282 return 0; 283 284 int rv = 0; 285 if (sparse_entry_) { 286 rv = entry->ReadSparseData(current_range_start_, data, read_len, 287 callback); 288 } else { 289 if (current_range_start_ > kint32max) 290 return ERR_INVALID_ARGUMENT; 291 292 rv = entry->ReadData(kDataStream, static_cast<int>(current_range_start_), 293 data, read_len, callback); 294 } 295 return rv; 296 } 297 298 int PartialData::CacheWrite(disk_cache::Entry* entry, IOBuffer* data, 299 int data_len, CompletionCallback* callback) { 300 if (sparse_entry_) { 301 return entry->WriteSparseData(current_range_start_, data, data_len, 302 callback); 303 } else { 304 if (current_range_start_ > kint32max) 305 return ERR_INVALID_ARGUMENT; 306 307 return entry->WriteData(kDataStream, static_cast<int>(current_range_start_), 308 data, data_len, callback, true); 309 } 310 } 311 312 void PartialData::OnCacheReadCompleted(int result) { 313 if (result > 0) { 314 current_range_start_ += result; 315 cached_min_len_ -= result; 316 DCHECK(cached_min_len_ >= 0); 317 } 318 } 319 320 void PartialData::OnNetworkReadCompleted(int result) { 321 if (result > 0) 322 current_range_start_ += result; 323 } 324 325 // Static. 326 void PartialData::AddRangeHeader(int64 start, int64 end, std::string* headers) { 327 DCHECK(start >= 0 || end >= 0); 328 std::string my_start, my_end; 329 if (start >= 0) 330 my_start = Int64ToString(start); 331 if (end >= 0) 332 my_end = Int64ToString(end); 333 334 headers->append(StringPrintf("Range: bytes=%s-%s\r\n", my_start.c_str(), 335 my_end.c_str())); 336 } 337 338 } // namespace net 339