[go: nahoru, domu]

blob: f991a956a962ffa373ccb8be2f3c1fd496f93cac [file] [log] [blame]
rvargas@google.com8a301142011-04-13 18:33:401// Copyright (c) 2011 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
erg@google.comb104b502010-10-18 20:21:31109PartialData::PartialData()
110 : range_present_(false),
111 final_range_(false),
112 sparse_entry_(true),
113 truncated_(false),
rvargas@google.com634739b2011-03-02 18:08:25114 initial_validation_(false),
erg@google.comb104b502010-10-18 20:21:31115 core_(NULL),
116 callback_(NULL) {
117}
118
rvargas@google.com034740a2010-06-11 17:16:48119PartialData::~PartialData() {
120 if (core_)
121 core_->Cancel();
122}
123
willchan@chromium.org8c76ae22010-04-20 22:15:43124bool PartialData::Init(const HttpRequestHeaders& headers) {
125 std::string range_header;
126 if (!headers.GetHeader(HttpRequestHeaders::kRange, &range_header))
127 return false;
128
rvargas@google.com8bf26f49a2009-06-12 17:35:50129 std::vector<HttpByteRange> ranges;
willchan@chromium.org8c76ae22010-04-20 22:15:43130 if (!HttpUtil::ParseRangeHeader(range_header, &ranges) || ranges.size() != 1)
rvargas@google.com8bf26f49a2009-06-12 17:35:50131 return false;
132
133 // We can handle this range request.
134 byte_range_ = ranges[0];
135 if (!byte_range_.IsValid())
136 return false;
137
rvargas@google.com95792eb12009-06-22 21:30:40138 resource_size_ = 0;
rvargas@google.com8bf26f49a2009-06-12 17:35:50139 current_range_start_ = byte_range_.first_byte_position();
rvargas@google.coma5c9d982010-10-12 20:48:02140
141 DVLOG(1) << "Range start: " << current_range_start_ << " end: " <<
142 byte_range_.last_byte_position();
rvargas@google.com8bf26f49a2009-06-12 17:35:50143 return true;
144}
145
willchan@chromium.org8c76ae22010-04-20 22:15:43146void PartialData::SetHeaders(const HttpRequestHeaders& headers) {
147 DCHECK(extra_headers_.IsEmpty());
148 extra_headers_.CopyFrom(headers);
rvargas@google.come75e8af2009-11-03 00:04:20149}
150
willchan@chromium.org8c76ae22010-04-20 22:15:43151void PartialData::RestoreHeaders(HttpRequestHeaders* headers) const {
rvargas@google.com67fe45c2009-06-24 17:44:57152 DCHECK(current_range_start_ >= 0 || byte_range_.IsSuffixByteRange());
153 int64 end = byte_range_.IsSuffixByteRange() ?
154 byte_range_.suffix_length() : byte_range_.last_byte_position();
rvargas@google.com8bf26f49a2009-06-12 17:35:50155
willchan@chromium.org8c76ae22010-04-20 22:15:43156 headers->CopyFrom(extra_headers_);
rvargas@google.com634739b2011-03-02 18:08:25157 if (!truncated_ && byte_range_.IsValid())
rvargas@google.coma189bce2009-12-01 01:59:12158 AddRangeHeader(current_range_start_, end, headers);
rvargas@google.com8bf26f49a2009-06-12 17:35:50159}
160
rvargas@google.com034740a2010-06-11 17:16:48161int PartialData::ShouldValidateCache(disk_cache::Entry* entry,
162 CompletionCallback* callback) {
163 DCHECK_GE(current_range_start_, 0);
rvargas@google.com8bf26f49a2009-06-12 17:35:50164
165 // Scan the disk cache for the first cached portion within this range.
rvargas@google.com034740a2010-06-11 17:16:48166 int len = GetNextRangeLen();
rvargas@google.com8bf26f49a2009-06-12 17:35:50167 if (!len)
168 return 0;
rvargas@google.com8bf26f49a2009-06-12 17:35:50169
rvargas@google.coma5c9d982010-10-12 20:48:02170 DVLOG(3) << "ShouldValidateCache len: " << len;
171
rvargas@google.come5dad132009-08-18 00:53:41172 if (sparse_entry_) {
rvargas@google.com034740a2010-06-11 17:16:48173 DCHECK(!callback_);
174 Core* core = Core::CreateCore(this);
175 cached_min_len_ = core->GetAvailableRange(entry, current_range_start_, len,
176 &cached_start_);
177
178 if (cached_min_len_ == ERR_IO_PENDING) {
179 callback_ = callback;
180 return ERR_IO_PENDING;
181 }
rvargas@google.com634739b2011-03-02 18:08:25182 } else if (!truncated_) {
rvargas@google.coma5c9d982010-10-12 20:48:02183 if (byte_range_.HasFirstBytePosition() &&
184 byte_range_.first_byte_position() >= resource_size_) {
185 // The caller should take care of this condition because we should have
186 // failed IsRequestedRangeOK(), but it's better to be consistent here.
187 len = 0;
188 }
rvargas@google.come5dad132009-08-18 00:53:41189 cached_min_len_ = len;
190 cached_start_ = current_range_start_;
191 }
192
rvargas@google.com034740a2010-06-11 17:16:48193 if (cached_min_len_ < 0)
rvargas@google.com8bf26f49a2009-06-12 17:35:50194 return cached_min_len_;
rvargas@google.com034740a2010-06-11 17:16:48195
196 // Return a positive number to indicate success (versus error or finished).
197 return 1;
198}
199
200void PartialData::PrepareCacheValidation(disk_cache::Entry* entry,
201 HttpRequestHeaders* headers) {
202 DCHECK_GE(current_range_start_, 0);
203 DCHECK_GE(cached_min_len_, 0);
204
205 int len = GetNextRangeLen();
206 DCHECK_NE(0, len);
207 range_present_ = false;
rvargas@google.com8bf26f49a2009-06-12 17:35:50208
willchan@chromium.org8c76ae22010-04-20 22:15:43209 headers->CopyFrom(extra_headers_);
rvargas@google.com8bf26f49a2009-06-12 17:35:50210
211 if (!cached_min_len_) {
212 // We don't have anything else stored.
213 final_range_ = true;
rvargas@google.com28accfe2009-09-04 23:36:33214 cached_start_ =
215 byte_range_.HasLastBytePosition() ? current_range_start_ + len : 0;
rvargas@google.com8bf26f49a2009-06-12 17:35:50216 }
217
218 if (current_range_start_ == cached_start_) {
219 // The data lives in the cache.
220 range_present_ = true;
221 if (len == cached_min_len_)
222 final_range_ = true;
223 AddRangeHeader(current_range_start_, cached_start_ + cached_min_len_ - 1,
224 headers);
225 } else {
226 // This range is not in the cache.
227 AddRangeHeader(current_range_start_, cached_start_ - 1, headers);
228 }
rvargas@google.com8bf26f49a2009-06-12 17:35:50229}
230
231bool PartialData::IsCurrentRangeCached() const {
232 return range_present_;
233}
234
235bool PartialData::IsLastRange() const {
236 return final_range_;
237}
238
rvargas@google.com44f873a62009-08-12 00:14:48239bool PartialData::UpdateFromStoredHeaders(const HttpResponseHeaders* headers,
rvargas@google.com28accfe2009-09-04 23:36:33240 disk_cache::Entry* entry,
241 bool truncated) {
rvargas@google.com67fe45c2009-06-24 17:44:57242 resource_size_ = 0;
rvargas@google.combd069d72011-05-19 01:11:11243 if (!headers->HasStrongValidators())
244 return false;
245
rvargas@google.com28accfe2009-09-04 23:36:33246 if (truncated) {
247 DCHECK_EQ(headers->response_code(), 200);
rvargas@google.com28accfe2009-09-04 23:36:33248 // We don't have the real length and the user may be trying to create a
249 // sparse entry so let's not write to this entry.
250 if (byte_range_.IsValid())
251 return false;
252
rvargas@google.comdbd39fb2010-01-08 01:13:36253 // Now we avoid resume if there is no content length, but that was not
254 // always the case so double check here.
255 int64 total_length = headers->GetContentLength();
rvargas@google.combd069d72011-05-19 01:11:11256 if (total_length <= 0)
rvargas@google.comdbd39fb2010-01-08 01:13:36257 return false;
258
hclam@chromium.orgecd8becb2009-10-02 17:57:45259 truncated_ = true;
rvargas@google.com634739b2011-03-02 18:08:25260 initial_validation_ = true;
hclam@chromium.orgecd8becb2009-10-02 17:57:45261 sparse_entry_ = false;
rvargas@google.com634739b2011-03-02 18:08:25262 int current_len = entry->GetDataSize(kDataStream);
263 byte_range_.set_first_byte_position(current_len);
rvargas@google.comdbd39fb2010-01-08 01:13:36264 resource_size_ = total_length;
rvargas@google.com634739b2011-03-02 18:08:25265 current_range_start_ = current_len;
266 cached_min_len_ = current_len;
267 cached_start_ = current_len + 1;
rvargas@google.com28accfe2009-09-04 23:36:33268 return true;
269 }
270
rvargas@google.come5dad132009-08-18 00:53:41271 if (headers->response_code() == 200) {
272 DCHECK(byte_range_.IsValid());
273 sparse_entry_ = false;
274 resource_size_ = entry->GetDataSize(kDataStream);
rvargas@google.coma5c9d982010-10-12 20:48:02275 DVLOG(2) << "UpdateFromStoredHeaders size: " << resource_size_;
rvargas@google.come5dad132009-08-18 00:53:41276 return true;
277 }
278
rvargas@google.com8a301142011-04-13 18:33:40279 if (!headers->HasStrongValidators())
280 return false;
281
rvargas@google.comdbd39fb2010-01-08 01:13:36282 int64 length_value = headers->GetContentLength();
283 if (length_value <= 0)
rvargas@google.com67fe45c2009-06-24 17:44:57284 return false; // We must have stored the resource length.
285
rvargas@google.comdbd39fb2010-01-08 01:13:36286 resource_size_ = length_value;
rvargas@google.com67fe45c2009-06-24 17:44:57287
rvargas@google.come5dad132009-08-18 00:53:41288 // Make sure that this is really a sparse entry.
rvargas@google.com034740a2010-06-11 17:16:48289 return entry->CouldBeSparse();
rvargas@google.come5dad132009-08-18 00:53:41290}
291
rvargas@google.com634739b2011-03-02 18:08:25292void PartialData::SetRangeToStartDownload() {
293 DCHECK(truncated_);
294 DCHECK(!sparse_entry_);
295 current_range_start_ = 0;
296 cached_start_ = 0;
297 initial_validation_ = false;
298}
299
rvargas@google.come5dad132009-08-18 00:53:41300bool PartialData::IsRequestedRangeOK() {
rvargas@google.com44f873a62009-08-12 00:14:48301 if (byte_range_.IsValid()) {
302 if (!byte_range_.ComputeBounds(resource_size_))
303 return false;
rvargas@google.com634739b2011-03-02 18:08:25304 if (truncated_)
305 return true;
rvargas@google.com67fe45c2009-06-24 17:44:57306
rvargas@google.com44f873a62009-08-12 00:14:48307 if (current_range_start_ < 0)
308 current_range_start_ = byte_range_.first_byte_position();
309 } else {
310 // This is not a range request but we have partial data stored.
311 current_range_start_ = 0;
312 byte_range_.set_last_byte_position(resource_size_ - 1);
313 }
314
rvargas@google.come5dad132009-08-18 00:53:41315 bool rv = current_range_start_ >= 0;
316 if (!rv)
317 current_range_start_ = 0;
rvargas@google.com67fe45c2009-06-24 17:44:57318
rvargas@google.come5dad132009-08-18 00:53:41319 return rv;
rvargas@google.com95792eb12009-06-22 21:30:40320}
321
322bool PartialData::ResponseHeadersOK(const HttpResponseHeaders* headers) {
rvargas@google.come5dad132009-08-18 00:53:41323 if (headers->response_code() == 304) {
hclam@chromium.orgd9adff2c2009-09-05 01:15:45324 if (!byte_range_.IsValid() || truncated_)
rvargas@google.com28accfe2009-09-04 23:36:33325 return true;
326
rvargas@google.come5dad132009-08-18 00:53:41327 // We must have a complete range here.
328 return byte_range_.HasFirstBytePosition() &&
cbentzel@chromium.org2227c692010-05-04 15:36:11329 byte_range_.HasLastBytePosition();
rvargas@google.come5dad132009-08-18 00:53:41330 }
331
rvargas@google.com95792eb12009-06-22 21:30:40332 int64 start, end, total_length;
333 if (!headers->GetContentRange(&start, &end, &total_length))
334 return false;
335 if (total_length <= 0)
336 return false;
337
rvargas@google.com7eab0d2262009-10-14 22:05:54338 int64 content_length = headers->GetContentLength();
cevans@chromium.orgc3695f52009-10-28 18:29:54339 if (content_length < 0 || content_length != end - start + 1)
rvargas@google.com7eab0d2262009-10-14 22:05:54340 return false;
341
rvargas@google.com95792eb12009-06-22 21:30:40342 if (!resource_size_) {
343 // First response. Update our values with the ones provided by the server.
344 resource_size_ = total_length;
rvargas@google.com67fe45c2009-06-24 17:44:57345 if (!byte_range_.HasFirstBytePosition()) {
rvargas@google.com95792eb12009-06-22 21:30:40346 byte_range_.set_first_byte_position(start);
rvargas@google.com67fe45c2009-06-24 17:44:57347 current_range_start_ = start;
348 }
rvargas@google.com95792eb12009-06-22 21:30:40349 if (!byte_range_.HasLastBytePosition())
350 byte_range_.set_last_byte_position(end);
351 } else if (resource_size_ != total_length) {
352 return false;
353 }
354
rvargas@google.comdbd39fb2010-01-08 01:13:36355 if (truncated_) {
356 if (!byte_range_.HasLastBytePosition())
357 byte_range_.set_last_byte_position(end);
358 }
359
rvargas@google.com95792eb12009-06-22 21:30:40360 if (start != current_range_start_)
361 return false;
362
rvargas@google.com44f873a62009-08-12 00:14:48363 if (byte_range_.IsValid() && end > byte_range_.last_byte_position())
rvargas@google.com95792eb12009-06-22 21:30:40364 return false;
365
366 return true;
367}
368
369// We are making multiple requests to complete the range requested by the user.
370// Just assume that everything is fine and say that we are returning what was
371// requested.
rvargas@google.coma5c9d982010-10-12 20:48:02372void PartialData::FixResponseHeaders(HttpResponseHeaders* headers,
373 bool success) {
rvargas@google.com28accfe2009-09-04 23:36:33374 if (truncated_)
375 return;
376
rvargas@google.com95792eb12009-06-22 21:30:40377 headers->RemoveHeader(kLengthHeader);
378 headers->RemoveHeader(kRangeHeader);
379
rvargas@google.coma5c9d982010-10-12 20:48:02380 int64 range_len, start, end;
rvargas@google.com44f873a62009-08-12 00:14:48381 if (byte_range_.IsValid()) {
rvargas@google.coma5c9d982010-10-12 20:48:02382 if (success) {
383 if (!sparse_entry_)
384 headers->ReplaceStatusLine("HTTP/1.1 206 Partial Content");
rvargas@google.come5dad132009-08-18 00:53:41385
rvargas@google.coma5c9d982010-10-12 20:48:02386 DCHECK(byte_range_.HasFirstBytePosition());
387 DCHECK(byte_range_.HasLastBytePosition());
388 start = byte_range_.first_byte_position();
389 end = byte_range_.last_byte_position();
390 range_len = end - start + 1;
391 } else {
392 headers->ReplaceStatusLine(
393 "HTTP/1.1 416 Requested Range Not Satisfiable");
394 start = 0;
395 end = 0;
396 range_len = 0;
397 }
398
evan@chromium.org34b2b002009-11-20 06:53:28399 headers->AddHeader(
tfarina@chromium.orgd8eb84242010-09-25 02:25:06400 base::StringPrintf("%s: bytes %" PRId64 "-%" PRId64 "/%" PRId64,
rvargas@google.coma5c9d982010-10-12 20:48:02401 kRangeHeader, start, end, resource_size_));
rvargas@google.com44f873a62009-08-12 00:14:48402 } else {
403 // TODO(rvargas): Is it safe to change the protocol version?
404 headers->ReplaceStatusLine("HTTP/1.1 200 OK");
405 DCHECK_NE(resource_size_, 0);
406 range_len = resource_size_;
407 }
rvargas@google.com95792eb12009-06-22 21:30:40408
tfarina@chromium.orgd8eb84242010-09-25 02:25:06409 headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader,
410 range_len));
rvargas@google.com95792eb12009-06-22 21:30:40411}
412
413void PartialData::FixContentLength(HttpResponseHeaders* headers) {
414 headers->RemoveHeader(kLengthHeader);
tfarina@chromium.orgd8eb84242010-09-25 02:25:06415 headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader,
416 resource_size_));
rvargas@google.com95792eb12009-06-22 21:30:40417}
418
rvargas@google.com8bf26f49a2009-06-12 17:35:50419int PartialData::CacheRead(disk_cache::Entry* entry, IOBuffer* data,
420 int data_len, CompletionCallback* callback) {
421 int read_len = std::min(data_len, cached_min_len_);
hclam@chromium.org8f28d632009-10-01 22:09:21422 if (!read_len)
423 return 0;
424
rvargas@google.come5dad132009-08-18 00:53:41425 int rv = 0;
426 if (sparse_entry_) {
427 rv = entry->ReadSparseData(current_range_start_, data, read_len,
428 callback);
429 } else {
430 if (current_range_start_ > kint32max)
431 return ERR_INVALID_ARGUMENT;
432
433 rv = entry->ReadData(kDataStream, static_cast<int>(current_range_start_),
434 data, read_len, callback);
435 }
rvargas@google.com8bf26f49a2009-06-12 17:35:50436 return rv;
437}
438
439int PartialData::CacheWrite(disk_cache::Entry* entry, IOBuffer* data,
cbentzel@chromium.org2227c692010-05-04 15:36:11440 int data_len, CompletionCallback* callback) {
rvargas@google.coma5c9d982010-10-12 20:48:02441 DVLOG(3) << "To write: " << data_len;
rvargas@google.come5dad132009-08-18 00:53:41442 if (sparse_entry_) {
443 return entry->WriteSparseData(current_range_start_, data, data_len,
444 callback);
445 } else {
446 if (current_range_start_ > kint32max)
447 return ERR_INVALID_ARGUMENT;
448
449 return entry->WriteData(kDataStream, static_cast<int>(current_range_start_),
rvargas@google.com28accfe2009-09-04 23:36:33450 data, data_len, callback, true);
rvargas@google.come5dad132009-08-18 00:53:41451 }
rvargas@google.com8bf26f49a2009-06-12 17:35:50452}
453
454void PartialData::OnCacheReadCompleted(int result) {
rvargas@google.coma5c9d982010-10-12 20:48:02455 DVLOG(3) << "Read: " << result;
rvargas@google.com8bf26f49a2009-06-12 17:35:50456 if (result > 0) {
457 current_range_start_ += result;
458 cached_min_len_ -= result;
rvargas@google.com034740a2010-06-11 17:16:48459 DCHECK_GE(cached_min_len_, 0);
rvargas@google.com8bf26f49a2009-06-12 17:35:50460 }
461}
462
463void PartialData::OnNetworkReadCompleted(int result) {
464 if (result > 0)
465 current_range_start_ += result;
rvargas@google.com8bf26f49a2009-06-12 17:35:50466}
rvargas@google.com8bf26f49a2009-06-12 17:35:50467
rvargas@google.com034740a2010-06-11 17:16:48468int PartialData::GetNextRangeLen() {
469 int64 range_len =
470 byte_range_.HasLastBytePosition() ?
471 byte_range_.last_byte_position() - current_range_start_ + 1 :
472 kint32max;
473 if (range_len > kint32max)
474 range_len = kint32max;
475 return static_cast<int32>(range_len);
476}
477
478void PartialData::GetAvailableRangeCompleted(int result, int64 start) {
479 DCHECK(callback_);
480 DCHECK_NE(ERR_IO_PENDING, result);
481
482 cached_start_ = start;
483 cached_min_len_ = result;
484 if (result >= 0)
485 result = 1; // Return success, go ahead and validate the entry.
486
487 CompletionCallback* cb = callback_;
488 callback_ = NULL;
489 cb->Run(result);
490}
491
rvargas@google.com8bf26f49a2009-06-12 17:35:50492} // namespace net