Chromium Code Reviews| OLD | NEW |
|---|---|
| 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_resource_handler.h" | 5 #include "content/browser/download/download_resource_handler.h" |
| 6 | 6 |
| 7 #include <string> | 7 #include <string> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 27 #include "content/public/common/resource_response.h" | 27 #include "content/public/common/resource_response.h" |
| 28 #include "net/base/io_buffer.h" | 28 #include "net/base/io_buffer.h" |
| 29 #include "net/base/net_errors.h" | 29 #include "net/base/net_errors.h" |
| 30 #include "net/http/http_response_headers.h" | 30 #include "net/http/http_response_headers.h" |
| 31 #include "net/http/http_status_code.h" | 31 #include "net/http/http_status_code.h" |
| 32 #include "net/url_request/url_request_context.h" | 32 #include "net/url_request/url_request_context.h" |
| 33 | 33 |
| 34 namespace content { | 34 namespace content { |
| 35 namespace { | 35 namespace { |
| 36 | 36 |
| 37 void CallStartedCBOnUIThread( | |
| 38 const DownloadUrlParameters::OnStartedCallback& started_cb, | |
| 39 DownloadItem* item, | |
| 40 DownloadInterruptReason interrupt_reason) { | |
| 41 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 42 | |
| 43 if (started_cb.is_null()) | |
| 44 return; | |
| 45 started_cb.Run(item, interrupt_reason); | |
| 46 } | |
| 47 | |
| 48 // Static function in order to prevent any accidental accesses to | 37 // Static function in order to prevent any accidental accesses to |
| 49 // DownloadResourceHandler members from the UI thread. | 38 // DownloadResourceHandler members from the UI thread. |
|
Randy Smith (Not in Mondays)
2014/03/18 21:07:28
nit: This comment seems outdated; it implies that
asanka
2014/03/19 20:37:06
Removed comment.
| |
| 50 static void StartOnUIThread( | 39 void StartOnUIThread( |
| 51 scoped_ptr<DownloadCreateInfo> info, | 40 scoped_ptr<DownloadCreateInfo> info, |
| 52 scoped_ptr<ByteStreamReader> stream, | 41 scoped_ptr<ByteStreamReader> stream, |
| 53 const DownloadUrlParameters::OnStartedCallback& started_cb) { | 42 const DownloadUrlParameters::OnStartedCallback& started_cb) { |
| 54 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 43 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 55 | 44 |
| 56 DownloadManager* download_manager = info->request_handle.GetDownloadManager(); | 45 DownloadManager* download_manager = info->request_handle.GetDownloadManager(); |
| 57 if (!download_manager) { | 46 if (!download_manager) { |
| 58 // NULL in unittests or if the page closed right after starting the | 47 // NULL in unittests or if the page closed right after starting the |
| 59 // download. | 48 // download. |
| 60 if (!started_cb.is_null()) | 49 if (!started_cb.is_null()) |
| 61 started_cb.Run(NULL, DOWNLOAD_INTERRUPT_REASON_USER_CANCELED); | 50 started_cb.Run(NULL, DOWNLOAD_INTERRUPT_REASON_USER_CANCELED); |
| 62 | 51 |
| 63 // |stream| gets deleted on non-FILE thread, but it's ok since | 52 // |stream| gets deleted on non-FILE thread, but it's ok since |
| 64 // we're not using stream_writer_ yet. | 53 // we're not using stream_writer_ yet. |
| 65 | 54 |
| 66 return; | 55 return; |
| 67 } | 56 } |
| 68 | 57 |
| 69 download_manager->StartDownload(info.Pass(), stream.Pass(), started_cb); | 58 download_manager->StartDownload(info.Pass(), stream.Pass(), started_cb); |
| 70 } | 59 } |
| 71 | 60 |
| 61 DownloadInterruptReason HTTPResponseCodeToInterruptReason(int response_code) { | |
| 62 switch (response_code) { | |
| 63 case -1: // Non-HTTP request. | |
| 64 case net::HTTP_OK: | |
| 65 case net::HTTP_CREATED: | |
| 66 case net::HTTP_ACCEPTED: | |
| 67 case net::HTTP_NON_AUTHORITATIVE_INFORMATION: | |
| 68 case net::HTTP_RESET_CONTENT: | |
| 69 case net::HTTP_PARTIAL_CONTENT: | |
| 70 return DOWNLOAD_INTERRUPT_REASON_NONE; | |
| 71 case net::HTTP_NO_CONTENT: | |
| 72 case net::HTTP_NOT_FOUND: | |
| 73 return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT; | |
| 74 case net::HTTP_PRECONDITION_FAILED: | |
| 75 // Failed our 'If-Unmodified-Since' or 'If-Match'; see | |
| 76 // download_manager_impl.cc BeginDownload() | |
| 77 return DOWNLOAD_INTERRUPT_REASON_SERVER_PRECONDITION; | |
| 78 case net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE: | |
| 79 // Retry by downloading from the start automatically: | |
| 80 // If we haven't received data when we get this error, we won't. | |
| 81 return DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE; | |
| 82 // TODO(asanka): Add handling for 5xx errors with backoff semantics. | |
| 83 default: // All other errors. | |
| 84 // Redirection and informational codes should have been handled earlier | |
| 85 // in the stack. | |
| 86 DCHECK_NE(3, response_code / 100); | |
| 87 DCHECK_NE(1, response_code / 100); | |
| 88 return DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED; | |
| 89 } | |
| 90 } | |
| 91 | |
| 72 } // namespace | 92 } // namespace |
| 73 | 93 |
| 74 const int DownloadResourceHandler::kDownloadByteStreamSize = 100 * 1024; | 94 const int DownloadResourceHandler::kDownloadByteStreamSize = 100 * 1024; |
| 75 | 95 |
| 76 DownloadResourceHandler::DownloadResourceHandler( | 96 DownloadResourceHandler::DownloadResourceHandler( |
| 77 uint32 id, | 97 uint32 id, |
| 78 net::URLRequest* request, | 98 net::URLRequest* request, |
| 79 const DownloadUrlParameters::OnStartedCallback& started_cb, | 99 const DownloadUrlParameters::OnStartedCallback& started_cb, |
| 80 scoped_ptr<DownloadSaveInfo> save_info) | 100 scoped_ptr<DownloadSaveInfo> save_info) |
| 81 : ResourceHandler(request), | 101 : ResourceHandler(request), |
| 82 download_id_(id), | 102 download_id_(id), |
| 83 started_cb_(started_cb), | 103 started_cb_(started_cb), |
| 84 save_info_(save_info.Pass()), | 104 save_info_(save_info.Pass()), |
| 85 last_buffer_size_(0), | 105 last_buffer_size_(0), |
| 86 bytes_read_(0), | 106 bytes_read_(0), |
| 87 pause_count_(0), | 107 pause_count_(0), |
| 88 was_deferred_(false), | 108 was_deferred_(false), |
| 89 on_response_started_called_(false) { | 109 download_manager_notified_(false) { |
| 90 RecordDownloadCount(UNTHROTTLED_COUNT); | 110 RecordDownloadCount(UNTHROTTLED_COUNT); |
| 91 } | 111 } |
| 92 | 112 |
| 93 bool DownloadResourceHandler::OnUploadProgress(int request_id, | 113 bool DownloadResourceHandler::OnUploadProgress(int request_id, |
| 94 uint64 position, | 114 uint64 position, |
| 95 uint64 size) { | 115 uint64 size) { |
| 96 return true; | 116 return true; |
| 97 } | 117 } |
| 98 | 118 |
| 99 bool DownloadResourceHandler::OnRequestRedirected( | 119 bool DownloadResourceHandler::OnRequestRedirected( |
| 100 int request_id, | 120 int request_id, |
| 101 const GURL& url, | 121 const GURL& url, |
| 102 ResourceResponse* response, | 122 ResourceResponse* response, |
| 103 bool* defer) { | 123 bool* defer) { |
| 104 // We treat a download as a main frame load, and thus update the policy URL | 124 // We treat a download as a main frame load, and thus update the policy URL |
| 105 // on redirects. | 125 // on redirects. |
| 106 request()->set_first_party_for_cookies(url); | 126 request()->set_first_party_for_cookies(url); |
| 107 return true; | 127 return true; |
| 108 } | 128 } |
| 109 | 129 |
| 110 // Send the download creation information to the download thread. | 130 // Send the download creation information to the download thread. |
| 111 bool DownloadResourceHandler::OnResponseStarted( | 131 bool DownloadResourceHandler::OnResponseStarted( |
| 112 int request_id, | 132 int request_id, |
| 113 ResourceResponse* response, | 133 ResourceResponse* response, |
| 114 bool* defer) { | 134 bool* defer) { |
| 115 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 135 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 116 // There can be only one (call) | |
| 117 DCHECK(!on_response_started_called_); | |
| 118 on_response_started_called_ = true; | |
| 119 | |
| 120 VLOG(20) << __FUNCTION__ << "()" << DebugString() | 136 VLOG(20) << __FUNCTION__ << "()" << DebugString() |
| 121 << " request_id = " << request_id; | 137 << " request_id = " << request_id; |
| 122 download_start_time_ = base::TimeTicks::Now(); | 138 download_start_time_ = base::TimeTicks::Now(); |
| 123 | 139 |
| 124 // If it's a download, we don't want to poison the cache with it. | 140 // If it's a download, we don't want to poison the cache with it. |
| 125 request()->StopCaching(); | 141 request()->StopCaching(); |
| 126 | 142 |
| 127 // Lower priority as well, so downloads don't contend for resources | 143 // Lower priority as well, so downloads don't contend for resources |
| 128 // with main frames. | 144 // with main frames. |
| 129 request()->SetPriority(net::IDLE); | 145 request()->SetPriority(net::IDLE); |
| 130 | 146 |
| 147 const net::HttpResponseHeaders* headers = request()->response_headers(); | |
| 148 DownloadInterruptReason interrupt_reason = | |
| 149 (headers ? HTTPResponseCodeToInterruptReason(headers->response_code()) | |
| 150 : DOWNLOAD_INTERRUPT_REASON_NONE); | |
| 151 | |
| 152 if (interrupt_reason != DOWNLOAD_INTERRUPT_REASON_NONE) | |
| 153 NotifyDownloadManagerOfFailedRequest(interrupt_reason); | |
| 154 else | |
| 155 NotifyDownloadManagerOfSuccessfulResponseStart(response); | |
| 156 | |
| 157 return true; | |
| 158 } | |
| 159 | |
| 160 void DownloadResourceHandler::NotifyDownloadManagerOfSuccessfulResponseStart( | |
| 161 ResourceResponse* response) { | |
| 162 // NotifyDownloadManagerOfFailedRequest or | |
| 163 // NotifyDownloadManagerOfSuccessfulResponseStart can only be called once. | |
| 164 DCHECK(!download_manager_notified_); | |
| 165 download_manager_notified_ = true; | |
| 166 | |
| 167 const ResourceRequestInfoImpl* request_info = GetRequestInfo(); | |
| 168 const net::HttpResponseHeaders* headers = request()->response_headers(); | |
| 169 | |
| 131 // If the content-length header is not present (or contains something other | 170 // If the content-length header is not present (or contains something other |
| 132 // than numbers), the incoming content_length is -1 (unknown size). | 171 // than numbers), the incoming content_length is -1 (unknown size). |
| 133 // Set the content length to 0 to indicate unknown size to DownloadManager. | 172 // Set the content length to 0 to indicate unknown size to DownloadManager. |
| 134 int64 content_length = | 173 int64 content_length = |
| 135 response->head.content_length > 0 ? response->head.content_length : 0; | 174 response->head.content_length > 0 ? response->head.content_length : 0; |
|
Randy Smith (Not in Mondays)
2014/03/18 21:07:28
nit, question: It feels a little funny to be both
asanka
2014/03/19 20:37:06
The URLRequestJob provides the expected content le
| |
| 136 | 175 |
| 137 const ResourceRequestInfoImpl* request_info = GetRequestInfo(); | |
| 138 | |
| 139 // Deleted in DownloadManager. | |
| 140 scoped_ptr<DownloadCreateInfo> info( | 176 scoped_ptr<DownloadCreateInfo> info( |
| 141 new DownloadCreateInfo(base::Time::Now(), | 177 new DownloadCreateInfo(base::Time::Now(), |
| 142 content_length, | 178 content_length, |
| 143 request()->net_log(), | 179 request()->net_log(), |
| 144 request_info->HasUserGesture(), | 180 request_info->HasUserGesture(), |
| 145 request_info->GetPageTransition(), | 181 request_info->GetPageTransition(), |
| 146 save_info_.Pass())); | 182 save_info_.Pass())); |
| 147 | 183 |
| 148 // Create the ByteStream for sending data to the download sink. | |
| 149 scoped_ptr<ByteStreamReader> stream_reader; | |
| 150 CreateByteStream( | |
| 151 base::MessageLoopProxy::current(), | |
| 152 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), | |
| 153 kDownloadByteStreamSize, &stream_writer_, &stream_reader); | |
| 154 stream_writer_->RegisterCallback( | |
| 155 base::Bind(&DownloadResourceHandler::ResumeRequest, AsWeakPtr())); | |
| 156 | |
| 157 info->download_id = download_id_; | |
| 158 info->url_chain = request()->url_chain(); | |
| 159 info->referrer_url = GURL(request()->referrer()); | |
| 160 info->mime_type = response->head.mime_type; | |
| 161 info->remote_address = request()->GetSocketAddress().host(); | |
| 162 request()->GetResponseHeaderByName("content-disposition", | |
| 163 &info->content_disposition); | |
| 164 RecordDownloadMimeType(info->mime_type); | |
| 165 RecordDownloadContentDisposition(info->content_disposition); | |
| 166 | |
| 167 info->request_handle = | |
| 168 DownloadRequestHandle(AsWeakPtr(), request_info->GetChildID(), | |
| 169 request_info->GetRouteID(), | |
| 170 request_info->GetRequestID()); | |
| 171 | |
| 172 // Get the last modified time and etag. | |
| 173 const net::HttpResponseHeaders* headers = request()->response_headers(); | |
| 174 if (headers) { | 184 if (headers) { |
| 175 if (headers->HasStrongValidators()) { | 185 if (headers->HasStrongValidators()) { |
| 176 // If we don't have strong validators as per RFC 2616 section 13.3.3, then | 186 // If we don't have strong validators as per RFC 2616 section 13.3.3, then |
| 177 // we neither store nor use them for range requests. | 187 // we neither store nor use them for range requests. |
| 178 if (!headers->EnumerateHeader(NULL, "Last-Modified", | 188 if (!headers->EnumerateHeader(NULL, "Last-Modified", |
| 179 &info->last_modified)) | 189 &info->last_modified)) |
| 180 info->last_modified.clear(); | 190 info->last_modified.clear(); |
| 181 if (!headers->EnumerateHeader(NULL, "ETag", &info->etag)) | 191 if (!headers->EnumerateHeader(NULL, "ETag", &info->etag)) |
| 182 info->etag.clear(); | 192 info->etag.clear(); |
| 183 } | 193 } |
| 184 | 194 |
| 185 int status = headers->response_code(); | 195 int status = headers->response_code(); |
| 186 if (2 == status / 100 && status != net::HTTP_PARTIAL_CONTENT) { | 196 if (2 == status / 100 && status != net::HTTP_PARTIAL_CONTENT) { |
| 187 // Success & not range response; if we asked for a range, we didn't | 197 // Success & not range response; if we asked for a range, we didn't |
| 188 // get it--reset the file pointers to reflect that. | 198 // get it--reset the file pointers to reflect that. |
| 189 info->save_info->offset = 0; | 199 info->save_info->offset = 0; |
| 190 info->save_info->hash_state = ""; | 200 info->save_info->hash_state = ""; |
| 191 } | 201 } |
| 192 | 202 |
| 193 if (!headers->GetMimeType(&info->original_mime_type)) | 203 if (!headers->GetMimeType(&info->original_mime_type)) |
| 194 info->original_mime_type.clear(); | 204 info->original_mime_type.clear(); |
| 195 } | 205 } |
| 196 | 206 |
| 207 info->download_id = download_id_; | |
| 208 info->url_chain = request()->url_chain(); | |
| 209 info->referrer_url = GURL(request()->referrer()); | |
| 210 | |
| 211 scoped_ptr<ByteStreamReader> stream_reader; | |
| 212 // Create the ByteStream for sending data to the download sink. | |
| 213 CreateByteStream( | |
| 214 base::MessageLoopProxy::current(), | |
| 215 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), | |
| 216 kDownloadByteStreamSize, | |
| 217 &stream_writer_, | |
| 218 &stream_reader); | |
| 219 stream_writer_->RegisterCallback( | |
| 220 base::Bind(&DownloadResourceHandler::ResumeRequest, AsWeakPtr())); | |
| 221 | |
| 222 info->mime_type = response->head.mime_type; | |
| 223 info->remote_address = request()->GetSocketAddress().host(); | |
| 224 request()->GetResponseHeaderByName("content-disposition", | |
| 225 &info->content_disposition); | |
|
Randy Smith (Not in Mondays)
2014/03/18 21:07:28
nit, suggestion: It makes some sense to me to swit
asanka
2014/03/19 20:37:06
Done.
| |
| 226 RecordDownloadMimeType(info->mime_type); | |
| 227 RecordDownloadContentDisposition(info->content_disposition); | |
| 228 | |
| 229 info->request_handle = DownloadRequestHandle(AsWeakPtr(), | |
| 230 request_info->GetChildID(), | |
| 231 request_info->GetRouteID(), | |
| 232 request_info->GetRequestID()); | |
| 233 | |
| 197 BrowserThread::PostTask( | 234 BrowserThread::PostTask( |
| 198 BrowserThread::UI, FROM_HERE, | 235 BrowserThread::UI, FROM_HERE, |
| 199 base::Bind(&StartOnUIThread, | 236 base::Bind(&StartOnUIThread, |
| 200 base::Passed(&info), | 237 base::Passed(&info), |
| 201 base::Passed(&stream_reader), | 238 base::Passed(&stream_reader), |
| 202 // Pass to StartOnUIThread so that variable | 239 // Pass to StartOnUIThread so that variable |
| 203 // access is always on IO thread but function | 240 // access is always on IO thread but function |
| 204 // is called on UI thread. | 241 // is called on UI thread. |
| 205 started_cb_)); | 242 started_cb_)); |
| 206 // Guaranteed to be called in StartOnUIThread | 243 // Guaranteed to be called in StartOnUIThread |
| 207 started_cb_.Reset(); | 244 started_cb_.Reset(); |
| 208 | |
| 209 return true; | |
| 210 } | 245 } |
| 211 | 246 |
| 212 void DownloadResourceHandler::CallStartedCB( | 247 void DownloadResourceHandler::NotifyDownloadManagerOfFailedRequest( |
| 213 DownloadItem* item, | |
| 214 DownloadInterruptReason interrupt_reason) { | 248 DownloadInterruptReason interrupt_reason) { |
| 215 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 249 // NotifyDownloadManagerOfFailedRequest or |
| 216 if (started_cb_.is_null()) | 250 // NotifyDownloadManagerOfSuccessfulResponseStart can only be called once. |
| 217 return; | 251 DCHECK(!download_manager_notified_); |
| 218 BrowserThread::PostTask( | 252 DCHECK_NE(DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason); |
| 219 BrowserThread::UI, | 253 download_manager_notified_ = true; |
| 220 FROM_HERE, | 254 |
| 221 base::Bind( | 255 const ResourceRequestInfoImpl* request_info = GetRequestInfo(); |
| 222 &CallStartedCBOnUIThread, started_cb_, item, interrupt_reason)); | 256 |
| 257 scoped_ptr<DownloadCreateInfo> info( | |
| 258 new DownloadCreateInfo(base::Time::Now(), | |
| 259 -1, | |
| 260 request()->net_log(), | |
| 261 false, | |
| 262 request_info->GetPageTransition(), | |
| 263 save_info_.Pass())); | |
| 264 info->download_id = download_id_; | |
| 265 info->url_chain = request()->url_chain(); | |
| 266 info->referrer_url = GURL(request()->referrer()); | |
| 267 info->interrupt_reason = interrupt_reason; | |
| 268 info->remote_address = request()->GetSocketAddress().host(); | |
| 269 info->request_handle = DownloadRequestHandle(AsWeakPtr(), | |
| 270 request_info->GetChildID(), | |
| 271 request_info->GetRouteID(), | |
| 272 request_info->GetRequestID()); | |
| 273 | |
| 274 scoped_ptr<ByteStreamReader> stream_reader; | |
| 275 BrowserThread::PostTask(BrowserThread::UI, | |
| 276 FROM_HERE, | |
| 277 base::Bind(&StartOnUIThread, | |
| 278 base::Passed(&info), | |
| 279 base::Passed(&stream_reader), | |
|
Randy Smith (Not in Mondays)
2014/03/18 21:07:28
nit: I think it'd be clearer to cons up a null one
asanka
2014/03/19 20:37:06
Done.
| |
| 280 started_cb_)); | |
| 223 started_cb_.Reset(); | 281 started_cb_.Reset(); |
| 224 } | 282 } |
| 225 | 283 |
| 226 bool DownloadResourceHandler::OnWillStart(int request_id, | 284 bool DownloadResourceHandler::OnWillStart(int request_id, |
| 227 const GURL& url, | 285 const GURL& url, |
| 228 bool* defer) { | 286 bool* defer) { |
| 229 return true; | 287 return true; |
| 230 } | 288 } |
| 231 | 289 |
| 232 bool DownloadResourceHandler::OnBeforeNetworkStart(int request_id, | 290 bool DownloadResourceHandler::OnBeforeNetworkStart(int request_id, |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 270 double potential_bandwidth = last_buffer_size_/seconds_since_last_read; | 328 double potential_bandwidth = last_buffer_size_/seconds_since_last_read; |
| 271 RecordBandwidth(actual_bandwidth, potential_bandwidth); | 329 RecordBandwidth(actual_bandwidth, potential_bandwidth); |
| 272 } | 330 } |
| 273 last_read_time_ = now; | 331 last_read_time_ = now; |
| 274 | 332 |
| 275 if (!bytes_read) | 333 if (!bytes_read) |
| 276 return true; | 334 return true; |
| 277 bytes_read_ += bytes_read; | 335 bytes_read_ += bytes_read; |
| 278 DCHECK(read_buffer_.get()); | 336 DCHECK(read_buffer_.get()); |
| 279 | 337 |
| 338 // If this was an error response, then stream_writer_ would be NULL. | |
| 339 if (!stream_writer_) { | |
| 340 read_buffer_ = NULL; | |
| 341 return true; | |
| 342 } | |
| 343 | |
| 280 // Take the data ship it down the stream. If the stream is full, pause the | 344 // Take the data ship it down the stream. If the stream is full, pause the |
| 281 // request; the stream callback will resume it. | 345 // request; the stream callback will resume it. |
| 282 if (!stream_writer_->Write(read_buffer_, bytes_read)) { | 346 if (!stream_writer_->Write(read_buffer_, bytes_read)) { |
| 283 PauseRequest(); | 347 PauseRequest(); |
| 284 *defer = was_deferred_ = true; | 348 *defer = was_deferred_ = true; |
| 285 last_stream_pause_time_ = now; | 349 last_stream_pause_time_ = now; |
| 286 } | 350 } |
| 287 | 351 |
| 288 read_buffer_ = NULL; // Drop our reference. | 352 read_buffer_ = NULL; // Drop our reference. |
| 289 | 353 |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 334 // stack cancelled the request. There aren't that many things that | 398 // stack cancelled the request. There aren't that many things that |
| 335 // could do this to a download request (whose lifetime is separated from | 399 // could do this to a download request (whose lifetime is separated from |
| 336 // the tab from which it came). We map this to USER_CANCELLED as the | 400 // the tab from which it came). We map this to USER_CANCELLED as the |
| 337 // case we know about (system suspend because of laptop close) corresponds | 401 // case we know about (system suspend because of laptop close) corresponds |
| 338 // to a user action. | 402 // to a user action. |
| 339 // TODO(ahendrickson) -- Find a better set of codes to use here, as | 403 // TODO(ahendrickson) -- Find a better set of codes to use here, as |
| 340 // CANCELED/ERR_ABORTED can occur for reasons other than user cancel. | 404 // CANCELED/ERR_ABORTED can occur for reasons other than user cancel. |
| 341 reason = DOWNLOAD_INTERRUPT_REASON_USER_CANCELED; | 405 reason = DOWNLOAD_INTERRUPT_REASON_USER_CANCELED; |
| 342 } | 406 } |
| 343 | 407 |
| 344 if (status.is_success() && | |
| 345 reason == DOWNLOAD_INTERRUPT_REASON_NONE && | |
| 346 request()->response_headers()) { | |
| 347 // Handle server's response codes. | |
| 348 switch(response_code) { | |
| 349 case -1: // Non-HTTP request. | |
| 350 case net::HTTP_OK: | |
| 351 case net::HTTP_CREATED: | |
| 352 case net::HTTP_ACCEPTED: | |
| 353 case net::HTTP_NON_AUTHORITATIVE_INFORMATION: | |
| 354 case net::HTTP_RESET_CONTENT: | |
| 355 case net::HTTP_PARTIAL_CONTENT: | |
| 356 // Expected successful codes. | |
| 357 break; | |
| 358 case net::HTTP_NO_CONTENT: | |
| 359 case net::HTTP_NOT_FOUND: | |
| 360 reason = DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT; | |
| 361 break; | |
| 362 case net::HTTP_PRECONDITION_FAILED: | |
| 363 // Failed our 'If-Unmodified-Since' or 'If-Match'; see | |
| 364 // download_manager_impl.cc BeginDownload() | |
| 365 reason = DOWNLOAD_INTERRUPT_REASON_SERVER_PRECONDITION; | |
| 366 break; | |
| 367 case net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE: | |
| 368 // Retry by downloading from the start automatically: | |
| 369 // If we haven't received data when we get this error, we won't. | |
| 370 reason = DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE; | |
| 371 break; | |
| 372 default: // All other errors. | |
| 373 // Redirection and informational codes should have been handled earlier | |
| 374 // in the stack. | |
| 375 DCHECK_NE(3, response_code / 100); | |
| 376 DCHECK_NE(1, response_code / 100); | |
| 377 reason = DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED; | |
| 378 break; | |
| 379 } | |
| 380 } | |
| 381 | |
| 382 std::string accept_ranges; | 408 std::string accept_ranges; |
| 383 bool has_strong_validators = false; | 409 bool has_strong_validators = false; |
| 384 if (request()->response_headers()) { | 410 if (request()->response_headers()) { |
| 385 request()->response_headers()->EnumerateHeader( | 411 request()->response_headers()->EnumerateHeader( |
| 386 NULL, "Accept-Ranges", &accept_ranges); | 412 NULL, "Accept-Ranges", &accept_ranges); |
| 387 has_strong_validators = | 413 has_strong_validators = |
| 388 request()->response_headers()->HasStrongValidators(); | 414 request()->response_headers()->HasStrongValidators(); |
| 389 } | 415 } |
| 390 RecordAcceptsRanges(accept_ranges, bytes_read_, has_strong_validators); | 416 RecordAcceptsRanges(accept_ranges, bytes_read_, has_strong_validators); |
| 391 RecordNetworkBlockage(base::TimeTicks::Now() - download_start_time_, | 417 RecordNetworkBlockage(base::TimeTicks::Now() - download_start_time_, |
| 392 total_pause_time_); | 418 total_pause_time_); |
| 393 | 419 |
| 394 CallStartedCB(NULL, reason); | 420 // If OnResponseCompleted() is called without a corresponding |
| 395 | 421 // OnResponseStarted() call, then the DownloadManager wouldn't have been |
| 396 // Send the info down the stream. Conditional is in case we get | 422 // notified. |
| 397 // OnResponseCompleted without OnResponseStarted. | 423 if (!download_manager_notified_ && |
| 398 if (stream_writer_) | 424 status.status() != net::URLRequestStatus::CANCELED) { |
| 425 DCHECK_NE(DOWNLOAD_INTERRUPT_REASON_NONE, reason); | |
| 426 NotifyDownloadManagerOfFailedRequest(reason); | |
| 427 } else if (stream_writer_) { | |
| 428 // Send the info down the stream. Conditional is in case we get | |
| 429 // OnResponseCompleted without OnResponseStarted. | |
| 399 stream_writer_->Close(reason); | 430 stream_writer_->Close(reason); |
| 431 stream_writer_.reset(); // We no longer need the stream. | |
| 432 } | |
| 400 | 433 |
| 401 // If the error mapped to something unknown, record it so that | 434 // If the error mapped to something unknown, record it so that |
| 402 // we can drill down. | 435 // we can drill down. |
| 403 if (reason == DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED) { | 436 if (reason == DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED) { |
| 404 UMA_HISTOGRAM_CUSTOM_ENUMERATION("Download.MapErrorNetworkFailed", | 437 UMA_HISTOGRAM_CUSTOM_ENUMERATION("Download.MapErrorNetworkFailed", |
| 405 std::abs(status.error()), | 438 std::abs(status.error()), |
| 406 net::GetAllErrorCodesForUma()); | 439 net::GetAllErrorCodesForUma()); |
| 407 } | 440 } |
| 408 | 441 |
| 409 stream_writer_.reset(); // We no longer need the stream. | |
| 410 read_buffer_ = NULL; | 442 read_buffer_ = NULL; |
| 411 } | 443 } |
| 412 | 444 |
| 413 void DownloadResourceHandler::OnDataDownloaded( | 445 void DownloadResourceHandler::OnDataDownloaded( |
| 414 int request_id, | 446 int request_id, |
| 415 int bytes_downloaded) { | 447 int bytes_downloaded) { |
| 416 NOTREACHED(); | 448 NOTREACHED(); |
| 417 } | 449 } |
| 418 | 450 |
| 419 void DownloadResourceHandler::PauseRequest() { | 451 void DownloadResourceHandler::PauseRequest() { |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 466 request()->url().spec().c_str() : | 498 request()->url().spec().c_str() : |
| 467 "<NULL request>", | 499 "<NULL request>", |
| 468 info->GetChildID(), | 500 info->GetChildID(), |
| 469 info->GetRequestID(), | 501 info->GetRequestID(), |
| 470 info->GetRouteID()); | 502 info->GetRouteID()); |
| 471 } | 503 } |
| 472 | 504 |
| 473 DownloadResourceHandler::~DownloadResourceHandler() { | 505 DownloadResourceHandler::~DownloadResourceHandler() { |
| 474 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 506 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 475 | 507 |
| 476 // This won't do anything if the callback was called before. | 508 // DownloadManager would not be notified if: |
| 477 // If it goes through, it will likely be because OnWillStart() returned | 509 // |
| 478 // false somewhere in the chain of resource handlers. | 510 // - The Content-Type or Content-Disposition indicated that the entity should |
| 479 CallStartedCB(NULL, DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED); | 511 // be downloaded, but the response code was not 2xx. BufferedResourceHandler |
| 512 // will create a DownloadResourceHandler, but won't invoke it. | |
| 513 // TODO(asanka): Fix this so that ResourceHandlers are only created if they | |
| 514 // are going to be used. | |
| 515 // | |
| 516 // - OnWillStart() returned false somewhere in the chain of resource handlers. | |
| 517 if (!download_manager_notified_ && !started_cb_.is_null()) | |
|
Randy Smith (Not in Mondays)
2014/03/18 21:07:28
DCHECK opportunity? Shouldn't these two condition
asanka
2014/03/19 20:37:06
After verifying the OnWillStart() pathway, I decid
| |
| 518 BrowserThread::PostTask( | |
| 519 BrowserThread::UI, | |
| 520 FROM_HERE, | |
| 521 base::Bind(started_cb_, | |
|
Randy Smith (Not in Mondays)
2014/03/18 21:07:28
Is the eventual intent to get rid of started_cb?
asanka
2014/03/19 20:37:06
Yeah. I'd like to get rid of started_cb_. I added
| |
| 522 static_cast<DownloadItem*>(NULL), | |
| 523 DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED)); | |
| 480 | 524 |
| 481 // Remove output stream callback if a stream exists. | 525 // Remove output stream callback if a stream exists. |
| 482 if (stream_writer_) | 526 if (stream_writer_) |
| 483 stream_writer_->RegisterCallback(base::Closure()); | 527 stream_writer_->RegisterCallback(base::Closure()); |
| 484 | 528 |
| 485 UMA_HISTOGRAM_TIMES("SB2.DownloadDuration", | 529 UMA_HISTOGRAM_TIMES("SB2.DownloadDuration", |
| 486 base::TimeTicks::Now() - download_start_time_); | 530 base::TimeTicks::Now() - download_start_time_); |
| 487 } | 531 } |
| 488 | 532 |
| 489 } // namespace content | 533 } // namespace content |
| OLD | NEW |