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

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 19 matching lines...) Expand all
30 #include "content/public/browser/download_manager_delegate.h" 30 #include "content/public/browser/download_manager_delegate.h"
31 #include "content/public/browser/navigation_entry.h" 31 #include "content/public/browser/navigation_entry.h"
32 #include "content/public/browser/resource_context.h" 32 #include "content/public/browser/resource_context.h"
33 #include "content/public/browser/web_contents.h" 33 #include "content/public/browser/web_contents.h"
34 #include "device/power_save_blocker/power_save_blocker.h" 34 #include "device/power_save_blocker/power_save_blocker.h"
35 #include "net/base/elements_upload_data_stream.h" 35 #include "net/base/elements_upload_data_stream.h"
36 #include "net/base/io_buffer.h" 36 #include "net/base/io_buffer.h"
37 #include "net/base/load_flags.h" 37 #include "net/base/load_flags.h"
38 #include "net/base/net_errors.h" 38 #include "net/base/net_errors.h"
39 #include "net/base/upload_bytes_element_reader.h" 39 #include "net/base/upload_bytes_element_reader.h"
40 #include "net/http/http_request_headers.h"
40 #include "net/http/http_response_headers.h" 41 #include "net/http/http_response_headers.h"
41 #include "net/http/http_status_code.h" 42 #include "net/http/http_status_code.h"
42 #include "net/url_request/url_request_context.h" 43 #include "net/url_request/url_request_context.h"
43 44
44 namespace content { 45 namespace content {
45 46
46 namespace { 47 namespace {
47 48
48 // This is a UserData::Data that will be attached to a URLRequest as a 49 // This is a UserData::Data that will be attached to a URLRequest as a
49 // side-channel for passing download parameters. 50 // side-channel for passing download parameters.
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
151 else 152 else
152 load_flags |= net::LOAD_SKIP_CACHE_VALIDATION; 153 load_flags |= net::LOAD_SKIP_CACHE_VALIDATION;
153 } else { 154 } else {
154 load_flags |= net::LOAD_DISABLE_CACHE; 155 load_flags |= net::LOAD_DISABLE_CACHE;
155 } 156 }
156 request->SetLoadFlags(load_flags); 157 request->SetLoadFlags(load_flags);
157 158
158 bool has_last_modified = !params->last_modified().empty(); 159 bool has_last_modified = !params->last_modified().empty();
159 bool has_etag = !params->etag().empty(); 160 bool has_etag = !params->etag().empty();
160 161
161 // If we've asked for a range, we want to make sure that we only get that 162 // 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 163 // for download resumption and parallel download.
163 // asked to continue if we don't have a verifier. 164 DCHECK((params->offset() == 0 &&
164 DCHECK(params->offset() == 0 || has_etag || has_last_modified); 165 params->length() == DownloadSaveInfo::kLengthFullContent) ||
166 has_etag || has_last_modified);
165 167
166 // If we're not at the beginning of the file, retrieve only the remaining 168 // Add "Range" and "If-Range" request header fields if the range request is to
167 // portion. 169 // retrieve bytes from {offset} to the end of the file.
168 if (params->offset() > 0 && (has_etag || has_last_modified)) { 170 // E.g. "Range:bytes=50-".
171 if (params->offset() > 0 &&
172 params->length() == DownloadSaveInfo::kLengthFullContent &&
173 (has_etag || has_last_modified)) {
169 request->SetExtraRequestHeaderByName( 174 request->SetExtraRequestHeaderByName(
170 "Range", base::StringPrintf("bytes=%" PRId64 "-", params->offset()), 175 net::HttpRequestHeaders::kRange,
171 true); 176 base::StringPrintf("bytes=%" PRId64 "-", params->offset()), true);
172 177
173 // In accordance with RFC 2616 Section 14.27, use If-Range to specify that 178 // 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. 179 // 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 180 // Last-Modified can be used in the absence of ETag as a validator if the
176 // response headers satisfied the HttpUtil::HasStrongValidators() predicate. 181 // response headers satisfied the HttpUtil::HasStrongValidators() predicate.
177 // 182 //
178 // This function assumes that HasStrongValidators() was true and that the 183 // This function assumes that HasStrongValidators() was true and that the
179 // ETag and Last-Modified header values supplied are valid. 184 // ETag and Last-Modified header values supplied are valid.
180 request->SetExtraRequestHeaderByName( 185 request->SetExtraRequestHeaderByName(
181 "If-Range", has_etag ? params->etag() : params->last_modified(), true); 186 net::HttpRequestHeaders::kIfRange,
187 has_etag ? params->etag() : params->last_modified(), true);
188 }
189
190 // Add "Range", "If-Match", and "If-Unmodified-Since" for range requests with
191 // last byte position specified. e.g. "Range:bytes=50-59". If the file is
192 // updated on the server, we should get http 412 response code.
193 if (params->length() != DownloadSaveInfo::kLengthFullContent &&
194 (has_etag || has_last_modified)) {
195 request->SetExtraRequestHeaderByName(
196 net::HttpRequestHeaders::kRange,
197 base::StringPrintf("bytes=%" PRId64 "-%" PRId64, params->offset(),
198 params->offset() + params->length() - 1),
199 true);
200 if (has_etag) {
201 request->SetExtraRequestHeaderByName(net::HttpRequestHeaders::kIfMatch,
202 params->etag(), true);
203 }
204 // According to RFC 7232 section 3.4, "If-Unmodified-Since" is mainly for
205 // old servers that didn't implement "If-Match" and must be ignored when
206 // "If-Match" presents.
207 if (has_last_modified) {
208 request->SetExtraRequestHeaderByName(
209 net::HttpRequestHeaders::kIfUnmodifiedSince, params->last_modified(),
210 true);
211 }
182 } 212 }
183 213
184 // Downloads are treated as top level navigations. Hence the first-party 214 // Downloads are treated as top level navigations. Hence the first-party
185 // origin for cookies is always based on the target URL and is updated on 215 // origin for cookies is always based on the target URL and is updated on
186 // redirects. 216 // redirects.
187 request->set_first_party_for_cookies(params->url()); 217 request->set_first_party_for_cookies(params->url());
188 request->set_first_party_url_policy( 218 request->set_first_party_url_policy(
189 net::URLRequest::UPDATE_FIRST_PARTY_URL_ON_REDIRECT); 219 net::URLRequest::UPDATE_FIRST_PARTY_URL_ON_REDIRECT);
190 request->set_initiator(params->initiator()); 220 request->set_initiator(params->initiator());
191 221
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after
297 327
298 if (!override_mime_type.empty()) 328 if (!override_mime_type.empty())
299 create_info->mime_type = override_mime_type; 329 create_info->mime_type = override_mime_type;
300 else 330 else
301 request()->GetMimeType(&create_info->mime_type); 331 request()->GetMimeType(&create_info->mime_type);
302 332
303 // Get the last modified time and etag. 333 // Get the last modified time and etag.
304 const net::HttpResponseHeaders* headers = request()->response_headers(); 334 const net::HttpResponseHeaders* headers = request()->response_headers();
305 if (headers) { 335 if (headers) {
306 if (headers->HasStrongValidators()) { 336 if (headers->HasStrongValidators()) {
307 // If we don't have strong validators as per RFC 2616 section 13.3.3, then 337 // If we don't have strong validators as per RFC 7232 section 2, then
308 // we neither store nor use them for range requests. 338 // we neither store nor use them for range requests.
309 if (!headers->EnumerateHeader(nullptr, "Last-Modified", 339 if (!headers->EnumerateHeader(nullptr, "Last-Modified",
310 &create_info->last_modified)) 340 &create_info->last_modified))
311 create_info->last_modified.clear(); 341 create_info->last_modified.clear();
312 if (!headers->EnumerateHeader(nullptr, "ETag", &create_info->etag)) 342 if (!headers->EnumerateHeader(nullptr, "ETag", &create_info->etag))
313 create_info->etag.clear(); 343 create_info->etag.clear();
314 } 344 }
315 345
316 // Grab the first content-disposition header. There may be more than one, 346 // 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 347 // 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()) { 569 switch (http_headers.response_code()) {
540 case -1: // Non-HTTP request. 570 case -1: // Non-HTTP request.
541 case net::HTTP_OK: 571 case net::HTTP_OK:
542 case net::HTTP_NON_AUTHORITATIVE_INFORMATION: 572 case net::HTTP_NON_AUTHORITATIVE_INFORMATION:
543 case net::HTTP_PARTIAL_CONTENT: 573 case net::HTTP_PARTIAL_CONTENT:
544 // Expected successful codes. 574 // Expected successful codes.
545 break; 575 break;
546 576
547 case net::HTTP_CREATED: 577 case net::HTTP_CREATED:
548 case net::HTTP_ACCEPTED: 578 case net::HTTP_ACCEPTED:
549 // Per RFC 2616 the entity being transferred is metadata about the 579 // 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 580 // 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 581 // 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 582 // case of HTTP_ACCEPTED). However, we currently don't have special
553 // handling for these response and they are downloaded the same as a 583 // handling for these response and they are downloaded the same as a
554 // regular response. 584 // regular response.
555 break; 585 break;
556 586
557 case net::HTTP_NO_CONTENT: 587 case net::HTTP_NO_CONTENT:
558 case net::HTTP_RESET_CONTENT: 588 case net::HTTP_RESET_CONTENT:
559 // These two status codes don't have an entity (or rather RFC 2616 589 // 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 590 // 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. 591 // resource not being found since there is no entity to download.
562 592
563 case net::HTTP_NOT_FOUND: 593 case net::HTTP_NOT_FOUND:
564 return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT; 594 return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
565 break; 595 break;
566 596
567 case net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE: 597 case net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE:
568 // Retry by downloading from the start automatically: 598 // Retry by downloading from the start automatically:
569 // If we haven't received data when we get this error, we won't. 599 // If we haven't received data when we get this error, we won't.
570 return DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE; 600 return DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE;
571 break; 601 break;
572 case net::HTTP_UNAUTHORIZED: 602 case net::HTTP_UNAUTHORIZED:
573 case net::HTTP_PROXY_AUTHENTICATION_REQUIRED: 603 case net::HTTP_PROXY_AUTHENTICATION_REQUIRED:
574 // Server didn't authorize this request. 604 // Server didn't authorize this request.
575 return DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED; 605 return DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED;
576 break; 606 break;
577 case net::HTTP_FORBIDDEN: 607 case net::HTTP_FORBIDDEN:
578 // Server forbids access to this resource. 608 // Server forbids access to this resource.
579 return DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN; 609 return DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN;
580 break; 610 break;
581 default: // All other errors. 611 default: // All other errors.
582 // Redirection and informational codes should have been handled earlier 612 // Redirection and informational codes should have been handled earlier
583 // in the stack. 613 // in the stack.
614 // TODO(xingliu): Handle HTTP_PRECONDITION_FAILED and resurrect
615 // DOWNLOAD_INTERRUPT_REASON_SERVER_PRECONDITION for range requests.
616 // This will change extensions::api::download::InterruptReason.
584 DCHECK_NE(3, http_headers.response_code() / 100); 617 DCHECK_NE(3, http_headers.response_code() / 100);
585 DCHECK_NE(1, http_headers.response_code() / 100); 618 DCHECK_NE(1, http_headers.response_code() / 100);
586 return DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED; 619 return DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED;
587 } 620 }
588 621
589 if (save_info && save_info->offset > 0) { 622 // The caller is expecting a partial response.
590 // The caller is expecting a partial response. 623 if (save_info && (save_info->offset > 0 || save_info->length > 0)) {
624 if (http_headers.response_code() != net::HTTP_PARTIAL_CONTENT) {
625 // Server should send partial content when "If-Match" or
626 // "If-Unmodified-Since" check passes, and the range request header has
627 // last byte position. e.g. "Range:bytes=50-99".
628 if (save_info->length != DownloadSaveInfo::kLengthFullContent)
629 return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
591 630
592 if (http_headers.response_code() != net::HTTP_PARTIAL_CONTENT) { 631 // Requested a partial range, but received the entire response, when
593 // Requested a partial range, but received the entire response. 632 // the range request header is "Range:bytes={offset}-".
594 save_info->offset = 0; 633 save_info->offset = 0;
595 save_info->hash_of_partial_file.clear(); 634 save_info->hash_of_partial_file.clear();
596 save_info->hash_state.reset(); 635 save_info->hash_state.reset();
597 return DOWNLOAD_INTERRUPT_REASON_NONE; 636 return DOWNLOAD_INTERRUPT_REASON_NONE;
598 } 637 }
599 638
600 int64_t first_byte = -1; 639 int64_t first_byte = -1;
601 int64_t last_byte = -1; 640 int64_t last_byte = -1;
602 int64_t length = -1; 641 int64_t length = -1;
603 if (!http_headers.GetContentRangeFor206(&first_byte, &last_byte, &length)) 642 if (!http_headers.GetContentRangeFor206(&first_byte, &last_byte, &length))
604 return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT; 643 return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
605 DCHECK_GE(first_byte, 0); 644 DCHECK_GE(first_byte, 0);
606 645
607 if (first_byte != save_info->offset) { 646 if (first_byte != save_info->offset ||
647 (save_info->length > 0 &&
648 last_byte != save_info->offset + save_info->length - 1)) {
608 // The server returned a different range than the one we requested. Assume 649 // The server returned a different range than the one we requested. Assume
609 // the response is bad. 650 // the response is bad.
610 // 651 //
611 // In the future we should consider allowing offsets that are less than 652 // 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 653 // the offset we've requested, since in theory we can truncate the partial
613 // file at the offset and continue. 654 // file at the offset and continue.
614 return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT; 655 return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
615 } 656 }
616 657
617 return DOWNLOAD_INTERRUPT_REASON_NONE; 658 return DOWNLOAD_INTERRUPT_REASON_NONE;
618 } 659 }
619 660
620 if (http_headers.response_code() == net::HTTP_PARTIAL_CONTENT) 661 if (http_headers.response_code() == net::HTTP_PARTIAL_CONTENT)
621 return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT; 662 return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
622 663
623 return DOWNLOAD_INTERRUPT_REASON_NONE; 664 return DOWNLOAD_INTERRUPT_REASON_NONE;
624 } 665 }
625 666
626 } // namespace content 667 } // 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