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