[go: nahoru, domu]

blob: 49d68d24bce207cfdcf410b0cfb57269431ae53b [file] [log] [blame]
rvargas@google.com034740a2010-06-11 17:16:481// Copyright (c) 2009-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"
9#include "base/string_util.h"
10#include "net/base/net_errors.h"
11#include "net/disk_cache/disk_cache.h"
rvargas@google.com95792eb12009-06-22 21:30:4012#include "net/http/http_response_headers.h"
rvargas@google.com8bf26f49a2009-06-12 17:35:5013#include "net/http/http_util.h"
rvargas@google.com95792eb12009-06-22 21:30:4014
willchan@chromium.org8c76ae22010-04-20 22:15:4315namespace net {
16
rvargas@google.com95792eb12009-06-22 21:30:4017namespace {
18
19// The headers that we have to process.
20const char kLengthHeader[] = "Content-Length";
21const char kRangeHeader[] = "Content-Range";
rvargas@google.come5dad132009-08-18 00:53:4122const int kDataStream = 1;
rvargas@google.com95792eb12009-06-22 21:30:4023
willchan@chromium.org8c76ae22010-04-20 22:15:4324void AddRangeHeader(int64 start, int64 end, HttpRequestHeaders* headers) {
25 DCHECK(start >= 0 || end >= 0);
26 std::string my_start, my_end;
27 if (start >= 0)
28 my_start = Int64ToString(start);
29 if (end >= 0)
30 my_end = Int64ToString(end);
31
32 headers->SetHeader(
33 HttpRequestHeaders::kRange,
34 StringPrintf("bytes=%s-%s", my_start.c_str(), my_end.c_str()));
rvargas@google.com95792eb12009-06-22 21:30:4035}
rvargas@google.com8bf26f49a2009-06-12 17:35:5036
willchan@chromium.org8c76ae22010-04-20 22:15:4337} // namespace
rvargas@google.com8bf26f49a2009-06-12 17:35:5038
rvargas@google.com034740a2010-06-11 17:16:4839// A core object that can be detached from the Partialdata object at destruction
40// so that asynchronous operations cleanup can be performed.
41class PartialData::Core {
42 public:
43 // Build a new core object. Lifetime management is automatic.
44 static Core* CreateCore(PartialData* owner) {
45 return new Core(owner);
46 }
47
48 // Wrapper for Entry::GetAvailableRange. If this method returns ERR_IO_PENDING
49 // PartialData::GetAvailableRangeCompleted() will be invoked on the owner
50 // object when finished (unless Cancel() is called first).
51 int GetAvailableRange(disk_cache::Entry* entry, int64 offset, int len,
52 int64* start);
53
54 // Cancels a pending operation. It is a mistake to call this method if there
55 // is no operation in progress; in fact, there will be no object to do so.
56 void Cancel();
57
58 private:
59 explicit Core(PartialData* owner);
60 ~Core();
61
62 // Pending io completion routine.
63 void OnIOComplete(int result);
64
65 PartialData* owner_;
66 int64 start_;
67 net::CompletionCallbackImpl<Core> callback_;
68 DISALLOW_COPY_AND_ASSIGN(Core);
69};
70
71PartialData::Core::Core(PartialData* owner)
72 : owner_(owner),
73 ALLOW_THIS_IN_INITIALIZER_LIST(callback_(this, &Core::OnIOComplete)) {
74 DCHECK(!owner_->core_);
75 owner_->core_ = this;
76}
77
78PartialData::Core::~Core() {
79 if (owner_)
80 owner_->core_ = NULL;
81}
82
83void PartialData::Core::Cancel() {
84 DCHECK(owner_);
85 owner_ = NULL;
86}
87
88int PartialData::Core::GetAvailableRange(disk_cache::Entry* entry, int64 offset,
89 int len, int64* start) {
90 int rv = entry->GetAvailableRange(offset, len, &start_, &callback_);
91 if (rv != net::ERR_IO_PENDING) {
92 // The callback will not be invoked. Lets cleanup.
93 *start = start_;
94 delete this;
95 }
96 return rv;
97}
98
99void PartialData::Core::OnIOComplete(int result) {
100 if (owner_)
101 owner_->GetAvailableRangeCompleted(result, start_);
102 delete this;
103}
104
105// -----------------------------------------------------------------------------
106
107PartialData::~PartialData() {
108 if (core_)
109 core_->Cancel();
110}
111
willchan@chromium.org8c76ae22010-04-20 22:15:43112bool PartialData::Init(const HttpRequestHeaders& headers) {
113 std::string range_header;
114 if (!headers.GetHeader(HttpRequestHeaders::kRange, &range_header))
115 return false;
116
rvargas@google.com8bf26f49a2009-06-12 17:35:50117 std::vector<HttpByteRange> ranges;
willchan@chromium.org8c76ae22010-04-20 22:15:43118 if (!HttpUtil::ParseRangeHeader(range_header, &ranges) || ranges.size() != 1)
rvargas@google.com8bf26f49a2009-06-12 17:35:50119 return false;
120
121 // We can handle this range request.
122 byte_range_ = ranges[0];
123 if (!byte_range_.IsValid())
124 return false;
125
rvargas@google.com95792eb12009-06-22 21:30:40126 resource_size_ = 0;
rvargas@google.com8bf26f49a2009-06-12 17:35:50127 current_range_start_ = byte_range_.first_byte_position();
128 return true;
129}
130
willchan@chromium.org8c76ae22010-04-20 22:15:43131void PartialData::SetHeaders(const HttpRequestHeaders& headers) {
132 DCHECK(extra_headers_.IsEmpty());
133 extra_headers_.CopyFrom(headers);
rvargas@google.come75e8af2009-11-03 00:04:20134}
135
willchan@chromium.org8c76ae22010-04-20 22:15:43136void PartialData::RestoreHeaders(HttpRequestHeaders* headers) const {
rvargas@google.com67fe45c2009-06-24 17:44:57137 DCHECK(current_range_start_ >= 0 || byte_range_.IsSuffixByteRange());
138 int64 end = byte_range_.IsSuffixByteRange() ?
139 byte_range_.suffix_length() : byte_range_.last_byte_position();
rvargas@google.com8bf26f49a2009-06-12 17:35:50140
willchan@chromium.org8c76ae22010-04-20 22:15:43141 headers->CopyFrom(extra_headers_);
rvargas@google.coma189bce2009-12-01 01:59:12142 if (byte_range_.IsValid())
143 AddRangeHeader(current_range_start_, end, headers);
rvargas@google.com8bf26f49a2009-06-12 17:35:50144}
145
rvargas@google.com034740a2010-06-11 17:16:48146int PartialData::ShouldValidateCache(disk_cache::Entry* entry,
147 CompletionCallback* callback) {
148 DCHECK_GE(current_range_start_, 0);
rvargas@google.com8bf26f49a2009-06-12 17:35:50149
150 // Scan the disk cache for the first cached portion within this range.
rvargas@google.com034740a2010-06-11 17:16:48151 int len = GetNextRangeLen();
rvargas@google.com8bf26f49a2009-06-12 17:35:50152 if (!len)
153 return 0;
rvargas@google.com8bf26f49a2009-06-12 17:35:50154
rvargas@google.come5dad132009-08-18 00:53:41155 if (sparse_entry_) {
rvargas@google.com034740a2010-06-11 17:16:48156 DCHECK(!callback_);
157 Core* core = Core::CreateCore(this);
158 cached_min_len_ = core->GetAvailableRange(entry, current_range_start_, len,
159 &cached_start_);
160
161 if (cached_min_len_ == ERR_IO_PENDING) {
162 callback_ = callback;
163 return ERR_IO_PENDING;
164 }
rvargas@google.com28accfe2009-09-04 23:36:33165 } else if (truncated_) {
166 if (!current_range_start_) {
167 // Update the cached range only the first time.
168 cached_min_len_ = static_cast<int32>(byte_range_.first_byte_position());
169 cached_start_ = 0;
170 }
rvargas@google.come5dad132009-08-18 00:53:41171 } else {
172 cached_min_len_ = len;
173 cached_start_ = current_range_start_;
174 }
175
rvargas@google.com034740a2010-06-11 17:16:48176 if (cached_min_len_ < 0)
rvargas@google.com8bf26f49a2009-06-12 17:35:50177 return cached_min_len_;
rvargas@google.com034740a2010-06-11 17:16:48178
179 // Return a positive number to indicate success (versus error or finished).
180 return 1;
181}
182
183void PartialData::PrepareCacheValidation(disk_cache::Entry* entry,
184 HttpRequestHeaders* headers) {
185 DCHECK_GE(current_range_start_, 0);
186 DCHECK_GE(cached_min_len_, 0);
187
188 int len = GetNextRangeLen();
189 DCHECK_NE(0, len);
190 range_present_ = false;
rvargas@google.com8bf26f49a2009-06-12 17:35:50191
willchan@chromium.org8c76ae22010-04-20 22:15:43192 headers->CopyFrom(extra_headers_);
rvargas@google.com8bf26f49a2009-06-12 17:35:50193
194 if (!cached_min_len_) {
195 // We don't have anything else stored.
196 final_range_ = true;
rvargas@google.com28accfe2009-09-04 23:36:33197 cached_start_ =
198 byte_range_.HasLastBytePosition() ? current_range_start_ + len : 0;
rvargas@google.com8bf26f49a2009-06-12 17:35:50199 }
200
201 if (current_range_start_ == cached_start_) {
202 // The data lives in the cache.
203 range_present_ = true;
204 if (len == cached_min_len_)
205 final_range_ = true;
206 AddRangeHeader(current_range_start_, cached_start_ + cached_min_len_ - 1,
207 headers);
208 } else {
209 // This range is not in the cache.
210 AddRangeHeader(current_range_start_, cached_start_ - 1, headers);
211 }
rvargas@google.com8bf26f49a2009-06-12 17:35:50212}
213
214bool PartialData::IsCurrentRangeCached() const {
215 return range_present_;
216}
217
218bool PartialData::IsLastRange() const {
219 return final_range_;
220}
221
rvargas@google.com44f873a62009-08-12 00:14:48222bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers,
rvargas@google.com28accfe2009-09-04 23:36:33223 disk_cache::Entry* entry,
224 bool truncated) {
rvargas@google.com67fe45c2009-06-24 17:44:57225 resource_size_ = 0;
rvargas@google.com28accfe2009-09-04 23:36:33226 if (truncated) {
227 DCHECK_EQ(headers->response_code(), 200);
rvargas@google.com28accfe2009-09-04 23:36:33228 // We don't have the real length and the user may be trying to create a
229 // sparse entry so let's not write to this entry.
230 if (byte_range_.IsValid())
231 return false;
232
rvargas@google.comdbd39fb2010-01-08 01:13:36233 // Now we avoid resume if there is no content length, but that was not
234 // always the case so double check here.
235 int64 total_length = headers->GetContentLength();
236 if (total_length <= 0 || !headers->HasStrongValidators())
237 return false;
238
hclam@chromium.orgecd8becb2009-10-02 17:57:45239 truncated_ = true;
240 sparse_entry_ = false;
rvargas@google.com28accfe2009-09-04 23:36:33241 byte_range_.set_first_byte_position(entry->GetDataSize(kDataStream));
rvargas@google.comdbd39fb2010-01-08 01:13:36242 resource_size_ = total_length;
rvargas@google.com28accfe2009-09-04 23:36:33243 current_range_start_ = 0;
244 return true;
245 }
246
rvargas@google.come5dad132009-08-18 00:53:41247 if (headers->response_code() == 200) {
248 DCHECK(byte_range_.IsValid());
249 sparse_entry_ = false;
250 resource_size_ = entry->GetDataSize(kDataStream);
251 return true;
252 }
253
rvargas@google.comdbd39fb2010-01-08 01:13:36254 int64 length_value = headers->GetContentLength();
255 if (length_value <= 0)
rvargas@google.com67fe45c2009-06-24 17:44:57256 return false; // We must have stored the resource length.
257
rvargas@google.comdbd39fb2010-01-08 01:13:36258 resource_size_ = length_value;
rvargas@google.com67fe45c2009-06-24 17:44:57259
rvargas@google.come5dad132009-08-18 00:53:41260 // Make sure that this is really a sparse entry.
rvargas@google.com034740a2010-06-11 17:16:48261 return entry->CouldBeSparse();
rvargas@google.come5dad132009-08-18 00:53:41262}
263
264bool PartialData::IsRequestedRangeOK() {
rvargas@google.com44f873a62009-08-12 00:14:48265 if (byte_range_.IsValid()) {
rvargas@google.com28accfe2009-09-04 23:36:33266 if (truncated_)
267 return true;
rvargas@google.com44f873a62009-08-12 00:14:48268 if (!byte_range_.ComputeBounds(resource_size_))
269 return false;
rvargas@google.com67fe45c2009-06-24 17:44:57270
rvargas@google.com44f873a62009-08-12 00:14:48271 if (current_range_start_ < 0)
272 current_range_start_ = byte_range_.first_byte_position();
273 } else {
274 // This is not a range request but we have partial data stored.
275 current_range_start_ = 0;
276 byte_range_.set_last_byte_position(resource_size_ - 1);
277 }
278
rvargas@google.come5dad132009-08-18 00:53:41279 bool rv = current_range_start_ >= 0;
280 if (!rv)
281 current_range_start_ = 0;
rvargas@google.com67fe45c2009-06-24 17:44:57282
rvargas@google.come5dad132009-08-18 00:53:41283 return rv;
rvargas@google.com95792eb12009-06-22 21:30:40284}
285
286bool PartialData::ResponseHeadersOK(const HttpResponseHeaders* headers) {
rvargas@google.come5dad132009-08-18 00:53:41287 if (headers->response_code() == 304) {
hclam@chromium.orgd9adff2c2009-09-05 01:15:45288 if (!byte_range_.IsValid() || truncated_)
rvargas@google.com28accfe2009-09-04 23:36:33289 return true;
290
rvargas@google.come5dad132009-08-18 00:53:41291 // We must have a complete range here.
292 return byte_range_.HasFirstBytePosition() &&
cbentzel@chromium.org2227c692010-05-04 15:36:11293 byte_range_.HasLastBytePosition();
rvargas@google.come5dad132009-08-18 00:53:41294 }
295
rvargas@google.com95792eb12009-06-22 21:30:40296 int64 start, end, total_length;
297 if (!headers->GetContentRange(&start, &end, &total_length))
298 return false;
299 if (total_length <= 0)
300 return false;
301
rvargas@google.com7eab0d2262009-10-14 22:05:54302 int64 content_length = headers->GetContentLength();
cevans@chromium.orgc3695f52009-10-28 18:29:54303 if (content_length < 0 || content_length != end - start + 1)
rvargas@google.com7eab0d2262009-10-14 22:05:54304 return false;
305
rvargas@google.com95792eb12009-06-22 21:30:40306 if (!resource_size_) {
307 // First response. Update our values with the ones provided by the server.
308 resource_size_ = total_length;
rvargas@google.com67fe45c2009-06-24 17:44:57309 if (!byte_range_.HasFirstBytePosition()) {
rvargas@google.com95792eb12009-06-22 21:30:40310 byte_range_.set_first_byte_position(start);
rvargas@google.com67fe45c2009-06-24 17:44:57311 current_range_start_ = start;
312 }
rvargas@google.com95792eb12009-06-22 21:30:40313 if (!byte_range_.HasLastBytePosition())
314 byte_range_.set_last_byte_position(end);
315 } else if (resource_size_ != total_length) {
316 return false;
317 }
318
rvargas@google.comdbd39fb2010-01-08 01:13:36319 if (truncated_) {
320 if (!byte_range_.HasLastBytePosition())
321 byte_range_.set_last_byte_position(end);
322 }
323
rvargas@google.com95792eb12009-06-22 21:30:40324 if (start != current_range_start_)
325 return false;
326
rvargas@google.com44f873a62009-08-12 00:14:48327 if (byte_range_.IsValid() && end > byte_range_.last_byte_position())
rvargas@google.com95792eb12009-06-22 21:30:40328 return false;
329
330 return true;
331}
332
333// We are making multiple requests to complete the range requested by the user.
334// Just assume that everything is fine and say that we are returning what was
335// requested.
336void PartialData::FixResponseHeaders(HttpResponseHeaders* headers) {
rvargas@google.com28accfe2009-09-04 23:36:33337 if (truncated_)
338 return;
339
rvargas@google.com95792eb12009-06-22 21:30:40340 headers->RemoveHeader(kLengthHeader);
341 headers->RemoveHeader(kRangeHeader);
342
rvargas@google.com44f873a62009-08-12 00:14:48343 int64 range_len;
344 if (byte_range_.IsValid()) {
rvargas@google.come5dad132009-08-18 00:53:41345 if (!sparse_entry_)
346 headers->ReplaceStatusLine("HTTP/1.1 206 Partial Content");
347
rvargas@google.com44f873a62009-08-12 00:14:48348 DCHECK(byte_range_.HasFirstBytePosition());
349 DCHECK(byte_range_.HasLastBytePosition());
evan@chromium.org34b2b002009-11-20 06:53:28350 headers->AddHeader(
351 StringPrintf("%s: bytes %" PRId64 "-%" PRId64 "/%" PRId64,
352 kRangeHeader,
353 byte_range_.first_byte_position(),
354 byte_range_.last_byte_position(),
355 resource_size_));
rvargas@google.com44f873a62009-08-12 00:14:48356 range_len = byte_range_.last_byte_position() -
357 byte_range_.first_byte_position() + 1;
358 } else {
359 // TODO(rvargas): Is it safe to change the protocol version?
360 headers->ReplaceStatusLine("HTTP/1.1 200 OK");
361 DCHECK_NE(resource_size_, 0);
362 range_len = resource_size_;
363 }
rvargas@google.com95792eb12009-06-22 21:30:40364
evan@chromium.org34b2b002009-11-20 06:53:28365 headers->AddHeader(StringPrintf("%s: %" PRId64, kLengthHeader, range_len));
rvargas@google.com95792eb12009-06-22 21:30:40366}
367
368void PartialData::FixContentLength(HttpResponseHeaders* headers) {
369 headers->RemoveHeader(kLengthHeader);
evan@chromium.org34b2b002009-11-20 06:53:28370 headers->AddHeader(StringPrintf("%s: %" PRId64, kLengthHeader,
371 resource_size_));
rvargas@google.com95792eb12009-06-22 21:30:40372}
373
rvargas@google.com8bf26f49a2009-06-12 17:35:50374int PartialData::CacheRead(disk_cache::Entry* entry, IOBuffer* data,
375 int data_len, CompletionCallback* callback) {
376 int read_len = std::min(data_len, cached_min_len_);
hclam@chromium.org8f28d632009-10-01 22:09:21377 if (!read_len)
378 return 0;
379
rvargas@google.come5dad132009-08-18 00:53:41380 int rv = 0;
381 if (sparse_entry_) {
382 rv = entry->ReadSparseData(current_range_start_, data, read_len,
383 callback);
384 } else {
385 if (current_range_start_ > kint32max)
386 return ERR_INVALID_ARGUMENT;
387
388 rv = entry->ReadData(kDataStream, static_cast<int>(current_range_start_),
389 data, read_len, callback);
390 }
rvargas@google.com8bf26f49a2009-06-12 17:35:50391 return rv;
392}
393
394int PartialData::CacheWrite(disk_cache::Entry* entry, IOBuffer* data,
cbentzel@chromium.org2227c692010-05-04 15:36:11395 int data_len, CompletionCallback* callback) {
rvargas@google.come5dad132009-08-18 00:53:41396 if (sparse_entry_) {
397 return entry->WriteSparseData(current_range_start_, data, data_len,
398 callback);
399 } else {
400 if (current_range_start_ > kint32max)
401 return ERR_INVALID_ARGUMENT;
402
403 return entry->WriteData(kDataStream, static_cast<int>(current_range_start_),
rvargas@google.com28accfe2009-09-04 23:36:33404 data, data_len, callback, true);
rvargas@google.come5dad132009-08-18 00:53:41405 }
rvargas@google.com8bf26f49a2009-06-12 17:35:50406}
407
408void PartialData::OnCacheReadCompleted(int result) {
409 if (result > 0) {
410 current_range_start_ += result;
411 cached_min_len_ -= result;
rvargas@google.com034740a2010-06-11 17:16:48412 DCHECK_GE(cached_min_len_, 0);
rvargas@google.com8bf26f49a2009-06-12 17:35:50413 }
414}
415
416void PartialData::OnNetworkReadCompleted(int result) {
417 if (result > 0)
418 current_range_start_ += result;
rvargas@google.com8bf26f49a2009-06-12 17:35:50419}
rvargas@google.com8bf26f49a2009-06-12 17:35:50420
rvargas@google.com034740a2010-06-11 17:16:48421int PartialData::GetNextRangeLen() {
422 int64 range_len =
423 byte_range_.HasLastBytePosition() ?
424 byte_range_.last_byte_position() - current_range_start_ + 1 :
425 kint32max;
426 if (range_len > kint32max)
427 range_len = kint32max;
428 return static_cast<int32>(range_len);
429}
430
431void PartialData::GetAvailableRangeCompleted(int result, int64 start) {
432 DCHECK(callback_);
433 DCHECK_NE(ERR_IO_PENDING, result);
434
435 cached_start_ = start;
436 cached_min_len_ = result;
437 if (result >= 0)
438 result = 1; // Return success, go ahead and validate the entry.
439
440 CompletionCallback* cb = callback_;
441 callback_ = NULL;
442 cb->Run(result);
443}
444
rvargas@google.com8bf26f49a2009-06-12 17:35:50445} // namespace net