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

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

Issue 1544603003: [Downloads] Do not store error responses during resumption. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@unify-downloader-core
Patch Set: Created 4 years, 11 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
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 30 matching lines...) Expand all
41 DownloadRequestCore::DownloadRequestCore( 41 DownloadRequestCore::DownloadRequestCore(
42 net::URLRequest* request, 42 net::URLRequest* request,
43 scoped_ptr<DownloadSaveInfo> save_info, 43 scoped_ptr<DownloadSaveInfo> save_info,
44 const base::Closure& on_ready_to_read_callback) 44 const base::Closure& on_ready_to_read_callback)
45 : on_ready_to_read_callback_(on_ready_to_read_callback), 45 : on_ready_to_read_callback_(on_ready_to_read_callback),
46 request_(request), 46 request_(request),
47 save_info_(std::move(save_info)), 47 save_info_(std::move(save_info)),
48 last_buffer_size_(0), 48 last_buffer_size_(0),
49 bytes_read_(0), 49 bytes_read_(0),
50 pause_count_(0), 50 pause_count_(0),
51 was_deferred_(false) { 51 was_deferred_(false),
52 is_resumption_request_(save_info_->offset > 0) {
52 DCHECK(request_); 53 DCHECK(request_);
53 DCHECK(save_info_); 54 DCHECK(save_info_);
54 DCHECK(!on_ready_to_read_callback_.is_null()); 55 DCHECK(!on_ready_to_read_callback_.is_null());
55 RecordDownloadCount(UNTHROTTLED_COUNT); 56 RecordDownloadCount(UNTHROTTLED_COUNT);
56 power_save_blocker_ = PowerSaveBlocker::Create( 57 power_save_blocker_ = PowerSaveBlocker::Create(
57 PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension, 58 PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension,
58 PowerSaveBlocker::kReasonOther, "Download in progress"); 59 PowerSaveBlocker::kReasonOther, "Download in progress");
59 } 60 }
60 61
61 DownloadRequestCore::~DownloadRequestCore() { 62 DownloadRequestCore::~DownloadRequestCore() {
62 DCHECK_CURRENTLY_ON(BrowserThread::IO); 63 DCHECK_CURRENTLY_ON(BrowserThread::IO);
63 // Remove output stream callback if a stream exists. 64 // Remove output stream callback if a stream exists.
64 if (stream_writer_) 65 if (stream_writer_)
65 stream_writer_->RegisterCallback(base::Closure()); 66 stream_writer_->RegisterCallback(base::Closure());
66 67
67 UMA_HISTOGRAM_TIMES("SB2.DownloadDuration", 68 UMA_HISTOGRAM_TIMES("SB2.DownloadDuration",
68 base::TimeTicks::Now() - download_start_time_); 69 base::TimeTicks::Now() - download_start_time_);
69 } 70 }
70 71
71 // Send the download creation information to the download thread. 72 // Send the download creation information to the download thread.
72 void DownloadRequestCore::OnResponseStarted( 73 DownloadInterruptReason DownloadRequestCore::OnResponseStarted(
73 scoped_ptr<DownloadCreateInfo>* create_info, 74 scoped_ptr<DownloadCreateInfo>* create_info_out,
74 scoped_ptr<ByteStreamReader>* stream_reader) { 75 scoped_ptr<ByteStreamReader>* stream_reader_out) {
75 DCHECK_CURRENTLY_ON(BrowserThread::IO); 76 DCHECK_CURRENTLY_ON(BrowserThread::IO);
76 DCHECK(save_info_); 77 DCHECK(save_info_);
77 DVLOG(20) << __FUNCTION__ << "()" << DebugString(); 78 DVLOG(20) << __FUNCTION__ << "()" << DebugString();
78 download_start_time_ = base::TimeTicks::Now(); 79 download_start_time_ = base::TimeTicks::Now();
79 80
81 DownloadInterruptReason result =
82 request()->response_headers()
83 ? HandleSuccessfulServerResponse(*request()->response_headers(),
84 save_info_.get())
85 : DOWNLOAD_INTERRUPT_REASON_NONE;
86 if (result != DOWNLOAD_INTERRUPT_REASON_NONE)
87 return result;
88
80 // If it's a download, we don't want to poison the cache with it. 89 // If it's a download, we don't want to poison the cache with it.
81 request()->StopCaching(); 90 request()->StopCaching();
82 91
83 // Lower priority as well, so downloads don't contend for resources 92 // Lower priority as well, so downloads don't contend for resources
84 // with main frames. 93 // with main frames.
85 request()->SetPriority(net::IDLE); 94 request()->SetPriority(net::IDLE);
86 95
87 // If the content-length header is not present (or contains something other 96 // If the content-length header is not present (or contains something other
88 // than numbers), the incoming content_length is -1 (unknown size). 97 // than numbers), the incoming content_length is -1 (unknown size).
89 // Set the content length to 0 to indicate unknown size to DownloadManager. 98 // Set the content length to 0 to indicate unknown size to DownloadManager.
90 int64_t content_length = request()->GetExpectedContentSize() > 0 99 int64_t content_length = request()->GetExpectedContentSize() > 0
91 ? request()->GetExpectedContentSize() 100 ? request()->GetExpectedContentSize()
92 : 0; 101 : 0;
93 102
94 // Deleted in DownloadManager. 103 // Deleted in DownloadManager.
95 scoped_ptr<DownloadCreateInfo> info( 104 scoped_ptr<DownloadCreateInfo> create_info(
96 new DownloadCreateInfo(base::Time::Now(), content_length, 105 new DownloadCreateInfo(base::Time::Now(), content_length,
97 request()->net_log(), std::move(save_info_))); 106 request()->net_log(), std::move(save_info_)));
98 107
99 // Create the ByteStream for sending data to the download sink. 108 // Create the ByteStream for sending data to the download sink.
100 CreateByteStream( 109 CreateByteStream(
101 base::ThreadTaskRunnerHandle::Get(), 110 base::ThreadTaskRunnerHandle::Get(),
102 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), 111 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE),
103 kDownloadByteStreamSize, &stream_writer_, stream_reader); 112 kDownloadByteStreamSize, &stream_writer_, stream_reader_out);
104 stream_writer_->RegisterCallback( 113 stream_writer_->RegisterCallback(
105 base::Bind(&DownloadRequestCore::ResumeRequest, AsWeakPtr())); 114 base::Bind(&DownloadRequestCore::ResumeRequest, AsWeakPtr()));
106 115
107 info->url_chain = request()->url_chain(); 116 create_info->url_chain = request()->url_chain();
108 info->referrer_url = GURL(request()->referrer()); 117 create_info->referrer_url = GURL(request()->referrer());
109 string mime_type; 118 string mime_type;
110 request()->GetMimeType(&mime_type); 119 request()->GetMimeType(&mime_type);
111 info->mime_type = mime_type; 120 create_info->mime_type = mime_type;
112 info->remote_address = request()->GetSocketAddress().host(); 121 create_info->remote_address = request()->GetSocketAddress().host();
113 if (request()->response_headers()) { 122 if (request()->response_headers()) {
114 // Grab the first content-disposition header. There may be more than one, 123 // Grab the first content-disposition header. There may be more than one,
115 // though as of this writing, the network stack ensures if there are, they 124 // though as of this writing, the network stack ensures if there are, they
116 // are all duplicates. 125 // are all duplicates.
117 request()->response_headers()->EnumerateHeader( 126 request()->response_headers()->EnumerateHeader(
118 nullptr, "Content-Disposition", &info->content_disposition); 127 nullptr, "Content-Disposition", &create_info->content_disposition);
119 } 128 }
120 RecordDownloadMimeType(info->mime_type); 129 RecordDownloadMimeType(create_info->mime_type);
121 RecordDownloadContentDisposition(info->content_disposition); 130 RecordDownloadContentDisposition(create_info->content_disposition);
122 131
123 // Get the last modified time and etag. 132 // Get the last modified time and etag.
124 const net::HttpResponseHeaders* headers = request()->response_headers(); 133 const net::HttpResponseHeaders* headers = request()->response_headers();
125 if (headers) { 134 if (headers) {
126 if (headers->HasStrongValidators()) { 135 if (headers->HasStrongValidators()) {
127 // If we don't have strong validators as per RFC 2616 section 13.3.3, then 136 // If we don't have strong validators as per RFC 2616 section 13.3.3, then
128 // we neither store nor use them for range requests. 137 // we neither store nor use them for range requests.
129 if (!headers->EnumerateHeader(nullptr, "Last-Modified", 138 if (!headers->EnumerateHeader(nullptr, "Last-Modified",
130 &info->last_modified)) 139 &create_info->last_modified))
131 info->last_modified.clear(); 140 create_info->last_modified.clear();
132 if (!headers->EnumerateHeader(nullptr, "ETag", &info->etag)) 141 if (!headers->EnumerateHeader(nullptr, "ETag", &create_info->etag))
133 info->etag.clear(); 142 create_info->etag.clear();
134 } 143 }
135 144
136 int status = headers->response_code(); 145 if (!headers->GetMimeType(&create_info->original_mime_type))
137 if (2 == status / 100 && status != net::HTTP_PARTIAL_CONTENT) { 146 create_info->original_mime_type.clear();
138 // Success & not range response; if we asked for a range, we didn't
139 // get it--reset the file pointers to reflect that.
140 info->save_info->offset = 0;
141 info->save_info->hash_state = "";
142 }
143
144 if (!headers->GetMimeType(&info->original_mime_type))
145 info->original_mime_type.clear();
146 } 147 }
147 148
148 // Blink verifies that the requester of this download is allowed to set a 149 // Blink verifies that the requester of this download is allowed to set a
149 // suggested name for the security origin of the downlaod URL. However, this 150 // suggested name for the security origin of the download URL. However, this
150 // assumption doesn't hold if there were cross origin redirects. Therefore, 151 // assumption doesn't hold if there were cross origin redirects. Therefore,
151 // clear the suggested_name for such requests. 152 // clear the suggested_name for such requests.
152 if (info->url_chain.size() > 1 && 153 if (create_info->url_chain.size() > 1 &&
153 info->url_chain.front().GetOrigin() != info->url_chain.back().GetOrigin()) 154 create_info->url_chain.front().GetOrigin() !=
154 info->save_info->suggested_name.clear(); 155 create_info->url_chain.back().GetOrigin())
156 create_info->save_info->suggested_name.clear();
155 157
156 info.swap(*create_info); 158 create_info.swap(*create_info_out);
159
160 return DOWNLOAD_INTERRUPT_REASON_NONE;
161 }
162
163 DownloadInterruptReason DownloadRequestCore::OnRequestRedirected(
164 const net::RedirectInfo& redirect_info) {
165 // A redirect while attempting a partial resumption indicates a potential
166 // middle box. Trigger another interruption so that the DownloadItem can
167 // retry.
168 return is_resumption_request_ ? DOWNLOAD_INTERRUPT_REASON_SERVER_UNREACHABLE
169 : DOWNLOAD_INTERRUPT_REASON_NONE;
157 } 170 }
158 171
159 // Create a new buffer, which will be handed to the download thread for file 172 // Create a new buffer, which will be handed to the download thread for file
160 // writing and deletion. 173 // writing and deletion.
161 bool DownloadRequestCore::OnWillRead(scoped_refptr<net::IOBuffer>* buf, 174 bool DownloadRequestCore::OnWillRead(scoped_refptr<net::IOBuffer>* buf,
162 int* buf_size, 175 int* buf_size,
163 int min_size) { 176 int min_size) {
164 DCHECK_CURRENTLY_ON(BrowserThread::IO); 177 DCHECK_CURRENTLY_ON(BrowserThread::IO);
165 DCHECK(buf && buf_size); 178 DCHECK(buf && buf_size);
166 DCHECK(!read_buffer_.get()); 179 DCHECK(!read_buffer_.get());
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
214 227
215 DownloadInterruptReason DownloadRequestCore::OnResponseCompleted( 228 DownloadInterruptReason DownloadRequestCore::OnResponseCompleted(
216 const net::URLRequestStatus& status) { 229 const net::URLRequestStatus& status) {
217 DCHECK_CURRENTLY_ON(BrowserThread::IO); 230 DCHECK_CURRENTLY_ON(BrowserThread::IO);
218 int response_code = status.is_success() ? request()->GetResponseCode() : 0; 231 int response_code = status.is_success() ? request()->GetResponseCode() : 0;
219 DVLOG(20) << __FUNCTION__ << "()" << DebugString() 232 DVLOG(20) << __FUNCTION__ << "()" << DebugString()
220 << " status.status() = " << status.status() 233 << " status.status() = " << status.status()
221 << " status.error() = " << status.error() 234 << " status.error() = " << status.error()
222 << " response_code = " << response_code; 235 << " response_code = " << response_code;
223 236
224 net::Error error_code = net::OK; 237 DownloadInterruptReason reason = HandleRequestStatus(status);
225 if (status.status() == net::URLRequestStatus::FAILED ||
226 // Note cancels as failures too.
227 status.status() == net::URLRequestStatus::CANCELED) {
228 error_code = static_cast<net::Error>(status.error()); // Normal case.
229 // Make sure that at least the fact of failure comes through.
230 if (error_code == net::OK)
231 error_code = net::ERR_FAILED;
232 }
233
234 // ERR_CONTENT_LENGTH_MISMATCH and ERR_INCOMPLETE_CHUNKED_ENCODING are
235 // allowed since a number of servers in the wild close the connection too
236 // early by mistake. Other browsers - IE9, Firefox 11.0, and Safari 5.1.4 -
237 // treat downloads as complete in both cases, so we follow their lead.
238 if (error_code == net::ERR_CONTENT_LENGTH_MISMATCH ||
239 error_code == net::ERR_INCOMPLETE_CHUNKED_ENCODING) {
240 error_code = net::OK;
241 }
242 DownloadInterruptReason reason = ConvertNetErrorToInterruptReason(
243 error_code, DOWNLOAD_INTERRUPT_FROM_NETWORK);
244 238
245 if (status.status() == net::URLRequestStatus::CANCELED && 239 if (status.status() == net::URLRequestStatus::CANCELED &&
246 status.error() == net::ERR_ABORTED) { 240 status.error() == net::ERR_ABORTED) {
247 // CANCELED + ERR_ABORTED == something outside of the network 241 // CANCELED + ERR_ABORTED == something outside of the network
248 // stack cancelled the request. There aren't that many things that 242 // stack cancelled the request. There aren't that many things that
249 // could do this to a download request (whose lifetime is separated from 243 // could do this to a download request (whose lifetime is separated from
250 // the tab from which it came). We map this to USER_CANCELLED as the 244 // the tab from which it came). We map this to USER_CANCELLED as the
251 // case we know about (system suspend because of laptop close) corresponds 245 // case we know about (system suspend because of laptop close) corresponds
252 // to a user action. 246 // to a user action.
253 // TODO(ahendrickson) -- Find a better set of codes to use here, as 247 // TODO(ahendrickson) -- Find a better set of codes to use here, as
254 // CANCELED/ERR_ABORTED can occur for reasons other than user cancel. 248 // CANCELED/ERR_ABORTED can occur for reasons other than user cancel.
255 if (net::IsCertStatusError(request()->ssl_info().cert_status)) 249 if (net::IsCertStatusError(request()->ssl_info().cert_status)) {
256 reason = DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM; 250 reason = is_resumption_request_
257 else 251 ? DOWNLOAD_INTERRUPT_REASON_SERVER_UNREACHABLE
252 : DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM;
253 } else {
258 reason = DOWNLOAD_INTERRUPT_REASON_USER_CANCELED; 254 reason = DOWNLOAD_INTERRUPT_REASON_USER_CANCELED;
259 }
260
261 if (status.is_success() && reason == DOWNLOAD_INTERRUPT_REASON_NONE &&
262 request()->response_headers()) {
263 // Handle server's response codes.
264 switch (response_code) {
265 case -1: // Non-HTTP request.
266 case net::HTTP_OK:
267 case net::HTTP_CREATED:
268 case net::HTTP_ACCEPTED:
269 case net::HTTP_NON_AUTHORITATIVE_INFORMATION:
270 case net::HTTP_RESET_CONTENT:
271 case net::HTTP_PARTIAL_CONTENT:
272 // Expected successful codes.
273 break;
274 case net::HTTP_NO_CONTENT:
275 case net::HTTP_NOT_FOUND:
276 reason = DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
277 break;
278 case net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE:
279 // Retry by downloading from the start automatically:
280 // If we haven't received data when we get this error, we won't.
281 reason = DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE;
282 break;
283 case net::HTTP_UNAUTHORIZED:
284 // Server didn't authorize this request.
285 reason = DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED;
286 break;
287 case net::HTTP_FORBIDDEN:
288 // Server forbids access to this resource.
289 reason = DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN;
290 break;
291 default: // All other errors.
292 // Redirection and informational codes should have been handled earlier
293 // in the stack.
294 DCHECK_NE(3, response_code / 100);
295 DCHECK_NE(1, response_code / 100);
296 reason = DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED;
297 break;
298 } 255 }
299 } 256 }
300 257
301 std::string accept_ranges; 258 std::string accept_ranges;
302 bool has_strong_validators = false; 259 bool has_strong_validators = false;
303 if (request()->response_headers()) { 260 if (request()->response_headers()) {
304 request()->response_headers()->EnumerateHeader(nullptr, "Accept-Ranges", 261 request()->response_headers()->EnumerateHeader(nullptr, "Accept-Ranges",
305 &accept_ranges); 262 &accept_ranges);
306 has_strong_validators = 263 has_strong_validators =
307 request()->response_headers()->HasStrongValidators(); 264 request()->response_headers()->HasStrongValidators();
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
356 313
357 std::string DownloadRequestCore::DebugString() const { 314 std::string DownloadRequestCore::DebugString() const {
358 return base::StringPrintf( 315 return base::StringPrintf(
359 "{" 316 "{"
360 " url_ = " 317 " url_ = "
361 "\"%s\"" 318 "\"%s\""
362 " }", 319 " }",
363 request() ? request()->url().spec().c_str() : "<NULL request>"); 320 request() ? request()->url().spec().c_str() : "<NULL request>");
364 } 321 }
365 322
323 // static
324 DownloadInterruptReason DownloadRequestCore::HandleRequestStatus(
325 const net::URLRequestStatus& status) {
326 net::Error error_code = net::OK;
327 if (status.status() == net::URLRequestStatus::FAILED ||
328 // Note cancels as failures too.
329 status.status() == net::URLRequestStatus::CANCELED) {
330 error_code = static_cast<net::Error>(status.error()); // Normal case.
331 // Make sure that at least the fact of failure comes through.
332 if (error_code == net::OK)
333 error_code = net::ERR_FAILED;
334 }
335
336 // ERR_CONTENT_LENGTH_MISMATCH and ERR_INCOMPLETE_CHUNKED_ENCODING are
337 // allowed since a number of servers in the wild close the connection too
338 // early by mistake. Other browsers - IE9, Firefox 11.0, and Safari 5.1.4 -
339 // treat downloads as complete in both cases, so we follow their lead.
340 if (error_code == net::ERR_CONTENT_LENGTH_MISMATCH ||
341 error_code == net::ERR_INCOMPLETE_CHUNKED_ENCODING) {
342 error_code = net::OK;
343 }
344 DownloadInterruptReason reason = ConvertNetErrorToInterruptReason(
345 error_code, DOWNLOAD_INTERRUPT_FROM_NETWORK);
346
347 return reason;
348 }
349
350 // static
351 DownloadInterruptReason DownloadRequestCore::HandleSuccessfulServerResponse(
352 const net::HttpResponseHeaders& http_headers,
353 DownloadSaveInfo* save_info) {
354 switch (http_headers.response_code()) {
355 case -1: // Non-HTTP request.
356 case net::HTTP_OK:
357 case net::HTTP_NON_AUTHORITATIVE_INFORMATION:
358 case net::HTTP_PARTIAL_CONTENT:
359 // Expected successful codes.
360 break;
361
362 case net::HTTP_CREATED:
363 case net::HTTP_ACCEPTED:
364 // Per RFC 2616 the entity being transferred is metadata about the
365 // resource at the target URL and not the resource at that URL (or the
366 // resource that would be at the URL once processing is completed in the
367 // case of HTTP_ACCEPTED). However, we currently don't have special
368 // handling for these response and they are downloaded the same as a
369 // regular response.
370 break;
371
372 case net::HTTP_NO_CONTENT:
373 case net::HTTP_RESET_CONTENT:
374 // These two status codes don't have an entity (or rather RFC 2616
375 // requires that there be no entity). They are treated the same as the
376 // resource not being found since there is no entity to download.
377
378 case net::HTTP_NOT_FOUND:
379 return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
380 break;
381
382 case net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE:
383 // Retry by downloading from the start automatically:
384 // If we haven't received data when we get this error, we won't.
385 return DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE;
386 break;
387 case net::HTTP_UNAUTHORIZED:
388 case net::HTTP_PROXY_AUTHENTICATION_REQUIRED:
389 // Server didn't authorize this request.
390 return DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED;
391 break;
392 case net::HTTP_FORBIDDEN:
393 // Server forbids access to this resource.
394 return DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN;
395 break;
396 default: // All other errors.
397 // Redirection and informational codes should have been handled earlier
398 // in the stack.
399 DCHECK_NE(3, http_headers.response_code() / 100);
400 DCHECK_NE(1, http_headers.response_code() / 100);
401 return DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED;
402 }
403
404 if (save_info->offset > 0) {
405 // The caller is expecting a partial response.
406
407 if (http_headers.response_code() != net::HTTP_PARTIAL_CONTENT) {
408 // Requested a partial range, but received the entire response.
409 save_info->offset = 0;
410 save_info->hash_state.clear();
411 return DOWNLOAD_INTERRUPT_REASON_NONE;
412 }
413
414 int64_t first_byte = -1;
415 int64_t last_byte = -1;
416 int64_t length = -1;
417 if (!http_headers.GetContentRange(&first_byte, &last_byte, &length))
418 return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
419 DCHECK_GE(first_byte, 0);
420
421 if (first_byte != save_info->offset) {
422 // The server returned a different range than the one we requested. Assume
423 // the response is bad.
424 //
425 // In the future we should consider allowing offsets that are less than
426 // the offset we've requested, since in theory we can truncate the partial
427 // file at the offset and continue.
428 return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
429 }
430
431 return DOWNLOAD_INTERRUPT_REASON_NONE;
432 }
433
434 if (http_headers.response_code() == net::HTTP_PARTIAL_CONTENT)
435 return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
436
437 return DOWNLOAD_INTERRUPT_REASON_NONE;
438 }
439
366 } // namespace content 440 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698