[go: nahoru, domu]

blob: 799c3faff35daaba4c7b057328581ef2c0667006 [file] [log] [blame]
brettw@chromium.org528c56d2010-07-30 19:28:441// Copyright (c) 2010 The Chromium Authors. All rights reserved.
rvargas@google.com8bf26f49a2009-06-12 17:35:502// 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
evan@chromium.org34b2b002009-11-20 06:53:287#include "base/format_macros.h"
rvargas@google.com8bf26f49a2009-06-12 17:35:508#include "base/logging.h"
brettw@chromium.org528c56d2010-07-30 19:28:449#include "base/string_number_conversions.h"
rvargas@google.com8bf26f49a2009-06-12 17:35:5010#include "base/string_util.h"
tfarina@chromium.orgd8eb84242010-09-25 02:25:0611#include "base/stringprintf.h"
rvargas@google.com8bf26f49a2009-06-12 17:35:5012#include "net/base/net_errors.h"
13#include "net/disk_cache/disk_cache.h"
rvargas@google.com95792eb12009-06-22 21:30:4014#include "net/http/http_response_headers.h"
rvargas@google.com8bf26f49a2009-06-12 17:35:5015#include "net/http/http_util.h"
rvargas@google.com95792eb12009-06-22 21:30:4016
willchan@chromium.org8c76ae22010-04-20 22:15:4317namespace net {
18
rvargas@google.com95792eb12009-06-22 21:30:4019namespace {
20
21// The headers that we have to process.
22const char kLengthHeader[] = "Content-Length";
23const char kRangeHeader[] = "Content-Range";
rvargas@google.come5dad132009-08-18 00:53:4124const int kDataStream = 1;
rvargas@google.com95792eb12009-06-22 21:30:4025
willchan@chromium.org8c76ae22010-04-20 22:15:4326void AddRangeHeader(int64 start, int64 end, HttpRequestHeaders* headers) {
27 DCHECK(start >= 0 || end >= 0);
28 std::string my_start, my_end;
29 if (start >= 0)
brettw@chromium.org528c56d2010-07-30 19:28:4430 my_start = base::Int64ToString(start);
willchan@chromium.org8c76ae22010-04-20 22:15:4331 if (end >= 0)
brettw@chromium.org528c56d2010-07-30 19:28:4432 my_end = base::Int64ToString(end);
willchan@chromium.org8c76ae22010-04-20 22:15:4333
34 headers->SetHeader(
35 HttpRequestHeaders::kRange,
tfarina@chromium.orgd8eb84242010-09-25 02:25:0636 base::StringPrintf("bytes=%s-%s", my_start.c_str(), my_end.c_str()));
rvargas@google.com95792eb12009-06-22 21:30:4037}
rvargas@google.com8bf26f49a2009-06-12 17:35:5038
willchan@chromium.org8c76ae22010-04-20 22:15:4339} // namespace
rvargas@google.com8bf26f49a2009-06-12 17:35:5040
rvargas@google.com034740a2010-06-11 17:16:4841// A core object that can be detached from the Partialdata object at destruction
42// so that asynchronous operations cleanup can be performed.
43class PartialData::Core {
44 public:
45 // Build a new core object. Lifetime management is automatic.
46 static Core* CreateCore(PartialData* owner) {
47 return new Core(owner);
48 }
49
50 // Wrapper for Entry::GetAvailableRange. If this method returns ERR_IO_PENDING
51 // PartialData::GetAvailableRangeCompleted() will be invoked on the owner
52 // object when finished (unless Cancel() is called first).
53 int GetAvailableRange(disk_cache::Entry* entry, int64 offset, int len,
54 int64* start);
55
56 // Cancels a pending operation. It is a mistake to call this method if there
57 // is no operation in progress; in fact, there will be no object to do so.
58 void Cancel();
59
60 private:
61 explicit Core(PartialData* owner);
62 ~Core();
63
64 // Pending io completion routine.
65 void OnIOComplete(int result);
66
67 PartialData* owner_;
68 int64 start_;
69 net::CompletionCallbackImpl<Core> callback_;
70 DISALLOW_COPY_AND_ASSIGN(Core);
71};
72
73PartialData::Core::Core(PartialData* owner)
74 : owner_(owner),
75 ALLOW_THIS_IN_INITIALIZER_LIST(callback_(this, &Core::OnIOComplete)) {
76 DCHECK(!owner_->core_);
77 owner_->core_ = this;
78}
79
80PartialData::Core::~Core() {
81 if (owner_)
82 owner_->core_ = NULL;
83}
84
85void PartialData::Core::Cancel() {
86 DCHECK(owner_);
87 owner_ = NULL;
88}
89
90int PartialData::Core::GetAvailableRange(disk_cache::Entry* entry, int64 offset,
91 int len, int64* start) {
92 int rv = entry->GetAvailableRange(offset, len, &start_, &callback_);
93 if (rv != net::ERR_IO_PENDING) {
94 // The callback will not be invoked. Lets cleanup.
95 *start = start_;
96 delete this;
97 }
98 return rv;
99}
100
101void PartialData::Core::OnIOComplete(int result) {
102 if (owner_)
103 owner_->GetAvailableRangeCompleted(result, start_);
104 delete this;
105}
106
107// -----------------------------------------------------------------------------
108
109PartialData::~PartialData() {
110 if (core_)
111 core_->Cancel();
112}
113
willchan@chromium.org8c76ae22010-04-20 22:15:43114bool PartialData::Init(const HttpRequestHeaders& headers) {
115 std::string range_header;
116 if (!headers.GetHeader(HttpRequestHeaders::kRange, &range_header))
117 return false;
118
rvargas@google.com8bf26f49a2009-06-12 17:35:50119 std::vector<HttpByteRange> ranges;
willchan@chromium.org8c76ae22010-04-20 22:15:43120 if (!HttpUtil::ParseRangeHeader(range_header, &ranges) || ranges.size() != 1)
rvargas@google.com8bf26f49a2009-06-12 17:35:50121 return false;
122
123 // We can handle this range request.
124 byte_range_ = ranges[0];
125 if (!byte_range_.IsValid())
126 return false;
127
rvargas@google.com95792eb12009-06-22 21:30:40128 resource_size_ = 0;
rvargas@google.com8bf26f49a2009-06-12 17:35:50129 current_range_start_ = byte_range_.first_byte_position();
rvargas@google.coma5c9d982010-10-12 20:48:02130
131 DVLOG(1) << "Range start: " << current_range_start_ << " end: " <<
132 byte_range_.last_byte_position();
rvargas@google.com8bf26f49a2009-06-12 17:35:50133 return true;
134}
135
willchan@chromium.org8c76ae22010-04-20 22:15:43136void PartialData::SetHeaders(const HttpRequestHeaders& headers) {
137 DCHECK(extra_headers_.IsEmpty());
138 extra_headers_.CopyFrom(headers);
rvargas@google.come75e8af2009-11-03 00:04:20139}
140
willchan@chromium.org8c76ae22010-04-20 22:15:43141void PartialData::RestoreHeaders(HttpRequestHeaders* headers) const {
rvargas@google.com67fe45c2009-06-24 17:44:57142 DCHECK(current_range_start_ >= 0 || byte_range_.IsSuffixByteRange());
143 int64 end = byte_range_.IsSuffixByteRange() ?
144 byte_range_.suffix_length() : byte_range_.last_byte_position();
rvargas@google.com8bf26f49a2009-06-12 17:35:50145
willchan@chromium.org8c76ae22010-04-20 22:15:43146 headers->CopyFrom(extra_headers_);
rvargas@google.coma189bce2009-12-01 01:59:12147 if (byte_range_.IsValid())
148 AddRangeHeader(current_range_start_, end, headers);
rvargas@google.com8bf26f49a2009-06-12 17:35:50149}
150
rvargas@google.com034740a2010-06-11 17:16:48151int PartialData::ShouldValidateCache(disk_cache::Entry* entry,
152 CompletionCallback* callback) {
153 DCHECK_GE(current_range_start_, 0);
rvargas@google.com8bf26f49a2009-06-12 17:35:50154
155 // Scan the disk cache for the first cached portion within this range.
rvargas@google.com034740a2010-06-11 17:16:48156 int len = GetNextRangeLen();
rvargas@google.com8bf26f49a2009-06-12 17:35:50157 if (!len)
158 return 0;
rvargas@google.com8bf26f49a2009-06-12 17:35:50159
rvargas@google.coma5c9d982010-10-12 20:48:02160 DVLOG(3) << "ShouldValidateCache len: " << len;
161
rvargas@google.come5dad132009-08-18 00:53:41162 if (sparse_entry_) {
rvargas@google.com034740a2010-06-11 17:16:48163 DCHECK(!callback_);
164 Core* core = Core::CreateCore(this);
165 cached_min_len_ = core->GetAvailableRange(entry, current_range_start_, len,
166 &cached_start_);
167
168 if (cached_min_len_ == ERR_IO_PENDING) {
169 callback_ = callback;
170 return ERR_IO_PENDING;
171 }
rvargas@google.com28accfe2009-09-04 23:36:33172 } else if (truncated_) {
173 if (!current_range_start_) {
174 // Update the cached range only the first time.
175 cached_min_len_ = static_cast<int32>(byte_range_.first_byte_position());
176 cached_start_ = 0;
177 }
rvargas@google.come5dad132009-08-18 00:53:41178 } else {
rvargas@google.coma5c9d982010-10-12 20:48:02179 if (byte_range_.HasFirstBytePosition() &&
180 byte_range_.first_byte_position() >= resource_size_) {
181 // The caller should take care of this condition because we should have
182 // failed IsRequestedRangeOK(), but it's better to be consistent here.
183 len = 0;
184 }
rvargas@google.come5dad132009-08-18 00:53:41185 cached_min_len_ = len;
186 cached_start_ = current_range_start_;
187 }
188
rvargas@google.com034740a2010-06-11 17:16:48189 if (cached_min_len_ < 0)
rvargas@google.com8bf26f49a2009-06-12 17:35:50190 return cached_min_len_;
rvargas@google.com034740a2010-06-11 17:16:48191
192 // Return a positive number to indicate success (versus error or finished).
193 return 1;
194}
195
196void PartialData::PrepareCacheValidation(disk_cache::Entry* entry,
197 HttpRequestHeaders* headers) {
198 DCHECK_GE(current_range_start_, 0);
199 DCHECK_GE(cached_min_len_, 0);
200
201 int len = GetNextRangeLen();
202 DCHECK_NE(0, len);
203 range_present_ = false;
rvargas@google.com8bf26f49a2009-06-12 17:35:50204
willchan@chromium.org8c76ae22010-04-20 22:15:43205 headers->CopyFrom(extra_headers_);
rvargas@google.com8bf26f49a2009-06-12 17:35:50206
207 if (!cached_min_len_) {
208 // We don't have anything else stored.
209 final_range_ = true;
rvargas@google.com28accfe2009-09-04 23:36:33210 cached_start_ =
211 byte_range_.HasLastBytePosition() ? current_range_start_ + len : 0;
rvargas@google.com8bf26f49a2009-06-12 17:35:50212 }
213
214 if (current_range_start_ == cached_start_) {
215 // The data lives in the cache.
216 range_present_ = true;
217 if (len == cached_min_len_)
218 final_range_ = true;
219 AddRangeHeader(current_range_start_, cached_start_ + cached_min_len_ - 1,
220 headers);
221 } else {
222 // This range is not in the cache.
223 AddRangeHeader(current_range_start_, cached_start_ - 1, headers);
224 }
rvargas@google.com8bf26f49a2009-06-12 17:35:50225}
226
227bool PartialData::IsCurrentRangeCached() const {
228 return range_present_;
229}
230
231bool PartialData::IsLastRange() const {
232 return final_range_;
233}
234
rvargas@google.com44f873a62009-08-12 00:14:48235bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers,
rvargas@google.com28accfe2009-09-04 23:36:33236 disk_cache::Entry* entry,
237 bool truncated) {
rvargas@google.com67fe45c2009-06-24 17:44:57238 resource_size_ = 0;
rvargas@google.com28accfe2009-09-04 23:36:33239 if (truncated) {
240 DCHECK_EQ(headers->response_code(), 200);
rvargas@google.com28accfe2009-09-04 23:36:33241 // We don't have the real length and the user may be trying to create a
242 // sparse entry so let's not write to this entry.
243 if (byte_range_.IsValid())
244 return false;
245
rvargas@google.comdbd39fb2010-01-08 01:13:36246 // Now we avoid resume if there is no content length, but that was not
247 // always the case so double check here.
248 int64 total_length = headers->GetContentLength();
249 if (total_length <= 0 || !headers->HasStrongValidators())
250 return false;
251
hclam@chromium.orgecd8becb2009-10-02 17:57:45252 truncated_ = true;
253 sparse_entry_ = false;
rvargas@google.com28accfe2009-09-04 23:36:33254 byte_range_.set_first_byte_position(entry->GetDataSize(kDataStream));
rvargas@google.comdbd39fb2010-01-08 01:13:36255 resource_size_ = total_length;
rvargas@google.com28accfe2009-09-04 23:36:33256 current_range_start_ = 0;
257 return true;
258 }
259
rvargas@google.come5dad132009-08-18 00:53:41260 if (headers->response_code() == 200) {
261 DCHECK(byte_range_.IsValid());
262 sparse_entry_ = false;
263 resource_size_ = entry->GetDataSize(kDataStream);
rvargas@google.coma5c9d982010-10-12 20:48:02264 DVLOG(2) << "UpdateFromStoredHeaders size: " << resource_size_;
rvargas@google.come5dad132009-08-18 00:53:41265 return true;
266 }
267
rvargas@google.comdbd39fb2010-01-08 01:13:36268 int64 length_value = headers->GetContentLength();
269 if (length_value <= 0)
rvargas@google.com67fe45c2009-06-24 17:44:57270 return false; // We must have stored the resource length.
271
rvargas@google.comdbd39fb2010-01-08 01:13:36272 resource_size_ = length_value;
rvargas@google.com67fe45c2009-06-24 17:44:57273
rvargas@google.come5dad132009-08-18 00:53:41274 // Make sure that this is really a sparse entry.
rvargas@google.com034740a2010-06-11 17:16:48275 return entry->CouldBeSparse();
rvargas@google.come5dad132009-08-18 00:53:41276}
277
278bool PartialData::IsRequestedRangeOK() {
rvargas@google.com44f873a62009-08-12 00:14:48279 if (byte_range_.IsValid()) {
rvargas@google.com28accfe2009-09-04 23:36:33280 if (truncated_)
281 return true;
rvargas@google.com44f873a62009-08-12 00:14:48282 if (!byte_range_.ComputeBounds(resource_size_))
283 return false;
rvargas@google.com67fe45c2009-06-24 17:44:57284
rvargas@google.com44f873a62009-08-12 00:14:48285 if (current_range_start_ < 0)
286 current_range_start_ = byte_range_.first_byte_position();
287 } else {
288 // This is not a range request but we have partial data stored.
289 current_range_start_ = 0;
290 byte_range_.set_last_byte_position(resource_size_ - 1);
291 }
292
rvargas@google.come5dad132009-08-18 00:53:41293 bool rv = current_range_start_ >= 0;
294 if (!rv)
295 current_range_start_ = 0;
rvargas@google.com67fe45c2009-06-24 17:44:57296
rvargas@google.come5dad132009-08-18 00:53:41297 return rv;
rvargas@google.com95792eb12009-06-22 21:30:40298}
299
300bool PartialData::ResponseHeadersOK(const HttpResponseHeaders* headers) {
rvargas@google.come5dad132009-08-18 00:53:41301 if (headers->response_code() == 304) {
hclam@chromium.orgd9adff2c2009-09-05 01:15:45302 if (!byte_range_.IsValid() || truncated_)
rvargas@google.com28accfe2009-09-04 23:36:33303 return true;
304
rvargas@google.come5dad132009-08-18 00:53:41305 // We must have a complete range here.
306 return byte_range_.HasFirstBytePosition() &&
cbentzel@chromium.org2227c692010-05-04 15:36:11307 byte_range_.HasLastBytePosition();
rvargas@google.come5dad132009-08-18 00:53:41308 }
309
rvargas@google.com95792eb12009-06-22 21:30:40310 int64 start, end, total_length;
311 if (!headers->GetContentRange(&start, &end, &total_length))
312 return false;
313 if (total_length <= 0)
314 return false;
315
rvargas@google.com7eab0d2262009-10-14 22:05:54316 int64 content_length = headers->GetContentLength();
cevans@chromium.orgc3695f52009-10-28 18:29:54317 if (content_length < 0 || content_length != end - start + 1)
rvargas@google.com7eab0d2262009-10-14 22:05:54318 return false;
319
rvargas@google.com95792eb12009-06-22 21:30:40320 if (!resource_size_) {
321 // First response. Update our values with the ones provided by the server.
322 resource_size_ = total_length;
rvargas@google.com67fe45c2009-06-24 17:44:57323 if (!byte_range_.HasFirstBytePosition()) {
rvargas@google.com95792eb12009-06-22 21:30:40324 byte_range_.set_first_byte_position(start);
rvargas@google.com67fe45c2009-06-24 17:44:57325 current_range_start_ = start;
326 }
rvargas@google.com95792eb12009-06-22 21:30:40327 if (!byte_range_.HasLastBytePosition())
328 byte_range_.set_last_byte_position(end);
329 } else if (resource_size_ != total_length) {
330 return false;
331 }
332
rvargas@google.comdbd39fb2010-01-08 01:13:36333 if (truncated_) {
334 if (!byte_range_.HasLastBytePosition())
335 byte_range_.set_last_byte_position(end);
336 }
337
rvargas@google.com95792eb12009-06-22 21:30:40338 if (start != current_range_start_)
339 return false;
340
rvargas@google.com44f873a62009-08-12 00:14:48341 if (byte_range_.IsValid() && end > byte_range_.last_byte_position())
rvargas@google.com95792eb12009-06-22 21:30:40342 return false;
343
344 return true;
345}
346
347// We are making multiple requests to complete the range requested by the user.
348// Just assume that everything is fine and say that we are returning what was
349// requested.
rvargas@google.coma5c9d982010-10-12 20:48:02350void PartialData::FixResponseHeaders(HttpResponseHeaders* headers,
351 bool success) {
rvargas@google.com28accfe2009-09-04 23:36:33352 if (truncated_)
353 return;
354
rvargas@google.com95792eb12009-06-22 21:30:40355 headers->RemoveHeader(kLengthHeader);
356 headers->RemoveHeader(kRangeHeader);
357
rvargas@google.coma5c9d982010-10-12 20:48:02358 int64 range_len, start, end;
rvargas@google.com44f873a62009-08-12 00:14:48359 if (byte_range_.IsValid()) {
rvargas@google.coma5c9d982010-10-12 20:48:02360 if (success) {
361 if (!sparse_entry_)
362 headers->ReplaceStatusLine("HTTP/1.1 206 Partial Content");
rvargas@google.come5dad132009-08-18 00:53:41363
rvargas@google.coma5c9d982010-10-12 20:48:02364 DCHECK(byte_range_.HasFirstBytePosition());
365 DCHECK(byte_range_.HasLastBytePosition());
366 start = byte_range_.first_byte_position();
367 end = byte_range_.last_byte_position();
368 range_len = end - start + 1;
369 } else {
370 headers->ReplaceStatusLine(
371 "HTTP/1.1 416 Requested Range Not Satisfiable");
372 start = 0;
373 end = 0;
374 range_len = 0;
375 }
376
evan@chromium.org34b2b002009-11-20 06:53:28377 headers->AddHeader(
tfarina@chromium.orgd8eb84242010-09-25 02:25:06378 base::StringPrintf("%s: bytes %" PRId64 "-%" PRId64 "/%" PRId64,
rvargas@google.coma5c9d982010-10-12 20:48:02379 kRangeHeader, start, end, resource_size_));
rvargas@google.com44f873a62009-08-12 00:14:48380 } else {
381 // TODO(rvargas): Is it safe to change the protocol version?
382 headers->ReplaceStatusLine("HTTP/1.1 200 OK");
383 DCHECK_NE(resource_size_, 0);
384 range_len = resource_size_;
385 }
rvargas@google.com95792eb12009-06-22 21:30:40386
tfarina@chromium.orgd8eb84242010-09-25 02:25:06387 headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader,
388 range_len));
rvargas@google.com95792eb12009-06-22 21:30:40389}
390
391void PartialData::FixContentLength(HttpResponseHeaders* headers) {
392 headers->RemoveHeader(kLengthHeader);
tfarina@chromium.orgd8eb84242010-09-25 02:25:06393 headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader,
394 resource_size_));
rvargas@google.com95792eb12009-06-22 21:30:40395}
396
rvargas@google.com8bf26f49a2009-06-12 17:35:50397int PartialData::CacheRead(disk_cache::Entry* entry, IOBuffer* data,
398 int data_len, CompletionCallback* callback) {
399 int read_len = std::min(data_len, cached_min_len_);
hclam@chromium.org8f28d632009-10-01 22:09:21400 if (!read_len)
401 return 0;
402
rvargas@google.come5dad132009-08-18 00:53:41403 int rv = 0;
404 if (sparse_entry_) {
405 rv = entry->ReadSparseData(current_range_start_, data, read_len,
406 callback);
407 } else {
408 if (current_range_start_ > kint32max)
409 return ERR_INVALID_ARGUMENT;
410
411 rv = entry->ReadData(kDataStream, static_cast<int>(current_range_start_),
412 data, read_len, callback);
413 }
rvargas@google.com8bf26f49a2009-06-12 17:35:50414 return rv;
415}
416
417int PartialData::CacheWrite(disk_cache::Entry* entry, IOBuffer* data,
cbentzel@chromium.org2227c692010-05-04 15:36:11418 int data_len, CompletionCallback* callback) {
rvargas@google.coma5c9d982010-10-12 20:48:02419 DVLOG(3) << "To write: " << data_len;
rvargas@google.come5dad132009-08-18 00:53:41420 if (sparse_entry_) {
421 return entry->WriteSparseData(current_range_start_, data, data_len,
422 callback);
423 } else {
424 if (current_range_start_ > kint32max)
425 return ERR_INVALID_ARGUMENT;
426
427 return entry->WriteData(kDataStream, static_cast<int>(current_range_start_),
rvargas@google.com28accfe2009-09-04 23:36:33428 data, data_len, callback, true);
rvargas@google.come5dad132009-08-18 00:53:41429 }
rvargas@google.com8bf26f49a2009-06-12 17:35:50430}
431
432void PartialData::OnCacheReadCompleted(int result) {
rvargas@google.coma5c9d982010-10-12 20:48:02433 DVLOG(3) << "Read: " << result;
rvargas@google.com8bf26f49a2009-06-12 17:35:50434 if (result > 0) {
435 current_range_start_ += result;
436 cached_min_len_ -= result;
rvargas@google.com034740a2010-06-11 17:16:48437 DCHECK_GE(cached_min_len_, 0);
rvargas@google.com8bf26f49a2009-06-12 17:35:50438 }
439}
440
441void PartialData::OnNetworkReadCompleted(int result) {
442 if (result > 0)
443 current_range_start_ += result;
rvargas@google.com8bf26f49a2009-06-12 17:35:50444}
rvargas@google.com8bf26f49a2009-06-12 17:35:50445
rvargas@google.com034740a2010-06-11 17:16:48446int PartialData::GetNextRangeLen() {
447 int64 range_len =
448 byte_range_.HasLastBytePosition() ?
449 byte_range_.last_byte_position() - current_range_start_ + 1 :
450 kint32max;
451 if (range_len > kint32max)
452 range_len = kint32max;
453 return static_cast<int32>(range_len);
454}
455
456void PartialData::GetAvailableRangeCompleted(int result, int64 start) {
457 DCHECK(callback_);
458 DCHECK_NE(ERR_IO_PENDING, result);
459
460 cached_start_ = start;
461 cached_min_len_ = result;
462 if (result >= 0)
463 result = 1; // Return success, go ahead and validate the entry.
464
465 CompletionCallback* cb = callback_;
466 callback_ = NULL;
467 cb->Run(result);
468}
469
rvargas@google.com8bf26f49a2009-06-12 17:35:50470} // namespace net