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

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

Issue 2748103014: Allow the initial request to take over failed parallel requests. (Closed)
Patch Set: rebase again Created 3 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « content/browser/download/download_file_impl.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: content/browser/download/download_file_impl.cc
diff --git a/content/browser/download/download_file_impl.cc b/content/browser/download/download_file_impl.cc
index a47ac9b709ed72f1635c51e4ba23265fefeb4f78..4b47ae1f08e8bf0263a9e43c7fe8b8ec53113cae 100644
--- a/content/browser/download/download_file_impl.cc
+++ b/content/browser/download/download_file_impl.cc
@@ -404,18 +404,7 @@ void DownloadFileImpl::StreamActive(SourceStream* source_stream) {
// Take care of communication with our observer.
if (reason != DOWNLOAD_INTERRUPT_REASON_NONE) {
- // Error case for both upstream source and file write.
- // Shut down processing and signal an error to our observer.
- // Our observer will clean us up.
- source_stream->stream_reader()->RegisterCallback(base::Closure());
- weak_factory_.InvalidateWeakPtrs();
- SendUpdate(); // Make info up to date before error.
- std::unique_ptr<crypto::SecureHash> hash_state = file_.Finish();
- BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::Bind(&DownloadDestinationObserver::DestinationError, observer_,
- reason, TotalBytesReceived(), base::Passed(&hash_state)));
- num_active_streams_--;
+ HandleStreamError(source_stream, reason);
} else if (state == ByteStreamReader::STREAM_COMPLETE || should_terminate) {
// Signal successful completion or termination of the current stream.
source_stream->stream_reader()->RegisterCallback(base::Closure());
@@ -566,6 +555,88 @@ bool DownloadFileImpl::IsDownloadCompleted() {
return true;
}
+void DownloadFileImpl::HandleStreamError(SourceStream* source_stream,
+ DownloadInterruptReason reason) {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ source_stream->stream_reader()->RegisterCallback(base::Closure());
+ source_stream->set_finished(true);
+ num_active_streams_--;
+
+ bool can_recover_from_error = false;
+
+ if (is_sparse_file_) {
+ // If a neighboring stream request is available, check if it can help
+ // download all the data left by |source stream| or has already done so. We
+ // want to avoid the situation that a server always fail additional requests
+ // from the client thus causing the initial request and the download going
+ // nowhere.
+ // TODO(qinmin): make all streams half open so that they can recover
+ // failures from their neighbors.
+ SourceStream* preceding_neighbor = FindPrecedingNeighbor(source_stream);
+ while (preceding_neighbor) {
+ int64_t upper_range = source_stream->offset() + source_stream->length();
+ if ((!preceding_neighbor->is_finished() &&
+ (preceding_neighbor->length() ==
+ DownloadSaveInfo::kLengthFullContent ||
+ preceding_neighbor->offset() + preceding_neighbor->length() >=
+ upper_range)) ||
+ (preceding_neighbor->offset() + preceding_neighbor->bytes_written() >=
+ upper_range)) {
+ can_recover_from_error = true;
+ break;
+ }
+ // If the neighbor cannot recover the error and it has already created
+ // a slice, just interrupt the download.
+ if (preceding_neighbor->bytes_written() > 0)
+ break;
+ preceding_neighbor = FindPrecedingNeighbor(preceding_neighbor);
+ }
+
+ if (can_recover_from_error) {
+ // Since the neighbor stream will download all data downloading from its
+ // offset to source_stream->offset(). Close all other streams in the
+ // middle.
+ for (auto& stream : source_streams_) {
+ if (stream.second->offset() < source_stream->offset() &&
+ stream.second->offset() > preceding_neighbor->offset()) {
+ DCHECK_EQ(stream.second->bytes_written(), 0);
+ stream.second->stream_reader()->RegisterCallback(base::Closure());
+ stream.second->set_finished(true);
+ num_active_streams_--;
+ }
+ }
+ }
+ }
+
+ SendUpdate(); // Make info up to date before error.
+
+ if (!can_recover_from_error) {
+ // Error case for both upstream source and file write.
+ // Shut down processing and signal an error to our observer.
+ // Our observer will clean us up.
+ weak_factory_.InvalidateWeakPtrs();
+ std::unique_ptr<crypto::SecureHash> hash_state = file_.Finish();
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&DownloadDestinationObserver::DestinationError, observer_,
+ reason, TotalBytesReceived(), base::Passed(&hash_state)));
+ }
+}
+
+DownloadFileImpl::SourceStream* DownloadFileImpl::FindPrecedingNeighbor(
+ SourceStream* source_stream) {
+ int64_t max_preceding_offset = 0;
+ SourceStream* ret = nullptr;
+ for (auto& stream : source_streams_) {
+ int64_t offset = stream.second->offset();
+ if (offset < source_stream->offset() && offset >= max_preceding_offset) {
+ ret = stream.second.get();
+ max_preceding_offset = offset;
+ }
+ }
+ return ret;
+}
+
DownloadFileImpl::RenameParameters::RenameParameters(
RenameOption option,
const base::FilePath& new_path,
« no previous file with comments | « content/browser/download/download_file_impl.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698