Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(176)

Side by Side Diff: net/http/partial_data.cc

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

Powered by Google App Engine
This is Rietveld 408576698