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 |