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

Side by Side Diff: content/browser/download/download_request_core.cc

Issue 2660783002: Range request support for parallel download in DownloadRequestCore. (Closed)
Patch Set: Work on feedbacks. Created 3 years, 10 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 | « no previous file | content/browser/download/download_request_core_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/download/download_request_core.h" 5 #include "content/browser/download/download_request_core.h"
6 6
7 #include <string> 7 #include <string>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/callback_helpers.h" 10 #include "base/callback_helpers.h"
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after
151 else 151 else
152 load_flags |= net::LOAD_SKIP_CACHE_VALIDATION; 152 load_flags |= net::LOAD_SKIP_CACHE_VALIDATION;
153 } else { 153 } else {
154 load_flags |= net::LOAD_DISABLE_CACHE; 154 load_flags |= net::LOAD_DISABLE_CACHE;
155 } 155 }
156 request->SetLoadFlags(load_flags); 156 request->SetLoadFlags(load_flags);
157 157
158 bool has_last_modified = !params->last_modified().empty(); 158 bool has_last_modified = !params->last_modified().empty();
159 bool has_etag = !params->etag().empty(); 159 bool has_etag = !params->etag().empty();
160 160
161 // If we've asked for a range, we want to make sure that we only get that 161 // Strong validator(i.e. etag or last modified) is required in range requests
162 // range if our current copy of the information is good. We shouldn't be 162 // for download resumption and parallel download.
163 // asked to continue if we don't have a verifier. 163 DCHECK((params->offset() == 0 &&
164 DCHECK(params->offset() == 0 || has_etag || has_last_modified); 164 params->length() == DownloadSaveInfo::kLengthFullContent) ||
165 has_etag || has_last_modified);
165 166
166 // If we're not at the beginning of the file, retrieve only the remaining 167 // Add "Range" and "If-Range" request header fields if we ask for a range.
167 // portion. 168 if ((params->offset() > 0 ||
168 if (params->offset() > 0 && (has_etag || has_last_modified)) { 169 params->length() > DownloadSaveInfo::kLengthFullContent) &&
asanka 2017/02/01 22:51:17 This expression requires that kLengthFullContent b
xingliu 2017/02/02 05:46:36 Make sense, changed to != check in this file.
169 request->SetExtraRequestHeaderByName( 170 (has_etag || has_last_modified)) {
170 "Range", base::StringPrintf("bytes=%" PRId64 "-", params->offset()), 171 std::string range_header =
171 true); 172 params->length() > DownloadSaveInfo::kLengthFullContent
173 ? base::StringPrintf("bytes=%" PRId64 "-%" PRId64, params->offset(),
174 params->offset() + params->length() - 1)
175 : base::StringPrintf("bytes=%" PRId64 "-", params->offset());
172 176
173 // In accordance with RFC 2616 Section 14.27, use If-Range to specify that 177 request->SetExtraRequestHeaderByName("Range", range_header, true);
178
179 // In accordance with RFC 7233 Section 3.2, use If-Range to specify that
174 // the server return the entire entity if the validator doesn't match. 180 // the server return the entire entity if the validator doesn't match.
175 // Last-Modified can be used in the absence of ETag as a validator if the 181 // Last-Modified can be used in the absence of ETag as a validator if the
176 // response headers satisfied the HttpUtil::HasStrongValidators() predicate. 182 // response headers satisfied the HttpUtil::HasStrongValidators() predicate.
177 // 183 //
178 // This function assumes that HasStrongValidators() was true and that the 184 // This function assumes that HasStrongValidators() was true and that the
179 // ETag and Last-Modified header values supplied are valid. 185 // ETag and Last-Modified header values supplied are valid.
180 request->SetExtraRequestHeaderByName( 186 request->SetExtraRequestHeaderByName(
181 "If-Range", has_etag ? params->etag() : params->last_modified(), true); 187 "If-Range", has_etag ? params->etag() : params->last_modified(), true);
182 } 188 }
183 189
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after
297 303
298 if (!override_mime_type.empty()) 304 if (!override_mime_type.empty())
299 create_info->mime_type = override_mime_type; 305 create_info->mime_type = override_mime_type;
300 else 306 else
301 request()->GetMimeType(&create_info->mime_type); 307 request()->GetMimeType(&create_info->mime_type);
302 308
303 // Get the last modified time and etag. 309 // Get the last modified time and etag.
304 const net::HttpResponseHeaders* headers = request()->response_headers(); 310 const net::HttpResponseHeaders* headers = request()->response_headers();
305 if (headers) { 311 if (headers) {
306 if (headers->HasStrongValidators()) { 312 if (headers->HasStrongValidators()) {
307 // If we don't have strong validators as per RFC 2616 section 13.3.3, then 313 // If we don't have strong validators as per RFC 7232 section 2, then
308 // we neither store nor use them for range requests. 314 // we neither store nor use them for range requests.
309 if (!headers->EnumerateHeader(nullptr, "Last-Modified", 315 if (!headers->EnumerateHeader(nullptr, "Last-Modified",
310 &create_info->last_modified)) 316 &create_info->last_modified))
311 create_info->last_modified.clear(); 317 create_info->last_modified.clear();
312 if (!headers->EnumerateHeader(nullptr, "ETag", &create_info->etag)) 318 if (!headers->EnumerateHeader(nullptr, "ETag", &create_info->etag))
313 create_info->etag.clear(); 319 create_info->etag.clear();
314 } 320 }
315 321
316 // Grab the first content-disposition header. There may be more than one, 322 // Grab the first content-disposition header. There may be more than one,
317 // though as of this writing, the network stack ensures if there are, they 323 // though as of this writing, the network stack ensures if there are, they
(...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after
539 switch (http_headers.response_code()) { 545 switch (http_headers.response_code()) {
540 case -1: // Non-HTTP request. 546 case -1: // Non-HTTP request.
541 case net::HTTP_OK: 547 case net::HTTP_OK:
542 case net::HTTP_NON_AUTHORITATIVE_INFORMATION: 548 case net::HTTP_NON_AUTHORITATIVE_INFORMATION:
543 case net::HTTP_PARTIAL_CONTENT: 549 case net::HTTP_PARTIAL_CONTENT:
544 // Expected successful codes. 550 // Expected successful codes.
545 break; 551 break;
546 552
547 case net::HTTP_CREATED: 553 case net::HTTP_CREATED:
548 case net::HTTP_ACCEPTED: 554 case net::HTTP_ACCEPTED:
549 // Per RFC 2616 the entity being transferred is metadata about the 555 // Per RFC 7231 the entity being transferred is metadata about the
550 // resource at the target URL and not the resource at that URL (or the 556 // resource at the target URL and not the resource at that URL (or the
551 // resource that would be at the URL once processing is completed in the 557 // resource that would be at the URL once processing is completed in the
552 // case of HTTP_ACCEPTED). However, we currently don't have special 558 // case of HTTP_ACCEPTED). However, we currently don't have special
553 // handling for these response and they are downloaded the same as a 559 // handling for these response and they are downloaded the same as a
554 // regular response. 560 // regular response.
555 break; 561 break;
556 562
557 case net::HTTP_NO_CONTENT: 563 case net::HTTP_NO_CONTENT:
558 case net::HTTP_RESET_CONTENT: 564 case net::HTTP_RESET_CONTENT:
559 // These two status codes don't have an entity (or rather RFC 2616 565 // These two status codes don't have an entity (or rather RFC 7231
560 // requires that there be no entity). They are treated the same as the 566 // requires that there be no entity). They are treated the same as the
561 // resource not being found since there is no entity to download. 567 // resource not being found since there is no entity to download.
562 568
563 case net::HTTP_NOT_FOUND: 569 case net::HTTP_NOT_FOUND:
564 return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT; 570 return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
565 break; 571 break;
566 572
567 case net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE: 573 case net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE:
568 // Retry by downloading from the start automatically: 574 // Retry by downloading from the start automatically:
569 // If we haven't received data when we get this error, we won't. 575 // If we haven't received data when we get this error, we won't.
570 return DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE; 576 return DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE;
571 break; 577 break;
572 case net::HTTP_UNAUTHORIZED: 578 case net::HTTP_UNAUTHORIZED:
573 case net::HTTP_PROXY_AUTHENTICATION_REQUIRED: 579 case net::HTTP_PROXY_AUTHENTICATION_REQUIRED:
574 // Server didn't authorize this request. 580 // Server didn't authorize this request.
575 return DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED; 581 return DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED;
576 break; 582 break;
577 case net::HTTP_FORBIDDEN: 583 case net::HTTP_FORBIDDEN:
578 // Server forbids access to this resource. 584 // Server forbids access to this resource.
579 return DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN; 585 return DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN;
580 break; 586 break;
581 default: // All other errors. 587 default: // All other errors.
582 // Redirection and informational codes should have been handled earlier 588 // Redirection and informational codes should have been handled earlier
583 // in the stack. 589 // in the stack.
584 DCHECK_NE(3, http_headers.response_code() / 100); 590 DCHECK_NE(3, http_headers.response_code() / 100);
585 DCHECK_NE(1, http_headers.response_code() / 100); 591 DCHECK_NE(1, http_headers.response_code() / 100);
586 return DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED; 592 return DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED;
587 } 593 }
588 594
589 if (save_info && save_info->offset > 0) { 595 // The caller is expecting a partial response.
590 // The caller is expecting a partial response. 596 if (save_info && (save_info->offset > 0 || save_info->length > 0)) {
597 if (http_headers.response_code() != net::HTTP_PARTIAL_CONTENT) {
598 // Server should send partial content when we ask for a range with last
599 // byte position specified. e.g. "Range:bytes=50-99".
asanka 2017/02/01 22:51:17 Since we specified an If-Range header, we specific
xingliu 2017/02/02 05:46:36 Thanks for looking into RFC details here. Yeah, I
600 if (save_info->length > DownloadSaveInfo::kLengthFullContent)
601 return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
591 602
592 if (http_headers.response_code() != net::HTTP_PARTIAL_CONTENT) { 603 // Requested a partial range, but received the entire response, when
593 // Requested a partial range, but received the entire response. 604 // the range header is "Range:bytes={offset}-".
594 save_info->offset = 0; 605 save_info->offset = 0;
595 save_info->hash_of_partial_file.clear(); 606 save_info->hash_of_partial_file.clear();
596 save_info->hash_state.reset(); 607 save_info->hash_state.reset();
597 return DOWNLOAD_INTERRUPT_REASON_NONE; 608 return DOWNLOAD_INTERRUPT_REASON_NONE;
598 } 609 }
599 610
600 int64_t first_byte = -1; 611 int64_t first_byte = -1;
601 int64_t last_byte = -1; 612 int64_t last_byte = -1;
602 int64_t length = -1; 613 int64_t length = -1;
603 if (!http_headers.GetContentRangeFor206(&first_byte, &last_byte, &length)) 614 if (!http_headers.GetContentRangeFor206(&first_byte, &last_byte, &length))
604 return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT; 615 return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
605 DCHECK_GE(first_byte, 0); 616 DCHECK_GE(first_byte, 0);
606 617
607 if (first_byte != save_info->offset) { 618 if (first_byte != save_info->offset ||
619 (save_info->length > 0 &&
620 last_byte != save_info->offset + save_info->length - 1)) {
608 // The server returned a different range than the one we requested. Assume 621 // The server returned a different range than the one we requested. Assume
609 // the response is bad. 622 // the response is bad.
610 // 623 //
611 // In the future we should consider allowing offsets that are less than 624 // In the future we should consider allowing offsets that are less than
612 // the offset we've requested, since in theory we can truncate the partial 625 // the offset we've requested, since in theory we can truncate the partial
613 // file at the offset and continue. 626 // file at the offset and continue.
614 return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT; 627 return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
615 } 628 }
616 629
617 return DOWNLOAD_INTERRUPT_REASON_NONE; 630 return DOWNLOAD_INTERRUPT_REASON_NONE;
618 } 631 }
619 632
620 if (http_headers.response_code() == net::HTTP_PARTIAL_CONTENT) 633 if (http_headers.response_code() == net::HTTP_PARTIAL_CONTENT)
621 return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT; 634 return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
622 635
623 return DOWNLOAD_INTERRUPT_REASON_NONE; 636 return DOWNLOAD_INTERRUPT_REASON_NONE;
624 } 637 }
625 638
626 } // namespace content 639 } // namespace content
OLDNEW
« no previous file with comments | « no previous file | content/browser/download/download_request_core_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698