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

Unified Diff: content/browser/download/download_request_core.cc

Issue 148133007: [Downloads] Always call DM::StartDownload() for explicit downloads. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Deal with downloads that are blocked by throttles. Created 4 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 side-by-side diff with in-line comments
Download patch
Index: content/browser/download/download_request_core.cc
diff --git a/content/browser/download/download_request_core.cc b/content/browser/download/download_request_core.cc
index 072ba7b0b55469efc47e767f0fa3da78e1f5457e..b03c2d56bcc498761fd429fa7be86133e2e0e144 100644
--- a/content/browser/download/download_request_core.cc
+++ b/content/browser/download/download_request_core.cc
@@ -8,6 +8,7 @@
#include "base/bind.h"
#include "base/callback_helpers.h"
+#include "base/format_macros.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
@@ -21,41 +22,200 @@
#include "content/browser/download/download_manager_impl.h"
#include "content/browser/download/download_request_handle.h"
#include "content/browser/download/download_stats.h"
+#include "content/browser/loader/resource_dispatcher_host_impl.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/download_interrupt_reasons.h"
#include "content/public/browser/download_item.h"
#include "content/public/browser/download_manager_delegate.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/power_save_blocker.h"
+#include "content/public/browser/resource_context.h"
#include "content/public/browser/web_contents.h"
+#include "net/base/elements_upload_data_stream.h"
#include "net/base/io_buffer.h"
+#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
+#include "net/base/upload_bytes_element_reader.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_status_code.h"
#include "net/url_request/url_request_context.h"
namespace content {
+namespace {
+
+// This is a UserData::Data that will be attached to a URLRequest as a
+// side-channel for passing download parameters.
+class DownloadRequestData : public base::SupportsUserData::Data {
+ public:
+ ~DownloadRequestData() override {}
+
+ static void Attach(net::URLRequest* request,
+ DownloadUrlParameters* download_parameters,
+ uint32_t download_id);
+ static DownloadRequestData* Get(net::URLRequest* request);
+ static void Detach(net::URLRequest* request);
+
+ scoped_ptr<DownloadSaveInfo> TakeSaveInfo() { return std::move(save_info_); }
+ uint32_t download_id() const { return download_id_; }
+ const DownloadUrlParameters::OnStartedCallback& callback() const {
+ return on_started_callback_;
+ }
+
+ private:
+ static const int kKey;
+
+ scoped_ptr<DownloadSaveInfo> save_info_;
+ uint32_t download_id_ = DownloadItem::kInvalidId;
+ DownloadUrlParameters::OnStartedCallback on_started_callback_;
+};
+
+// static
+const int DownloadRequestData::kKey = 0;
+
+// static
+void DownloadRequestData::Attach(net::URLRequest* request,
+ DownloadUrlParameters* parameters,
+ uint32_t download_id) {
+ DownloadRequestData* request_data = new DownloadRequestData;
+ request_data->save_info_.reset(new DownloadSaveInfo);
+ request_data->save_info_->file_path = parameters->file_path();
+ request_data->save_info_->suggested_name = parameters->suggested_name();
+ request_data->save_info_->file = parameters->GetFile();
+ request_data->save_info_->offset = parameters->offset();
+ request_data->save_info_->hash_state = parameters->hash_state();
+ request_data->save_info_->prompt_for_save_location = parameters->prompt();
+ request_data->download_id_ = download_id;
+ request_data->on_started_callback_ = parameters->callback();
+ request->SetUserData(&kKey, request_data);
+}
+
+// static
+DownloadRequestData* DownloadRequestData::Get(net::URLRequest* request) {
+ return static_cast<DownloadRequestData*>(request->GetUserData(&kKey));
+}
+
+// static
+void DownloadRequestData::Detach(net::URLRequest* request) {
+ request->RemoveUserData(&kKey);
+}
+
+} // namespace
+
const int DownloadRequestCore::kDownloadByteStreamSize = 100 * 1024;
-DownloadRequestCore::DownloadRequestCore(
- net::URLRequest* request,
- scoped_ptr<DownloadSaveInfo> save_info,
- const base::Closure& on_ready_to_read_callback)
- : on_ready_to_read_callback_(on_ready_to_read_callback),
+// static
+scoped_ptr<net::URLRequest> DownloadRequestCore::CreateRequestOnIOThread(
+ uint32_t download_id,
+ DownloadUrlParameters* params) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ DCHECK(download_id == DownloadItem::kInvalidId ||
+ !params->content_initiated())
+ << "Content initiated downloads shouldn't specify a download ID";
+
+ // ResourceDispatcherHost{Base} is-not-a URLRequest::Delegate, and
+ // DownloadUrlParameters can-not include resource_dispatcher_host_impl.h, so
+ // we must down cast. RDHI is the only subclass of RDH as of 2012 May 4.
+ scoped_ptr<net::URLRequest> request(
+ params->resource_context()->GetRequestContext()->CreateRequest(
+ params->url(), net::DEFAULT_PRIORITY, nullptr));
+ request->set_method(params->method());
+
+ if (!params->post_body().empty()) {
+ const std::string& body = params->post_body();
+ scoped_ptr<net::UploadElementReader> reader(
+ net::UploadOwnedBytesElementReader::CreateWithString(body));
+ request->set_upload(
+ net::ElementsUploadDataStream::CreateWithReader(std::move(reader), 0));
+ }
+
+ if (params->post_id() >= 0) {
+ // The POST in this case does not have an actual body, and only works
+ // when retrieving data from cache. This is done because we don't want
+ // to do a re-POST without user consent, and currently don't have a good
+ // plan on how to display the UI for that.
+ DCHECK(params->prefer_cache());
+ DCHECK_EQ("POST", params->method());
+ std::vector<scoped_ptr<net::UploadElementReader>> element_readers;
+ request->set_upload(make_scoped_ptr(new net::ElementsUploadDataStream(
+ std::move(element_readers), params->post_id())));
+ }
+
+ int load_flags = request->load_flags();
+ if (params->prefer_cache()) {
+ // If there is upload data attached, only retrieve from cache because there
+ // is no current mechanism to prompt the user for their consent for a
+ // re-post. For GETs, try to retrieve data from the cache and skip
+ // validating the entry if present.
+ if (request->get_upload())
+ load_flags |= net::LOAD_ONLY_FROM_CACHE;
+ else
+ load_flags |= net::LOAD_PREFERRING_CACHE;
+ } else {
+ load_flags |= net::LOAD_DISABLE_CACHE;
+ }
+ request->SetLoadFlags(load_flags);
+
+ bool has_last_modified = !params->last_modified().empty();
+ bool has_etag = !params->etag().empty();
+
+ // If we've asked for a range, we want to make sure that we only get that
+ // range if our current copy of the information is good. We shouldn't be
+ // asked to continue if we don't have a verifier.
+ DCHECK(params->offset() == 0 || has_etag || has_last_modified);
+
+ // If we're not at the beginning of the file, retrieve only the remaining
+ // portion.
+ if (params->offset() > 0 && (has_etag || has_last_modified)) {
+ request->SetExtraRequestHeaderByName(
+ "Range", base::StringPrintf("bytes=%" PRId64 "-", params->offset()),
+ true);
+
+ // In accordance with RFC 2616 Section 14.27, use If-Range to specify that
+ // the server return the entire entity if the validator doesn't match.
+ // Last-Modified can be used in the absence of ETag as a validator if the
+ // response headers satisfied the HttpUtil::HasStrongValidators() predicate.
+ //
+ // This function assumes that HasStrongValidators() was true and that the
+ // ETag and Last-Modified header values supplied are valid.
+ request->SetExtraRequestHeaderByName(
+ "If-Range", has_etag ? params->etag() : params->last_modified(), true);
+ }
+
+ for (const auto& header : params->request_headers())
+ request->SetExtraRequestHeaderByName(header.first, header.second,
+ false /*overwrite*/);
+
+ DownloadRequestData::Attach(request.get(), std::move(params), download_id);
+ return request;
+}
+
+DownloadRequestCore::DownloadRequestCore(net::URLRequest* request,
+ Delegate* delegate)
+ : delegate_(delegate),
request_(request),
- save_info_(std::move(save_info)),
+ download_id_(DownloadItem::kInvalidId),
last_buffer_size_(0),
bytes_read_(0),
pause_count_(0),
- was_deferred_(false) {
+ was_deferred_(false),
+ started_(false),
+ abort_reason_(DOWNLOAD_INTERRUPT_REASON_NONE) {
DCHECK(request_);
- DCHECK(save_info_);
- DCHECK(!on_ready_to_read_callback_.is_null());
+ DCHECK(delegate_);
RecordDownloadCount(UNTHROTTLED_COUNT);
power_save_blocker_ = PowerSaveBlocker::Create(
PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension,
PowerSaveBlocker::kReasonOther, "Download in progress");
+ DownloadRequestData* request_data = DownloadRequestData::Get(request_);
+ if (request_data) {
+ save_info_ = request_data->TakeSaveInfo();
+ download_id_ = request_data->download_id();
+ on_started_callback_ = request_data->callback();
+ DownloadRequestData::Detach(request_);
+ } else {
+ save_info_.reset(new DownloadSaveInfo);
+ }
}
DownloadRequestCore::~DownloadRequestCore() {
@@ -68,15 +228,40 @@ DownloadRequestCore::~DownloadRequestCore() {
base::TimeTicks::Now() - download_start_time_);
}
-// Send the download creation information to the download thread.
-void DownloadRequestCore::OnResponseStarted(
- scoped_ptr<DownloadCreateInfo>* create_info,
- scoped_ptr<ByteStreamReader>* stream_reader) {
+scoped_ptr<DownloadCreateInfo> DownloadRequestCore::CreateDownloadCreateInfo(
+ DownloadInterruptReason result) {
+ DCHECK(!started_);
+ started_ = true;
+ scoped_ptr<DownloadCreateInfo> create_info(new DownloadCreateInfo(
+ base::Time::Now(), request()->net_log(), std::move(save_info_)));
+
+ if (result == DOWNLOAD_INTERRUPT_REASON_NONE)
+ create_info->remote_address = request()->GetSocketAddress().host();
+ create_info->url_chain = request()->url_chain();
+ create_info->referrer_url = GURL(request()->referrer());
+ create_info->result = result;
+ create_info->download_id = download_id_;
+ return create_info;
+}
+
+bool DownloadRequestCore::OnResponseStarted() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
- DCHECK(save_info_);
DVLOG(20) << __FUNCTION__ << "()" << DebugString();
download_start_time_ = base::TimeTicks::Now();
+ DownloadInterruptReason result =
+ request()->response_headers()
+ ? HandleSuccessfulServerResponse(*request()->response_headers(),
+ save_info_.get())
+ : DOWNLOAD_INTERRUPT_REASON_NONE;
+
+ scoped_ptr<DownloadCreateInfo> create_info = CreateDownloadCreateInfo(result);
+ if (result != DOWNLOAD_INTERRUPT_REASON_NONE) {
+ delegate_->OnStart(std::move(create_info), scoped_ptr<ByteStreamReader>(),
+ base::ResetAndReturn(&on_started_callback_));
+ return false;
+ }
+
// If it's a download, we don't want to poison the cache with it.
request()->StopCaching();
@@ -90,35 +275,18 @@ void DownloadRequestCore::OnResponseStarted(
int64_t content_length = request()->GetExpectedContentSize() > 0
? request()->GetExpectedContentSize()
: 0;
-
- // Deleted in DownloadManager.
- scoped_ptr<DownloadCreateInfo> info(
- new DownloadCreateInfo(base::Time::Now(), content_length,
- request()->net_log(), std::move(save_info_)));
+ create_info->total_bytes = content_length;
// Create the ByteStream for sending data to the download sink.
+ scoped_ptr<ByteStreamReader> stream_reader;
CreateByteStream(
base::ThreadTaskRunnerHandle::Get(),
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE),
- kDownloadByteStreamSize, &stream_writer_, stream_reader);
+ kDownloadByteStreamSize, &stream_writer_, &stream_reader);
stream_writer_->RegisterCallback(
base::Bind(&DownloadRequestCore::ResumeRequest, AsWeakPtr()));
- info->url_chain = request()->url_chain();
- info->referrer_url = GURL(request()->referrer());
- string mime_type;
- request()->GetMimeType(&mime_type);
- info->mime_type = mime_type;
- info->remote_address = request()->GetSocketAddress().host();
- if (request()->response_headers()) {
- // Grab the first content-disposition header. There may be more than one,
- // though as of this writing, the network stack ensures if there are, they
- // are all duplicates.
- request()->response_headers()->EnumerateHeader(
- nullptr, "Content-Disposition", &info->content_disposition);
- }
- RecordDownloadMimeType(info->mime_type);
- RecordDownloadContentDisposition(info->content_disposition);
+ request()->GetMimeType(&create_info->mime_type);
// Get the last modified time and etag.
const net::HttpResponseHeaders* headers = request()->response_headers();
@@ -127,33 +295,37 @@ void DownloadRequestCore::OnResponseStarted(
// If we don't have strong validators as per RFC 2616 section 13.3.3, then
// we neither store nor use them for range requests.
if (!headers->EnumerateHeader(nullptr, "Last-Modified",
- &info->last_modified))
- info->last_modified.clear();
- if (!headers->EnumerateHeader(nullptr, "ETag", &info->etag))
- info->etag.clear();
+ &create_info->last_modified))
+ create_info->last_modified.clear();
+ if (!headers->EnumerateHeader(nullptr, "ETag", &create_info->etag))
+ create_info->etag.clear();
}
- int status = headers->response_code();
- if (2 == status / 100 && status != net::HTTP_PARTIAL_CONTENT) {
- // Success & not range response; if we asked for a range, we didn't
- // get it--reset the file pointers to reflect that.
- info->save_info->offset = 0;
- info->save_info->hash_state = "";
- }
+ // Grab the first content-disposition header. There may be more than one,
+ // though as of this writing, the network stack ensures if there are, they
+ // are all duplicates.
+ headers->EnumerateHeader(nullptr, "Content-Disposition",
+ &create_info->content_disposition);
- if (!headers->GetMimeType(&info->original_mime_type))
- info->original_mime_type.clear();
+ if (!headers->GetMimeType(&create_info->original_mime_type))
+ create_info->original_mime_type.clear();
}
// Blink verifies that the requester of this download is allowed to set a
- // suggested name for the security origin of the downlaod URL. However, this
+ // suggested name for the security origin of the download URL. However, this
// assumption doesn't hold if there were cross origin redirects. Therefore,
// clear the suggested_name for such requests.
- if (info->url_chain.size() > 1 &&
- info->url_chain.front().GetOrigin() != info->url_chain.back().GetOrigin())
- info->save_info->suggested_name.clear();
+ if (create_info->url_chain.size() > 1 &&
+ create_info->url_chain.front().GetOrigin() !=
+ create_info->url_chain.back().GetOrigin())
+ create_info->save_info->suggested_name.clear();
+
+ RecordDownloadMimeType(create_info->mime_type);
+ RecordDownloadContentDisposition(create_info->content_disposition);
- info.swap(*create_info);
+ delegate_->OnStart(std::move(create_info), std::move(stream_reader),
+ base::ResetAndReturn(&on_started_callback_));
+ return true;
}
// Create a new buffer, which will be handed to the download thread for file
@@ -212,7 +384,13 @@ bool DownloadRequestCore::OnReadCompleted(int bytes_read, bool* defer) {
return true;
}
-DownloadInterruptReason DownloadRequestCore::OnResponseCompleted(
+void DownloadRequestCore::OnWillAbort(DownloadInterruptReason reason) {
+ DVLOG(20) << __FUNCTION__ << "() reason=" << reason << " " << DebugString();
+ DCHECK(!started_);
+ abort_reason_ = reason;
+}
+
+void DownloadRequestCore::OnResponseCompleted(
const net::URLRequestStatus& status) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
int response_code = status.is_success() ? request()->GetResponseCode() : 0;
@@ -221,80 +399,27 @@ DownloadInterruptReason DownloadRequestCore::OnResponseCompleted(
<< " status.error() = " << status.error()
<< " response_code = " << response_code;
- net::Error error_code = net::OK;
- if (status.status() == net::URLRequestStatus::FAILED ||
- // Note cancels as failures too.
- status.status() == net::URLRequestStatus::CANCELED) {
- error_code = static_cast<net::Error>(status.error()); // Normal case.
- // Make sure that at least the fact of failure comes through.
- if (error_code == net::OK)
- error_code = net::ERR_FAILED;
- }
-
- // ERR_CONTENT_LENGTH_MISMATCH and ERR_INCOMPLETE_CHUNKED_ENCODING are
- // allowed since a number of servers in the wild close the connection too
- // early by mistake. Other browsers - IE9, Firefox 11.0, and Safari 5.1.4 -
- // treat downloads as complete in both cases, so we follow their lead.
- if (error_code == net::ERR_CONTENT_LENGTH_MISMATCH ||
- error_code == net::ERR_INCOMPLETE_CHUNKED_ENCODING) {
- error_code = net::OK;
- }
- DownloadInterruptReason reason = ConvertNetErrorToInterruptReason(
- error_code, DOWNLOAD_INTERRUPT_FROM_NETWORK);
-
- if (status.status() == net::URLRequestStatus::CANCELED &&
- status.error() == net::ERR_ABORTED) {
- // CANCELED + ERR_ABORTED == something outside of the network
- // stack cancelled the request. There aren't that many things that
- // could do this to a download request (whose lifetime is separated from
- // the tab from which it came). We map this to USER_CANCELLED as the
- // case we know about (system suspend because of laptop close) corresponds
- // to a user action.
- // TODO(ahendrickson) -- Find a better set of codes to use here, as
- // CANCELED/ERR_ABORTED can occur for reasons other than user cancel.
- if (net::IsCertStatusError(request()->ssl_info().cert_status))
- reason = DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM;
- else
- reason = DOWNLOAD_INTERRUPT_REASON_USER_CANCELED;
- }
-
- if (status.is_success() && reason == DOWNLOAD_INTERRUPT_REASON_NONE &&
- request()->response_headers()) {
- // Handle server's response codes.
- switch (response_code) {
- case -1: // Non-HTTP request.
- case net::HTTP_OK:
- case net::HTTP_CREATED:
- case net::HTTP_ACCEPTED:
- case net::HTTP_NON_AUTHORITATIVE_INFORMATION:
- case net::HTTP_RESET_CONTENT:
- case net::HTTP_PARTIAL_CONTENT:
- // Expected successful codes.
- break;
- case net::HTTP_NO_CONTENT:
- case net::HTTP_NOT_FOUND:
- reason = DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
- break;
- case net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE:
- // Retry by downloading from the start automatically:
- // If we haven't received data when we get this error, we won't.
- reason = DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE;
- break;
- case net::HTTP_UNAUTHORIZED:
- // Server didn't authorize this request.
- reason = DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED;
- break;
- case net::HTTP_FORBIDDEN:
- // Server forbids access to this resource.
- reason = DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN;
- break;
- default: // All other errors.
- // Redirection and informational codes should have been handled earlier
- // in the stack.
- DCHECK_NE(3, response_code / 100);
- DCHECK_NE(1, response_code / 100);
- reason = DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED;
- break;
+ DownloadInterruptReason reason = HandleRequestStatus(status);
+
+ if (status.status() == net::URLRequestStatus::CANCELED) {
+ if (abort_reason_ != DOWNLOAD_INTERRUPT_REASON_NONE) {
+ // If a more specific interrupt reason was specified before the request
+ // was explicitly cancelled, then use it.
+ reason = abort_reason_;
+ } else if (status.error() == net::ERR_ABORTED) {
+ // CANCELED + ERR_ABORTED == something outside of the network
+ // stack cancelled the request. There aren't that many things that
+ // could do this to a download request (whose lifetime is separated from
+ // the tab from which it came). We map this to USER_CANCELLED as the
+ // case we know about (system suspend because of laptop close) corresponds
+ // to a user action.
+ // TODO(asanka): A lid close or other power event should result in an
+ // interruption that doesn't discard the partial state, unlike
+ // USER_CANCELLED. (https://crbug.com/166179)
+ if (net::IsCertStatusError(request()->ssl_info().cert_status))
+ reason = DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM;
+ else
+ reason = DOWNLOAD_INTERRUPT_REASON_USER_CANCELED;
}
}
@@ -325,7 +450,16 @@ DownloadInterruptReason DownloadRequestCore::OnResponseCompleted(
stream_writer_.reset(); // We no longer need the stream.
read_buffer_ = nullptr;
- return reason;
+ if (started_)
+ return;
+
+ // OnResponseCompleted() called without OnResponseStarted(). This should only
+ // happen when the request was aborted.
+ DCHECK_NE(reason, DOWNLOAD_INTERRUPT_REASON_NONE);
+ scoped_ptr<DownloadCreateInfo> create_info = CreateDownloadCreateInfo(reason);
+ scoped_ptr<ByteStreamReader> empty_byte_stream;
+ delegate_->OnStart(std::move(create_info), std::move(empty_byte_stream),
+ base::ResetAndReturn(&on_started_callback_));
}
void DownloadRequestCore::PauseRequest() {
@@ -351,16 +485,135 @@ void DownloadRequestCore::ResumeRequest() {
last_stream_pause_time_ = base::TimeTicks();
}
- on_ready_to_read_callback_.Run();
+ delegate_->OnReadyToRead();
}
std::string DownloadRequestCore::DebugString() const {
return base::StringPrintf(
"{"
+ " this=%p "
" url_ = "
"\"%s\""
" }",
+ reinterpret_cast<const void*>(this),
request() ? request()->url().spec().c_str() : "<NULL request>");
}
+// static
+DownloadInterruptReason DownloadRequestCore::HandleRequestStatus(
+ const net::URLRequestStatus& status) {
+ net::Error error_code = net::OK;
+ if (status.status() == net::URLRequestStatus::FAILED ||
+ // Note cancels as failures too.
+ status.status() == net::URLRequestStatus::CANCELED) {
+ error_code = static_cast<net::Error>(status.error()); // Normal case.
+ // Make sure that at least the fact of failure comes through.
+ if (error_code == net::OK)
+ error_code = net::ERR_FAILED;
+ }
+
+ // ERR_CONTENT_LENGTH_MISMATCH and ERR_INCOMPLETE_CHUNKED_ENCODING are
+ // allowed since a number of servers in the wild close the connection too
+ // early by mistake. Other browsers - IE9, Firefox 11.0, and Safari 5.1.4 -
+ // treat downloads as complete in both cases, so we follow their lead.
+ if (error_code == net::ERR_CONTENT_LENGTH_MISMATCH ||
+ error_code == net::ERR_INCOMPLETE_CHUNKED_ENCODING) {
+ error_code = net::OK;
+ }
+ DownloadInterruptReason reason = ConvertNetErrorToInterruptReason(
+ error_code, DOWNLOAD_INTERRUPT_FROM_NETWORK);
+
+ return reason;
+}
+
+// static
+DownloadInterruptReason DownloadRequestCore::HandleSuccessfulServerResponse(
+ const net::HttpResponseHeaders& http_headers,
+ DownloadSaveInfo* save_info) {
+ switch (http_headers.response_code()) {
+ case -1: // Non-HTTP request.
+ case net::HTTP_OK:
+ case net::HTTP_NON_AUTHORITATIVE_INFORMATION:
+ case net::HTTP_PARTIAL_CONTENT:
+ // Expected successful codes.
+ break;
+
+ case net::HTTP_CREATED:
+ case net::HTTP_ACCEPTED:
+ // Per RFC 2616 the entity being transferred is metadata about the
+ // resource at the target URL and not the resource at that URL (or the
+ // resource that would be at the URL once processing is completed in the
+ // case of HTTP_ACCEPTED). However, we currently don't have special
+ // handling for these response and they are downloaded the same as a
+ // regular response.
+ break;
+
+ case net::HTTP_NO_CONTENT:
+ case net::HTTP_RESET_CONTENT:
+ // These two status codes don't have an entity (or rather RFC 2616
+ // requires that there be no entity). They are treated the same as the
+ // resource not being found since there is no entity to download.
+
+ case net::HTTP_NOT_FOUND:
+ return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
+ break;
+
+ case net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE:
+ // Retry by downloading from the start automatically:
+ // If we haven't received data when we get this error, we won't.
+ return DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE;
+ break;
+ case net::HTTP_UNAUTHORIZED:
+ case net::HTTP_PROXY_AUTHENTICATION_REQUIRED:
+ // Server didn't authorize this request.
+ return DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED;
+ break;
+ case net::HTTP_FORBIDDEN:
+ // Server forbids access to this resource.
+ return DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN;
+ break;
+ default: // All other errors.
+ // Redirection and informational codes should have been handled earlier
+ // in the stack.
+ DCHECK_NE(3, http_headers.response_code() / 100);
+ DCHECK_NE(1, http_headers.response_code() / 100);
+ return DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED;
+ }
+
+ if (save_info && save_info->offset > 0) {
+ // The caller is expecting a partial response.
+
+ if (http_headers.response_code() != net::HTTP_PARTIAL_CONTENT) {
+ // Requested a partial range, but received the entire response.
+ save_info->offset = 0;
+ save_info->hash_state.clear();
+ return DOWNLOAD_INTERRUPT_REASON_NONE;
+ }
+
+ int64_t first_byte = -1;
+ int64_t last_byte = -1;
+ int64_t length = -1;
+ if (!http_headers.GetContentRange(&first_byte, &last_byte, &length))
+ return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
+ DCHECK_GE(first_byte, 0);
+
+ if (first_byte != save_info->offset) {
+ // The server returned a different range than the one we requested. Assume
+ // the response is bad.
+ //
+ // In the future we should consider allowing offsets that are less than
+ // the offset we've requested, since in theory we can truncate the partial
+ // file at the offset and continue.
+ return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
+ }
+
+ return DOWNLOAD_INTERRUPT_REASON_NONE;
+ }
+
+ if (http_headers.response_code() == net::HTTP_PARTIAL_CONTENT)
+ return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
+
+ return DOWNLOAD_INTERRUPT_REASON_NONE;
+}
+
} // namespace content

Powered by Google App Engine
This is Rietveld 408576698