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/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"
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;
30 PartialData::PartialData()
31 : current_range_start_(0),
32 current_range_end_(0),
36 range_present_(false),
40 initial_validation_(false),
44 PartialData::~PartialData() {
47 bool PartialData::Init(const HttpRequestHeaders
& headers
) {
48 std::string range_header
;
49 if (!headers
.GetHeader(HttpRequestHeaders::kRange
, &range_header
))
52 std::vector
<HttpByteRange
> ranges
;
53 if (!HttpUtil::ParseRangeHeader(range_header
, &ranges
) || ranges
.size() != 1)
56 // We can handle this range request.
57 byte_range_
= ranges
[0];
58 if (!byte_range_
.IsValid())
61 current_range_start_
= byte_range_
.first_byte_position();
63 DVLOG(1) << "Range start: " << current_range_start_
<< " end: " <<
64 byte_range_
.last_byte_position();
68 void PartialData::SetHeaders(const HttpRequestHeaders
& headers
) {
69 DCHECK(extra_headers_
.IsEmpty());
70 extra_headers_
.CopyFrom(headers
);
73 void PartialData::RestoreHeaders(HttpRequestHeaders
* headers
) const {
74 DCHECK(current_range_start_
>= 0 || byte_range_
.IsSuffixByteRange());
75 int64 end
= byte_range_
.IsSuffixByteRange() ?
76 byte_range_
.suffix_length() : byte_range_
.last_byte_position();
78 headers
->CopyFrom(extra_headers_
);
79 if (truncated_
|| !byte_range_
.IsValid())
82 if (current_range_start_
< 0) {
83 headers
->SetHeader(HttpRequestHeaders::kRange
,
84 HttpByteRange::Suffix(end
).GetHeaderValue());
86 headers
->SetHeader(HttpRequestHeaders::kRange
,
87 HttpByteRange::Bounded(
88 current_range_start_
, end
).GetHeaderValue());
92 int PartialData::ShouldValidateCache(disk_cache::Entry
* entry
,
93 const CompletionCallback
& callback
) {
94 DCHECK_GE(current_range_start_
, 0);
96 // Scan the disk cache for the first cached portion within this range.
97 int len
= GetNextRangeLen();
101 DVLOG(3) << "ShouldValidateCache len: " << len
;
104 DCHECK(callback_
.is_null());
105 int64
* start
= new int64
;
106 // This callback now owns "start". We make sure to keep it
107 // in a local variable since we want to use it later.
108 CompletionCallback cb
=
109 base::Bind(&PartialData::GetAvailableRangeCompleted
,
110 weak_factory_
.GetWeakPtr(), base::Owned(start
));
112 entry
->GetAvailableRange(current_range_start_
, len
, start
, cb
);
114 if (cached_min_len_
== ERR_IO_PENDING
) {
115 callback_
= callback
;
116 return ERR_IO_PENDING
;
118 cached_start_
= *start
;
120 } else if (!truncated_
) {
121 if (byte_range_
.HasFirstBytePosition() &&
122 byte_range_
.first_byte_position() >= resource_size_
) {
123 // The caller should take care of this condition because we should have
124 // failed IsRequestedRangeOK(), but it's better to be consistent here.
127 cached_min_len_
= len
;
128 cached_start_
= current_range_start_
;
131 if (cached_min_len_
< 0)
132 return cached_min_len_
;
134 // Return a positive number to indicate success (versus error or finished).
138 void PartialData::PrepareCacheValidation(disk_cache::Entry
* entry
,
139 HttpRequestHeaders
* headers
) {
140 DCHECK_GE(current_range_start_
, 0);
141 DCHECK_GE(cached_min_len_
, 0);
143 int len
= GetNextRangeLen();
145 range_present_
= false;
147 headers
->CopyFrom(extra_headers_
);
149 if (!cached_min_len_
) {
150 // We don't have anything else stored.
153 byte_range_
.HasLastBytePosition() ? current_range_start_
+ len
: 0;
156 if (current_range_start_
== cached_start_
) {
157 // The data lives in the cache.
158 range_present_
= true;
159 current_range_end_
= cached_start_
+ cached_min_len_
- 1;
160 if (len
== cached_min_len_
)
163 // This range is not in the cache.
164 current_range_end_
= cached_start_
- 1;
167 HttpRequestHeaders::kRange
,
168 HttpByteRange::Bounded(current_range_start_
, current_range_end_
)
172 bool PartialData::IsCurrentRangeCached() const {
173 return range_present_
;
176 bool PartialData::IsLastRange() const {
180 bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders
* headers
,
181 disk_cache::Entry
* entry
,
185 DCHECK_EQ(headers
->response_code(), 200);
186 // We don't have the real length and the user may be trying to create a
187 // sparse entry so let's not write to this entry.
188 if (byte_range_
.IsValid())
191 if (!headers
->HasStrongValidators())
194 // Now we avoid resume if there is no content length, but that was not
195 // always the case so double check here.
196 int64 total_length
= headers
->GetContentLength();
197 if (total_length
<= 0)
201 initial_validation_
= true;
202 sparse_entry_
= false;
203 int current_len
= entry
->GetDataSize(kDataStream
);
204 byte_range_
.set_first_byte_position(current_len
);
205 resource_size_
= total_length
;
206 current_range_start_
= current_len
;
207 cached_min_len_
= current_len
;
208 cached_start_
= current_len
+ 1;
212 if (headers
->response_code() != 206) {
213 DCHECK(byte_range_
.IsValid());
214 sparse_entry_
= false;
215 resource_size_
= entry
->GetDataSize(kDataStream
);
216 DVLOG(2) << "UpdateFromStoredHeaders size: " << resource_size_
;
220 if (!headers
->HasStrongValidators())
223 int64 length_value
= headers
->GetContentLength();
224 if (length_value
<= 0)
225 return false; // We must have stored the resource length.
227 resource_size_
= length_value
;
229 // Make sure that this is really a sparse entry.
230 return entry
->CouldBeSparse();
233 void PartialData::SetRangeToStartDownload() {
235 DCHECK(!sparse_entry_
);
236 current_range_start_
= 0;
238 initial_validation_
= false;
241 bool PartialData::IsRequestedRangeOK() {
242 if (byte_range_
.IsValid()) {
243 if (!byte_range_
.ComputeBounds(resource_size_
))
248 if (current_range_start_
< 0)
249 current_range_start_
= byte_range_
.first_byte_position();
251 // This is not a range request but we have partial data stored.
252 current_range_start_
= 0;
253 byte_range_
.set_last_byte_position(resource_size_
- 1);
256 bool rv
= current_range_start_
>= 0;
258 current_range_start_
= 0;
263 bool PartialData::ResponseHeadersOK(const HttpResponseHeaders
* headers
) {
264 if (headers
->response_code() == 304) {
265 if (!byte_range_
.IsValid() || truncated_
)
268 // We must have a complete range here.
269 return byte_range_
.HasFirstBytePosition() &&
270 byte_range_
.HasLastBytePosition();
273 int64 start
, end
, total_length
;
274 if (!headers
->GetContentRange(&start
, &end
, &total_length
))
276 if (total_length
<= 0)
279 DCHECK_EQ(headers
->response_code(), 206);
281 // A server should return a valid content length with a 206 (per the standard)
282 // but relax the requirement because some servers don't do that.
283 int64 content_length
= headers
->GetContentLength();
284 if (content_length
> 0 && content_length
!= end
- start
+ 1)
287 if (!resource_size_
) {
288 // First response. Update our values with the ones provided by the server.
289 resource_size_
= total_length
;
290 if (!byte_range_
.HasFirstBytePosition()) {
291 byte_range_
.set_first_byte_position(start
);
292 current_range_start_
= start
;
294 if (!byte_range_
.HasLastBytePosition())
295 byte_range_
.set_last_byte_position(end
);
296 } else if (resource_size_
!= total_length
) {
301 if (!byte_range_
.HasLastBytePosition())
302 byte_range_
.set_last_byte_position(end
);
305 if (start
!= current_range_start_
)
308 if (!current_range_end_
) {
309 // There is nothing in the cache.
310 DCHECK(byte_range_
.HasLastBytePosition());
311 current_range_end_
= byte_range_
.last_byte_position();
312 if (current_range_end_
>= resource_size_
) {
313 // We didn't know the real file size, and the server is saying that the
314 // requested range goes beyond the size. Fix it.
315 current_range_end_
= end
;
316 byte_range_
.set_last_byte_position(end
);
320 // If we received a range, but it's not exactly the range we asked for, avoid
321 // trouble and signal an error.
322 if (end
!= current_range_end_
)
328 // We are making multiple requests to complete the range requested by the user.
329 // Just assume that everything is fine and say that we are returning what was
331 void PartialData::FixResponseHeaders(HttpResponseHeaders
* headers
,
336 if (byte_range_
.IsValid() && success
) {
337 headers
->UpdateWithNewRange(byte_range_
, resource_size_
, !sparse_entry_
);
341 headers
->RemoveHeader(kLengthHeader
);
342 headers
->RemoveHeader(kRangeHeader
);
344 if (byte_range_
.IsValid()) {
345 headers
->ReplaceStatusLine("HTTP/1.1 416 Requested Range Not Satisfiable");
346 headers
->AddHeader(base::StringPrintf("%s: bytes 0-0/%" PRId64
,
347 kRangeHeader
, resource_size_
));
348 headers
->AddHeader(base::StringPrintf("%s: 0", kLengthHeader
));
350 // TODO(rvargas): Is it safe to change the protocol version?
351 headers
->ReplaceStatusLine("HTTP/1.1 200 OK");
352 DCHECK_NE(resource_size_
, 0);
353 headers
->AddHeader(base::StringPrintf("%s: %" PRId64
, kLengthHeader
,
358 void PartialData::FixContentLength(HttpResponseHeaders
* headers
) {
359 headers
->RemoveHeader(kLengthHeader
);
360 headers
->AddHeader(base::StringPrintf("%s: %" PRId64
, kLengthHeader
,
364 int PartialData::CacheRead(disk_cache::Entry
* entry
,
367 const CompletionCallback
& callback
) {
368 int read_len
= std::min(data_len
, cached_min_len_
);
374 rv
= entry
->ReadSparseData(current_range_start_
, data
, read_len
,
377 if (current_range_start_
> kint32max
)
378 return ERR_INVALID_ARGUMENT
;
380 rv
= entry
->ReadData(kDataStream
, static_cast<int>(current_range_start_
),
381 data
, read_len
, callback
);
386 int PartialData::CacheWrite(disk_cache::Entry
* entry
,
389 const CompletionCallback
& callback
) {
390 DVLOG(3) << "To write: " << data_len
;
392 return entry
->WriteSparseData(
393 current_range_start_
, data
, data_len
, callback
);
395 if (current_range_start_
> kint32max
)
396 return ERR_INVALID_ARGUMENT
;
398 return entry
->WriteData(kDataStream
, static_cast<int>(current_range_start_
),
399 data
, data_len
, callback
, true);
403 void PartialData::OnCacheReadCompleted(int result
) {
404 DVLOG(3) << "Read: " << result
;
406 current_range_start_
+= result
;
407 cached_min_len_
-= result
;
408 DCHECK_GE(cached_min_len_
, 0);
412 void PartialData::OnNetworkReadCompleted(int result
) {
414 current_range_start_
+= result
;
417 int PartialData::GetNextRangeLen() {
419 byte_range_
.HasLastBytePosition() ?
420 byte_range_
.last_byte_position() - current_range_start_
+ 1 :
422 if (range_len
> kint32max
)
423 range_len
= kint32max
;
424 return static_cast<int32
>(range_len
);
427 void PartialData::GetAvailableRangeCompleted(int64
* start
, int result
) {
428 DCHECK(!callback_
.is_null());
429 DCHECK_NE(ERR_IO_PENDING
, result
);
431 cached_start_
= *start
;
432 cached_min_len_
= result
;
434 result
= 1; // Return success, go ahead and validate the entry.
436 CompletionCallback cb
= callback_
;