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" |
| 11 #include "base/memory/weak_ptr.h" |
11 #include "base/message_loop/message_loop_proxy.h" | 12 #include "base/message_loop/message_loop_proxy.h" |
12 #include "base/metrics/histogram.h" | 13 #include "base/metrics/histogram.h" |
13 #include "base/metrics/stats_counters.h" | 14 #include "base/metrics/stats_counters.h" |
14 #include "base/strings/stringprintf.h" | 15 #include "base/strings/stringprintf.h" |
15 #include "content/browser/byte_stream.h" | 16 #include "content/browser/byte_stream.h" |
16 #include "content/browser/download/download_create_info.h" | 17 #include "content/browser/download/download_create_info.h" |
17 #include "content/browser/download/download_interrupt_reasons_impl.h" | 18 #include "content/browser/download/download_interrupt_reasons_impl.h" |
18 #include "content/browser/download/download_manager_impl.h" | 19 #include "content/browser/download/download_manager_impl.h" |
19 #include "content/browser/download/download_request_handle.h" | 20 #include "content/browser/download/download_request_handle.h" |
20 #include "content/browser/download/download_stats.h" | 21 #include "content/browser/download/download_stats.h" |
21 #include "content/browser/loader/resource_dispatcher_host_impl.h" | 22 #include "content/browser/loader/resource_dispatcher_host_impl.h" |
22 #include "content/browser/loader/resource_request_info_impl.h" | 23 #include "content/browser/loader/resource_request_info_impl.h" |
| 24 #include "content/browser/renderer_host/render_view_host_impl.h" |
| 25 #include "content/browser/web_contents/web_contents_impl.h" |
| 26 #include "content/public/browser/browser_context.h" |
23 #include "content/public/browser/browser_thread.h" | 27 #include "content/public/browser/browser_thread.h" |
24 #include "content/public/browser/download_interrupt_reasons.h" | 28 #include "content/public/browser/download_interrupt_reasons.h" |
25 #include "content/public/browser/download_item.h" | 29 #include "content/public/browser/download_item.h" |
26 #include "content/public/browser/download_manager_delegate.h" | 30 #include "content/public/browser/download_manager_delegate.h" |
27 #include "content/public/common/resource_response.h" | 31 #include "content/public/common/resource_response.h" |
28 #include "net/base/io_buffer.h" | 32 #include "net/base/io_buffer.h" |
29 #include "net/base/net_errors.h" | 33 #include "net/base/net_errors.h" |
30 #include "net/http/http_response_headers.h" | 34 #include "net/http/http_response_headers.h" |
31 #include "net/http/http_status_code.h" | 35 #include "net/http/http_status_code.h" |
32 #include "net/url_request/url_request_context.h" | 36 #include "net/url_request/url_request_context.h" |
33 | 37 |
34 namespace content { | 38 namespace content { |
35 namespace { | 39 namespace { |
36 | 40 |
37 void CallStartedCBOnUIThread( | 41 class DownloadResourceHandlerRequestHandle : public DownloadRequestHandle { |
38 const DownloadUrlParameters::OnStartedCallback& started_cb, | 42 public: |
39 DownloadItem* item, | 43 virtual ~DownloadResourceHandlerRequestHandle(); |
40 net::Error error) { | |
41 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
42 | 44 |
43 if (started_cb.is_null()) | 45 DownloadResourceHandlerRequestHandle( |
44 return; | 46 const base::WeakPtr<DownloadResourceHandler>& handler, |
45 started_cb.Run(item, error); | 47 int child_id, |
| 48 int render_view_id, |
| 49 int request_id); |
| 50 |
| 51 // DownloadRequestHandle. |
| 52 virtual void Start(const RequestStartedCallback& started_callback) OVERRIDE; |
| 53 virtual void PauseRequest() const OVERRIDE; |
| 54 virtual void ResumeRequest() const OVERRIDE; |
| 55 virtual void CancelRequest() const OVERRIDE; |
| 56 virtual std::string DebugString() const OVERRIDE; |
| 57 |
| 58 private: |
| 59 base::WeakPtr<DownloadResourceHandler> handler_; |
| 60 |
| 61 // The ID of the child process that started the download. |
| 62 int child_id_; |
| 63 |
| 64 // The ID of the render view that started the download. |
| 65 int render_view_id_; |
| 66 |
| 67 // The ID associated with the request used for the download. |
| 68 int request_id_; |
| 69 |
| 70 DISALLOW_COPY_AND_ASSIGN(DownloadResourceHandlerRequestHandle); |
| 71 }; |
| 72 |
| 73 DownloadResourceHandlerRequestHandle::~DownloadResourceHandlerRequestHandle() {} |
| 74 |
| 75 DownloadResourceHandlerRequestHandle::DownloadResourceHandlerRequestHandle( |
| 76 const base::WeakPtr<DownloadResourceHandler>& handler, |
| 77 int child_id, |
| 78 int render_view_id, |
| 79 int request_id) |
| 80 : handler_(handler), |
| 81 child_id_(child_id), |
| 82 render_view_id_(render_view_id), |
| 83 request_id_(request_id) { |
| 84 DCHECK(handler_.get()); |
| 85 } |
| 86 |
| 87 void DownloadResourceHandlerRequestHandle::Start( |
| 88 const RequestStartedCallback& started_callback) { |
| 89 // Start() shouldn't be getting called this request handle since |
| 90 // DownloadManager::StartDownloadWithActiveRequest() assumes that the request |
| 91 // is already in progress. |
| 92 NOTREACHED(); |
| 93 } |
| 94 |
| 95 void DownloadResourceHandlerRequestHandle::PauseRequest() const { |
| 96 BrowserThread::PostTask( |
| 97 BrowserThread::IO, |
| 98 FROM_HERE, |
| 99 base::Bind(&DownloadResourceHandler::PauseRequest, handler_)); |
| 100 } |
| 101 |
| 102 void DownloadResourceHandlerRequestHandle::ResumeRequest() const { |
| 103 BrowserThread::PostTask( |
| 104 BrowserThread::IO, |
| 105 FROM_HERE, |
| 106 base::Bind(&DownloadResourceHandler::ResumeRequest, handler_)); |
| 107 } |
| 108 |
| 109 void DownloadResourceHandlerRequestHandle::CancelRequest() const { |
| 110 BrowserThread::PostTask( |
| 111 BrowserThread::IO, |
| 112 FROM_HERE, |
| 113 base::Bind(&DownloadResourceHandler::CancelRequest, handler_)); |
| 114 } |
| 115 |
| 116 std::string DownloadResourceHandlerRequestHandle::DebugString() const { |
| 117 return base::StringPrintf( |
| 118 "{" |
| 119 " child_id = %d" |
| 120 " render_view_id = %d" |
| 121 " request_id = %d" |
| 122 "}", |
| 123 child_id_, |
| 124 render_view_id_, |
| 125 request_id_); |
46 } | 126 } |
47 | 127 |
48 // Static function in order to prevent any accidental accesses to | 128 // Static function in order to prevent any accidental accesses to |
49 // DownloadResourceHandler members from the UI thread. | 129 // DownloadResourceHandler members from the UI thread. |
50 static void StartOnUIThread( | 130 static void StartOnUIThread(int child_id, |
51 scoped_ptr<DownloadCreateInfo> info, | 131 int render_view_id, |
52 scoped_ptr<ByteStreamReader> stream, | 132 scoped_ptr<DownloadRequestHandle> handle, |
53 const DownloadUrlParameters::OnStartedCallback& started_cb) { | 133 scoped_ptr<DownloadCreateInfo> info) { |
54 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 134 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
55 | 135 |
56 DownloadManager* download_manager = info->request_handle.GetDownloadManager(); | 136 RenderViewHostImpl* rvh = |
57 if (!download_manager) { | 137 RenderViewHostImpl::FromID(child_id, render_view_id); |
58 // NULL in unittests or if the page closed right after starting the | 138 if (rvh == NULL) |
59 // download. | |
60 if (!started_cb.is_null()) | |
61 started_cb.Run(NULL, net::ERR_ACCESS_DENIED); | |
62 return; | 139 return; |
63 } | 140 RenderProcessHost* rph = rvh->GetProcess(); |
64 | 141 if (rph == NULL) |
65 download_manager->StartDownload(info.Pass(), stream.Pass(), started_cb); | 142 return; |
| 143 BrowserContext* context = rph->GetBrowserContext(); |
| 144 if (context == NULL) |
| 145 return; |
| 146 DownloadManager* download_manager = |
| 147 BrowserContext::GetDownloadManager(context); |
| 148 if (download_manager) |
| 149 download_manager->StartDownloadWithActiveRequest(handle.Pass(), |
| 150 info.Pass()); |
66 } | 151 } |
67 | 152 |
68 } // namespace | 153 } // namespace |
69 | 154 |
70 const int DownloadResourceHandler::kDownloadByteStreamSize = 100 * 1024; | |
71 | |
72 DownloadResourceHandler::DownloadResourceHandler( | 155 DownloadResourceHandler::DownloadResourceHandler( |
73 uint32 id, | 156 net::URLRequest* request) |
74 net::URLRequest* request, | 157 : request_model_(request, make_scoped_ptr(new DownloadSaveInfo())), |
75 const DownloadUrlParameters::OnStartedCallback& started_cb, | 158 render_view_id_(0), // Actually initialized below. |
76 scoped_ptr<DownloadSaveInfo> save_info) | 159 did_defer_request_(false), |
77 : download_id_(id), | |
78 render_view_id_(0), // Actually initialized below. | |
79 content_length_(0), | |
80 request_(request), | |
81 started_cb_(started_cb), | |
82 save_info_(save_info.Pass()), | |
83 last_buffer_size_(0), | |
84 bytes_read_(0), | |
85 pause_count_(0), | |
86 was_deferred_(false), | |
87 on_response_started_called_(false) { | 160 on_response_started_called_(false) { |
88 ResourceRequestInfoImpl* info(ResourceRequestInfoImpl::ForRequest(request)); | 161 ResourceRequestInfoImpl* info(ResourceRequestInfoImpl::ForRequest(request)); |
89 global_id_ = info->GetGlobalRequestID(); | 162 global_id_ = info->GetGlobalRequestID(); |
90 render_view_id_ = info->GetRouteID(); | 163 render_view_id_ = info->GetRouteID(); |
91 | |
92 RecordDownloadCount(UNTHROTTLED_COUNT); | |
93 } | 164 } |
94 | 165 |
95 bool DownloadResourceHandler::OnUploadProgress(int request_id, | 166 bool DownloadResourceHandler::OnUploadProgress(int request_id, |
96 uint64 position, | 167 uint64 position, |
97 uint64 size) { | 168 uint64 size) { |
98 return true; | 169 return true; |
99 } | 170 } |
100 | 171 |
101 bool DownloadResourceHandler::OnRequestRedirected( | 172 bool DownloadResourceHandler::OnRequestRedirected( |
102 int request_id, | 173 int request_id, |
103 const GURL& url, | 174 const GURL& url, |
104 ResourceResponse* response, | 175 ResourceResponse* response, |
105 bool* defer) { | 176 bool* defer) { |
106 // We treat a download as a main frame load, and thus update the policy URL | 177 request_model_.OnRequestRedirected(url); |
107 // on redirects. | |
108 request_->set_first_party_for_cookies(url); | |
109 return true; | 178 return true; |
110 } | 179 } |
111 | 180 |
112 // Send the download creation information to the download thread. | 181 // Send the download creation information to the download thread. |
113 bool DownloadResourceHandler::OnResponseStarted( | 182 bool DownloadResourceHandler::OnResponseStarted( |
114 int request_id, | 183 int request_id, |
115 ResourceResponse* response, | 184 ResourceResponse* response, |
116 bool* defer) { | 185 bool* defer) { |
117 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 186 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
118 // There can be only one (call) | 187 // There can be only one (call) |
119 DCHECK(!on_response_started_called_); | 188 DCHECK(!on_response_started_called_); |
120 on_response_started_called_ = true; | 189 on_response_started_called_ = true; |
121 | 190 |
122 VLOG(20) << __FUNCTION__ << "()" << DebugString() | 191 DVLOG(20) << __FUNCTION__ << "()" << DebugString() |
123 << " request_id = " << request_id; | 192 << " request_id = " << request_id; |
124 download_start_time_ = base::TimeTicks::Now(); | |
125 | |
126 // If it's a download, we don't want to poison the cache with it. | |
127 request_->StopCaching(); | |
128 | |
129 // Lower priority as well, so downloads don't contend for resources | |
130 // with main frames. | |
131 request_->SetPriority(net::IDLE); | |
132 | |
133 std::string content_disposition; | |
134 request_->GetResponseHeaderByName("content-disposition", | |
135 &content_disposition); | |
136 SetContentDisposition(content_disposition); | |
137 SetContentLength(response->head.content_length); | |
138 | 193 |
139 const ResourceRequestInfoImpl* request_info = | 194 const ResourceRequestInfoImpl* request_info = |
140 ResourceRequestInfoImpl::ForRequest(request_); | 195 ResourceRequestInfoImpl::ForRequest(request_model_.request()); |
141 | 196 |
142 // Deleted in DownloadManager. | 197 DownloadRequestModel::ResumeRequestCallback resume_callback = |
143 scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo( | 198 base::Bind(&DownloadResourceHandler::ResumeRequest, AsWeakPtr()); |
144 base::Time::Now(), content_length_, | 199 scoped_ptr<DownloadCreateInfo> create_info = |
145 request_->net_log(), request_info->HasUserGesture(), | 200 request_model_.OnResponseStarted(response->head.mime_type, |
146 request_info->GetPageTransition())); | 201 request_info->HasUserGesture(), |
| 202 request_info->GetPageTransition(), |
| 203 resume_callback); |
147 | 204 |
148 // Create the ByteStream for sending data to the download sink. | 205 scoped_ptr<DownloadRequestHandle> request_handle( |
149 scoped_ptr<ByteStreamReader> stream_reader; | 206 new DownloadResourceHandlerRequestHandle(AsWeakPtr(), |
150 CreateByteStream( | 207 global_id_.child_id, |
151 base::MessageLoopProxy::current(), | 208 render_view_id_, |
152 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), | 209 global_id_.request_id)); |
153 kDownloadByteStreamSize, &stream_writer_, &stream_reader); | 210 BrowserThread::PostTask(BrowserThread::UI, |
154 stream_writer_->RegisterCallback( | 211 FROM_HERE, |
155 base::Bind(&DownloadResourceHandler::ResumeRequest, AsWeakPtr())); | 212 base::Bind(&StartOnUIThread, |
156 | 213 global_id_.child_id, |
157 info->download_id = download_id_; | 214 render_view_id_, |
158 info->url_chain = request_->url_chain(); | 215 base::Passed(&request_handle), |
159 info->referrer_url = GURL(request_->referrer()); | 216 base::Passed(&create_info))); |
160 info->start_time = base::Time::Now(); | |
161 info->total_bytes = content_length_; | |
162 info->has_user_gesture = request_info->HasUserGesture(); | |
163 info->content_disposition = content_disposition_; | |
164 info->mime_type = response->head.mime_type; | |
165 info->remote_address = request_->GetSocketAddress().host(); | |
166 RecordDownloadMimeType(info->mime_type); | |
167 RecordDownloadContentDisposition(info->content_disposition); | |
168 | |
169 info->request_handle = | |
170 DownloadRequestHandle(AsWeakPtr(), global_id_.child_id, | |
171 render_view_id_, global_id_.request_id); | |
172 | |
173 // Get the last modified time and etag. | |
174 const net::HttpResponseHeaders* headers = request_->response_headers(); | |
175 if (headers) { | |
176 std::string last_modified_hdr; | |
177 if (headers->EnumerateHeader(NULL, "Last-Modified", &last_modified_hdr)) | |
178 info->last_modified = last_modified_hdr; | |
179 if (headers->EnumerateHeader(NULL, "ETag", &etag_)) | |
180 info->etag = etag_; | |
181 | |
182 int status = headers->response_code(); | |
183 if (2 == status / 100 && status != net::HTTP_PARTIAL_CONTENT) { | |
184 // Success & not range response; if we asked for a range, we didn't | |
185 // get it--reset the file pointers to reflect that. | |
186 save_info_->offset = 0; | |
187 save_info_->hash_state = ""; | |
188 } | |
189 } | |
190 | |
191 std::string content_type_header; | |
192 if (!response->head.headers.get() || | |
193 !response->head.headers->GetMimeType(&content_type_header)) | |
194 content_type_header = ""; | |
195 info->original_mime_type = content_type_header; | |
196 | |
197 if (!response->head.headers.get() || | |
198 !response->head.headers->EnumerateHeader( | |
199 NULL, "Accept-Ranges", &accept_ranges_)) { | |
200 accept_ranges_ = ""; | |
201 } | |
202 | |
203 info->save_info = save_info_.Pass(); | |
204 | |
205 BrowserThread::PostTask( | |
206 BrowserThread::UI, FROM_HERE, | |
207 base::Bind(&StartOnUIThread, | |
208 base::Passed(&info), | |
209 base::Passed(&stream_reader), | |
210 // Pass to StartOnUIThread so that variable | |
211 // access is always on IO thread but function | |
212 // is called on UI thread. | |
213 started_cb_)); | |
214 // Guaranteed to be called in StartOnUIThread | |
215 started_cb_.Reset(); | |
216 | |
217 return true; | 217 return true; |
218 } | 218 } |
219 | 219 |
220 void DownloadResourceHandler::CallStartedCB( | |
221 DownloadItem* item, net::Error error) { | |
222 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
223 if (started_cb_.is_null()) | |
224 return; | |
225 BrowserThread::PostTask( | |
226 BrowserThread::UI, FROM_HERE, | |
227 base::Bind(&CallStartedCBOnUIThread, started_cb_, item, error)); | |
228 started_cb_.Reset(); | |
229 } | |
230 | |
231 bool DownloadResourceHandler::OnWillStart(int request_id, | 220 bool DownloadResourceHandler::OnWillStart(int request_id, |
232 const GURL& url, | 221 const GURL& url, |
233 bool* defer) { | 222 bool* defer) { |
234 return true; | 223 return true; |
235 } | 224 } |
236 | 225 |
237 // Create a new buffer, which will be handed to the download thread for file | 226 // Create a new buffer, which will be handed to the download thread for file |
238 // writing and deletion. | 227 // writing and deletion. |
239 bool DownloadResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf, | 228 bool DownloadResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf, |
240 int* buf_size, int min_size) { | 229 int* buf_size, int min_size) { |
241 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 230 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
242 DCHECK(buf && buf_size); | 231 request_model_.OnWillRead(buf, buf_size, min_size); |
243 DCHECK(!read_buffer_.get()); | |
244 | |
245 *buf_size = min_size < 0 ? kReadBufSize : min_size; | |
246 last_buffer_size_ = *buf_size; | |
247 read_buffer_ = new net::IOBuffer(*buf_size); | |
248 *buf = read_buffer_.get(); | |
249 return true; | 232 return true; |
250 } | 233 } |
251 | 234 |
252 // Pass the buffer to the download file writer. | 235 // Pass the buffer to the download file writer. |
253 bool DownloadResourceHandler::OnReadCompleted(int request_id, int bytes_read, | 236 bool DownloadResourceHandler::OnReadCompleted(int request_id, int bytes_read, |
254 bool* defer) { | 237 bool* defer) { |
255 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 238 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
256 DCHECK(read_buffer_.get()); | 239 DownloadRequestModel::ReadState read_state = |
257 | 240 request_model_.OnReadCompleted(bytes_read); |
258 base::TimeTicks now(base::TimeTicks::Now()); | 241 if (read_state == DownloadRequestModel::WAIT_FOR_RESUME) |
259 if (!last_read_time_.is_null()) { | 242 *defer = did_defer_request_ = true; |
260 double seconds_since_last_read = (now - last_read_time_).InSecondsF(); | |
261 if (now == last_read_time_) | |
262 // Use 1/10 ms as a "very small number" so that we avoid | |
263 // divide-by-zero error and still record a very high potential bandwidth. | |
264 seconds_since_last_read = 0.00001; | |
265 | |
266 double actual_bandwidth = (bytes_read)/seconds_since_last_read; | |
267 double potential_bandwidth = last_buffer_size_/seconds_since_last_read; | |
268 RecordBandwidth(actual_bandwidth, potential_bandwidth); | |
269 } | |
270 last_read_time_ = now; | |
271 | |
272 if (!bytes_read) | |
273 return true; | |
274 bytes_read_ += bytes_read; | |
275 DCHECK(read_buffer_.get()); | |
276 | |
277 // Take the data ship it down the stream. If the stream is full, pause the | |
278 // request; the stream callback will resume it. | |
279 if (!stream_writer_->Write(read_buffer_, bytes_read)) { | |
280 PauseRequest(); | |
281 *defer = was_deferred_ = true; | |
282 last_stream_pause_time_ = now; | |
283 } | |
284 | |
285 read_buffer_ = NULL; // Drop our reference. | |
286 | |
287 if (pause_count_ > 0) | |
288 *defer = was_deferred_ = true; | |
289 | |
290 return true; | 243 return true; |
291 } | 244 } |
292 | 245 |
293 bool DownloadResourceHandler::OnResponseCompleted( | 246 bool DownloadResourceHandler::OnResponseCompleted( |
294 int request_id, | 247 int request_id, |
295 const net::URLRequestStatus& status, | 248 const net::URLRequestStatus& status, |
296 const std::string& security_info) { | 249 const std::string& security_info) { |
297 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 250 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
298 int response_code = status.is_success() ? request_->GetResponseCode() : 0; | 251 DVLOG(20) << __FUNCTION__ << "()" << DebugString() |
299 VLOG(20) << __FUNCTION__ << "()" << DebugString() | 252 << " request_id = " << request_id |
300 << " request_id = " << request_id | 253 << " status.status() = " << status.status() |
301 << " status.status() = " << status.status() | 254 << " status.error() = " << status.error(); |
302 << " status.error() = " << status.error() | 255 request_model_.OnResponseCompleted(); |
303 << " response_code = " << response_code; | |
304 | |
305 net::Error error_code = net::OK; | |
306 if (status.status() == net::URLRequestStatus::FAILED || | |
307 // Note cancels as failures too. | |
308 status.status() == net::URLRequestStatus::CANCELED) { | |
309 error_code = static_cast<net::Error>(status.error()); // Normal case. | |
310 // Make sure that at least the fact of failure comes through. | |
311 if (error_code == net::OK) | |
312 error_code = net::ERR_FAILED; | |
313 } | |
314 | |
315 // ERR_CONTENT_LENGTH_MISMATCH and ERR_INCOMPLETE_CHUNKED_ENCODING are | |
316 // allowed since a number of servers in the wild close the connection too | |
317 // early by mistake. Other browsers - IE9, Firefox 11.0, and Safari 5.1.4 - | |
318 // treat downloads as complete in both cases, so we follow their lead. | |
319 if (error_code == net::ERR_CONTENT_LENGTH_MISMATCH || | |
320 error_code == net::ERR_INCOMPLETE_CHUNKED_ENCODING) { | |
321 error_code = net::OK; | |
322 } | |
323 DownloadInterruptReason reason = | |
324 ConvertNetErrorToInterruptReason( | |
325 error_code, DOWNLOAD_INTERRUPT_FROM_NETWORK); | |
326 | |
327 if (status.status() == net::URLRequestStatus::CANCELED && | |
328 status.error() == net::ERR_ABORTED) { | |
329 // CANCELED + ERR_ABORTED == something outside of the network | |
330 // stack cancelled the request. There aren't that many things that | |
331 // could do this to a download request (whose lifetime is separated from | |
332 // the tab from which it came). We map this to USER_CANCELLED as the | |
333 // case we know about (system suspend because of laptop close) corresponds | |
334 // to a user action. | |
335 // TODO(ahendrickson) -- Find a better set of codes to use here, as | |
336 // CANCELED/ERR_ABORTED can occur for reasons other than user cancel. | |
337 reason = DOWNLOAD_INTERRUPT_REASON_USER_CANCELED; | |
338 } | |
339 | |
340 if (status.is_success() && | |
341 reason == DOWNLOAD_INTERRUPT_REASON_NONE && | |
342 request_->response_headers()) { | |
343 // Handle server's response codes. | |
344 switch(response_code) { | |
345 case -1: // Non-HTTP request. | |
346 case net::HTTP_OK: | |
347 case net::HTTP_CREATED: | |
348 case net::HTTP_ACCEPTED: | |
349 case net::HTTP_NON_AUTHORITATIVE_INFORMATION: | |
350 case net::HTTP_RESET_CONTENT: | |
351 case net::HTTP_PARTIAL_CONTENT: | |
352 // Expected successful codes. | |
353 break; | |
354 case net::HTTP_NO_CONTENT: | |
355 case net::HTTP_NOT_FOUND: | |
356 reason = DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT; | |
357 break; | |
358 case net::HTTP_PRECONDITION_FAILED: | |
359 // Failed our 'If-Unmodified-Since' or 'If-Match'; see | |
360 // download_manager_impl.cc BeginDownload() | |
361 reason = DOWNLOAD_INTERRUPT_REASON_SERVER_PRECONDITION; | |
362 break; | |
363 case net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE: | |
364 // Retry by downloading from the start automatically: | |
365 // If we haven't received data when we get this error, we won't. | |
366 reason = DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE; | |
367 break; | |
368 default: // All other errors. | |
369 // Redirection and informational codes should have been handled earlier | |
370 // in the stack. | |
371 DCHECK_NE(3, response_code / 100); | |
372 DCHECK_NE(1, response_code / 100); | |
373 reason = DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED; | |
374 break; | |
375 } | |
376 } | |
377 | |
378 RecordAcceptsRanges(accept_ranges_, bytes_read_, etag_); | |
379 RecordNetworkBlockage( | |
380 base::TimeTicks::Now() - download_start_time_, total_pause_time_); | |
381 | |
382 CallStartedCB(NULL, error_code); | |
383 | |
384 // Send the info down the stream. Conditional is in case we get | |
385 // OnResponseCompleted without OnResponseStarted. | |
386 if (stream_writer_) | |
387 stream_writer_->Close(reason); | |
388 | |
389 // If the error mapped to something unknown, record it so that | |
390 // we can drill down. | |
391 if (reason == DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED) { | |
392 UMA_HISTOGRAM_CUSTOM_ENUMERATION("Download.MapErrorNetworkFailed", | |
393 std::abs(status.error()), | |
394 net::GetAllErrorCodesForUma()); | |
395 } | |
396 | |
397 stream_writer_.reset(); // We no longer need the stream. | |
398 read_buffer_ = NULL; | |
399 | |
400 return true; | 256 return true; |
401 } | 257 } |
402 | 258 |
403 void DownloadResourceHandler::OnDataDownloaded( | 259 void DownloadResourceHandler::OnDataDownloaded( |
404 int request_id, | 260 int request_id, |
405 int bytes_downloaded) { | 261 int bytes_downloaded) { |
406 NOTREACHED(); | 262 NOTREACHED(); |
407 } | 263 } |
408 | 264 |
409 // If the content-length header is not present (or contains something other | |
410 // than numbers), the incoming content_length is -1 (unknown size). | |
411 // Set the content length to 0 to indicate unknown size to DownloadManager. | |
412 void DownloadResourceHandler::SetContentLength(const int64& content_length) { | |
413 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
414 content_length_ = 0; | |
415 if (content_length > 0) | |
416 content_length_ = content_length; | |
417 } | |
418 | |
419 void DownloadResourceHandler::SetContentDisposition( | |
420 const std::string& content_disposition) { | |
421 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
422 content_disposition_ = content_disposition; | |
423 } | |
424 | |
425 void DownloadResourceHandler::PauseRequest() { | 265 void DownloadResourceHandler::PauseRequest() { |
426 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 266 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
427 | 267 request_model_.OnPauseRequest(); |
428 ++pause_count_; | |
429 } | 268 } |
430 | 269 |
431 void DownloadResourceHandler::ResumeRequest() { | 270 void DownloadResourceHandler::ResumeRequest() { |
432 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 271 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
433 DCHECK_LT(0, pause_count_); | 272 if (request_model_.OnResumeRequest() == DownloadRequestModel::READY_TO_READ && |
434 | 273 did_defer_request_) { |
435 --pause_count_; | 274 did_defer_request_ = false; |
436 | 275 controller()->Resume(); |
437 if (!was_deferred_) | |
438 return; | |
439 if (pause_count_ > 0) | |
440 return; | |
441 | |
442 was_deferred_ = false; | |
443 if (!last_stream_pause_time_.is_null()) { | |
444 total_pause_time_ += (base::TimeTicks::Now() - last_stream_pause_time_); | |
445 last_stream_pause_time_ = base::TimeTicks(); | |
446 } | 276 } |
447 | |
448 controller()->Resume(); | |
449 } | 277 } |
450 | 278 |
451 void DownloadResourceHandler::CancelRequest() { | 279 void DownloadResourceHandler::CancelRequest() { |
452 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 280 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
453 | 281 |
454 ResourceDispatcherHostImpl::Get()->CancelRequest( | 282 ResourceDispatcherHostImpl::Get()->CancelRequest( |
455 global_id_.child_id, | 283 global_id_.child_id, |
456 global_id_.request_id, | 284 global_id_.request_id, |
457 false); | 285 false); |
458 } | 286 } |
459 | 287 |
460 std::string DownloadResourceHandler::DebugString() const { | 288 std::string DownloadResourceHandler::DebugString() const { |
461 return base::StringPrintf("{" | 289 return base::StringPrintf( |
462 " url_ = " "\"%s\"" | 290 "{" |
463 " global_id_ = {" | 291 " request = %s" |
464 " child_id = " "%d" | 292 " global_id_ = {" |
465 " request_id = " "%d" | 293 " child_id = %d" |
466 " }" | 294 " request_id = %d" |
467 " render_view_id_ = " "%d" | 295 " }" |
468 " }", | 296 " render_view_id_ = %d" |
469 request_ ? | 297 " }", |
470 request_->url().spec().c_str() : | 298 request_model_.DebugString().c_str(), |
471 "<NULL request>", | 299 global_id_.child_id, |
472 global_id_.child_id, | 300 global_id_.request_id, |
473 global_id_.request_id, | 301 render_view_id_); |
474 render_view_id_); | |
475 } | 302 } |
476 | 303 |
477 DownloadResourceHandler::~DownloadResourceHandler() { | 304 DownloadResourceHandler::~DownloadResourceHandler() { |
478 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 305 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
479 | |
480 // This won't do anything if the callback was called before. | |
481 // If it goes through, it will likely be because OnWillStart() returned | |
482 // false somewhere in the chain of resource handlers. | |
483 CallStartedCB(NULL, net::ERR_ACCESS_DENIED); | |
484 | |
485 // Remove output stream callback if a stream exists. | |
486 if (stream_writer_) | |
487 stream_writer_->RegisterCallback(base::Closure()); | |
488 | |
489 UMA_HISTOGRAM_TIMES("SB2.DownloadDuration", | |
490 base::TimeTicks::Now() - download_start_time_); | |
491 } | 306 } |
492 | 307 |
493 } // namespace content | 308 } // namespace content |
OLD | NEW |