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.
5 #include "net/http/partial_data.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"
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;
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
{
35 // Build a new core object. Lifetime management is automatic.
36 static Core
* CreateCore(PartialData
* owner
) {
37 return new Core(owner
);
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
,
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.
51 explicit Core(PartialData
* owner
);
54 // Pending io completion routine.
55 void OnIOComplete(int result
);
60 DISALLOW_COPY_AND_ASSIGN(Core
);
63 PartialData::Core::Core(PartialData
* owner
)
64 : owner_(owner
), start_(0) {
65 DCHECK(!owner_
->core_
);
69 PartialData::Core::~Core() {
74 void PartialData::Core::Cancel() {
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.
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"));
99 owner_
->GetAvailableRangeCompleted(result
, start_
);
103 // -----------------------------------------------------------------------------
105 PartialData::PartialData()
106 : current_range_start_(0),
107 current_range_end_(0),
111 range_present_(false),
115 initial_validation_(false),
119 PartialData::~PartialData() {
124 bool PartialData::Init(const HttpRequestHeaders
& headers
) {
125 std::string range_header
;
126 if (!headers
.GetHeader(HttpRequestHeaders::kRange
, &range_header
))
129 std::vector
<HttpByteRange
> ranges
;
130 if (!HttpUtil::ParseRangeHeader(range_header
, &ranges
) || ranges
.size() != 1)
133 // We can handle this range request.
134 byte_range_
= ranges
[0];
135 if (!byte_range_
.IsValid())
138 current_range_start_
= byte_range_
.first_byte_position();
140 DVLOG(1) << "Range start: " << current_range_start_
<< " end: " <<
141 byte_range_
.last_byte_position();
145 void PartialData::SetHeaders(const HttpRequestHeaders
& headers
) {
146 DCHECK(extra_headers_
.IsEmpty());
147 extra_headers_
.CopyFrom(headers
);
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();
155 headers
->CopyFrom(extra_headers_
);
156 if (truncated_
|| !byte_range_
.IsValid())
159 if (current_range_start_
< 0) {
160 headers
->SetHeader(HttpRequestHeaders::kRange
,
161 HttpByteRange::Suffix(end
).GetHeaderValue());
163 headers
->SetHeader(HttpRequestHeaders::kRange
,
164 HttpByteRange::Bounded(
165 current_range_start_
, end
).GetHeaderValue());
169 int PartialData::ShouldValidateCache(disk_cache::Entry
* entry
,
170 const CompletionCallback
& callback
) {
171 DCHECK_GE(current_range_start_
, 0);
173 // Scan the disk cache for the first cached portion within this range.
174 int len
= GetNextRangeLen();
178 DVLOG(3) << "ShouldValidateCache len: " << len
;
181 DCHECK(callback_
.is_null());
182 Core
* core
= Core::CreateCore(this);
183 cached_min_len_
= core
->GetAvailableRange(entry
, current_range_start_
, len
,
186 if (cached_min_len_
== ERR_IO_PENDING
) {
187 callback_
= callback
;
188 return ERR_IO_PENDING
;
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.
197 cached_min_len_
= len
;
198 cached_start_
= current_range_start_
;
201 if (cached_min_len_
< 0)
202 return cached_min_len_
;
204 // Return a positive number to indicate success (versus error or finished).
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);
213 int len
= GetNextRangeLen();
215 range_present_
= false;
217 headers
->CopyFrom(extra_headers_
);
219 if (!cached_min_len_
) {
220 // We don't have anything else stored.
223 byte_range_
.HasLastBytePosition() ? current_range_start_
+ len
: 0;
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_
)
233 HttpRequestHeaders::kRange
,
234 HttpByteRange::Bounded(current_range_start_
, current_range_end_
)
237 // This range is not in the cache.
238 current_range_end_
= cached_start_
- 1;
240 HttpRequestHeaders::kRange
,
241 HttpByteRange::Bounded(current_range_start_
, current_range_end_
)
246 bool PartialData::IsCurrentRangeCached() const {
247 return range_present_
;
250 bool PartialData::IsLastRange() const {
254 bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders
* headers
,
255 disk_cache::Entry
* entry
,
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())
265 if (!headers
->HasStrongValidators())
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)
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;
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_
;
294 if (!headers
->HasStrongValidators())
297 int64 length_value
= headers
->GetContentLength();
298 if (length_value
<= 0)
299 return false; // We must have stored the resource length.
301 resource_size_
= length_value
;
303 // Make sure that this is really a sparse entry.
304 return entry
->CouldBeSparse();
307 void PartialData::SetRangeToStartDownload() {
309 DCHECK(!sparse_entry_
);
310 current_range_start_
= 0;
312 initial_validation_
= false;
315 bool PartialData::IsRequestedRangeOK() {
316 if (byte_range_
.IsValid()) {
317 if (!byte_range_
.ComputeBounds(resource_size_
))
322 if (current_range_start_
< 0)
323 current_range_start_
= byte_range_
.first_byte_position();
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);
330 bool rv
= current_range_start_
>= 0;
332 current_range_start_
= 0;
337 bool PartialData::ResponseHeadersOK(const HttpResponseHeaders
* headers
) {
338 if (headers
->response_code() == 304) {
339 if (!byte_range_
.IsValid() || truncated_
)
342 // We must have a complete range here.
343 return byte_range_
.HasFirstBytePosition() &&
344 byte_range_
.HasLastBytePosition();
347 int64 start
, end
, total_length
;
348 if (!headers
->GetContentRange(&start
, &end
, &total_length
))
350 if (total_length
<= 0)
353 DCHECK_EQ(headers
->response_code(), 206);
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)
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
;
368 if (!byte_range_
.HasLastBytePosition())
369 byte_range_
.set_last_byte_position(end
);
370 } else if (resource_size_
!= total_length
) {
375 if (!byte_range_
.HasLastBytePosition())
376 byte_range_
.set_last_byte_position(end
);
379 if (start
!= current_range_start_
)
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
);
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_
)
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
405 void PartialData::FixResponseHeaders(HttpResponseHeaders
* headers
,
410 if (byte_range_
.IsValid() && success
) {
411 headers
->UpdateWithNewRange(byte_range_
, resource_size_
, !sparse_entry_
);
415 headers
->RemoveHeader(kLengthHeader
);
416 headers
->RemoveHeader(kRangeHeader
);
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
));
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
,
432 void PartialData::FixContentLength(HttpResponseHeaders
* headers
) {
433 headers
->RemoveHeader(kLengthHeader
);
434 headers
->AddHeader(base::StringPrintf("%s: %" PRId64
, kLengthHeader
,
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_
);
447 rv
= entry
->ReadSparseData(current_range_start_
, data
, read_len
,
450 if (current_range_start_
> kint32max
)
451 return ERR_INVALID_ARGUMENT
;
453 rv
= entry
->ReadData(kDataStream
, static_cast<int>(current_range_start_
),
454 data
, read_len
, callback
);
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
;
464 return entry
->WriteSparseData(
465 current_range_start_
, data
, data_len
, callback
);
467 if (current_range_start_
> kint32max
)
468 return ERR_INVALID_ARGUMENT
;
470 return entry
->WriteData(kDataStream
, static_cast<int>(current_range_start_
),
471 data
, data_len
, callback
, true);
475 void PartialData::OnCacheReadCompleted(int result
) {
476 DVLOG(3) << "Read: " << result
;
478 current_range_start_
+= result
;
479 cached_min_len_
-= result
;
480 DCHECK_GE(cached_min_len_
, 0);
484 void PartialData::OnNetworkReadCompleted(int result
) {
486 current_range_start_
+= result
;
489 int PartialData::GetNextRangeLen() {
491 byte_range_
.HasLastBytePosition() ?
492 byte_range_
.last_byte_position() - current_range_start_
+ 1 :
494 if (range_len
> kint32max
)
495 range_len
= kint32max
;
496 return static_cast<int32
>(range_len
);
499 void PartialData::GetAvailableRangeCompleted(int result
, int64 start
) {
500 DCHECK(!callback_
.is_null());
501 DCHECK_NE(ERR_IO_PENDING
, result
);
503 cached_start_
= start
;
504 cached_min_len_
= result
;
506 result
= 1; // Return success, go ahead and validate the entry.
508 CompletionCallback cb
= callback_
;