OLD | NEW |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 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 "net/url_request/url_request_inet_job.h" | 5 #include "net/url_request/url_request_inet_job.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/message_loop.h" | 9 #include "base/message_loop.h" |
10 #include "base/string_util.h" | 10 #include "base/string_util.h" |
11 #include "googleurl/src/gurl.h" | 11 #include "googleurl/src/gurl.h" |
12 #include "net/base/auth.h" | 12 #include "net/base/auth.h" |
13 #include "net/base/net_errors.h" | 13 #include "net/base/net_errors.h" |
14 #include "net/base/net_util.h" | 14 #include "net/base/net_util.h" |
15 #include "net/base/wininet_util.h" | 15 #include "net/base/wininet_util.h" |
16 #include "net/url_request/url_request_error_job.h" | 16 #include "net/url_request/url_request_error_job.h" |
17 #include "net/url_request/url_request_ftp_job.h" | 17 #include "net/url_request/url_request_ftp_job.h" |
18 #include "net/url_request/url_request_job_metrics.h" | 18 #include "net/url_request/url_request_job_metrics.h" |
19 #include "net/url_request/url_request_job_tracker.h" | 19 #include "net/url_request/url_request_job_tracker.h" |
20 | 20 |
| 21 using net::WinInetUtil; |
| 22 |
21 // | 23 // |
22 // HOW ASYNC IO WORKS | 24 // HOW ASYNC IO WORKS |
23 // | 25 // |
24 // The URLRequestInet* classes are now fully asynchronous. This means that | 26 // The URLRequestInet* classes are now fully asynchronous. This means that |
25 // all IO operations pass buffers into WinInet, and as WinInet completes those | 27 // all IO operations pass buffers into WinInet, and as WinInet completes those |
26 // IO requests, it will fill the buffer, and then callback to the client. | 28 // IO requests, it will fill the buffer, and then callback to the client. |
27 // Asynchronous IO Operations include: | 29 // Asynchronous IO Operations include: |
28 // HttpSendRequestEx | 30 // HttpSendRequestEx |
29 // InternetWriteFile | 31 // InternetWriteFile |
30 // HttpEndRequest | 32 // HttpEndRequest |
(...skipping 23 matching lines...) Expand all Loading... |
54 // *all* asynchronous IO completions will come to this method, even | 56 // *all* asynchronous IO completions will come to this method, even |
55 // those asynchronous IOs which may have been issued by a base class. | 57 // those asynchronous IOs which may have been issued by a base class. |
56 // For example, URLRequestInetJob has methods which Read from the | 58 // For example, URLRequestInetJob has methods which Read from the |
57 // connection asynchronously. Once URLRequestHttpJob overrides | 59 // connection asynchronously. Once URLRequestHttpJob overrides |
58 // OnIOComplete (so that it can receive its own async IO callbacks) | 60 // OnIOComplete (so that it can receive its own async IO callbacks) |
59 // it will also receive the URLRequestInetJob async IO callbacks. To | 61 // it will also receive the URLRequestInetJob async IO callbacks. To |
60 // make this work, the derived class must track its own state, and call | 62 // make this work, the derived class must track its own state, and call |
61 // the base class' version of OnIOComplete if appropriate. | 63 // the base class' version of OnIOComplete if appropriate. |
62 // | 64 // |
63 | 65 |
64 | 66 COMPILE_ASSERT( |
65 using namespace std; | 67 sizeof(URLRequestInetJob::AsyncResult) == sizeof(INTERNET_ASYNC_RESULT), |
66 | 68 async_result_inconsistent_size); |
67 using net::WinInetUtil; | |
68 | |
69 static const wchar_t kWndClass[] = L"URLRequestMessageWnd"; | |
70 | |
71 // Custom message types for use with message_hwnd | |
72 enum { | |
73 MSG_REQUEST_COMPLETE = WM_USER + 1 | |
74 }; | |
75 | 69 |
76 HINTERNET URLRequestInetJob::the_internet_ = NULL; | 70 HINTERNET URLRequestInetJob::the_internet_ = NULL; |
77 HWND URLRequestInetJob::message_hwnd_ = NULL; | |
78 #ifndef NDEBUG | 71 #ifndef NDEBUG |
79 MessageLoop* URLRequestInetJob::my_message_loop_ = NULL; | 72 MessageLoop* URLRequestInetJob::my_message_loop_ = NULL; |
80 #endif | 73 #endif |
81 | 74 |
82 URLRequestInetJob::URLRequestInetJob(URLRequest* request) | 75 URLRequestInetJob::URLRequestInetJob(URLRequest* request) |
83 : URLRequestJob(request), | 76 : URLRequestJob(request), |
84 connection_handle_(NULL), | 77 connection_handle_(NULL), |
85 request_handle_(NULL), | 78 request_handle_(NULL), |
86 last_error_(ERROR_SUCCESS), | 79 last_error_(ERROR_SUCCESS), |
87 is_waiting_(false), | 80 is_waiting_(false), |
88 read_in_progress_(false) { | 81 read_in_progress_(false), |
| 82 loop_(MessageLoop::current()) { |
89 // TODO(darin): we should re-create the internet if the UA string changes, | 83 // TODO(darin): we should re-create the internet if the UA string changes, |
90 // but we have to be careful about existing users of this internet. | 84 // but we have to be careful about existing users of this internet. |
91 if (!the_internet_) { | 85 if (!the_internet_) { |
92 InitializeTheInternet( | 86 InitializeTheInternet( |
93 request->context() ? request->context()->user_agent() : std::string()); | 87 request->context() ? request->context()->user_agent() : std::string()); |
94 } | 88 } |
95 #ifndef NDEBUG | 89 #ifndef NDEBUG |
96 DCHECK(MessageLoop::current() == my_message_loop_) << | 90 DCHECK(MessageLoop::current() == my_message_loop_) << |
97 "All URLRequests should happen on the same thread"; | 91 "All URLRequests should happen on the same thread"; |
98 #endif | 92 #endif |
99 } | 93 } |
100 | 94 |
101 URLRequestInetJob::~URLRequestInetJob() { | 95 URLRequestInetJob::~URLRequestInetJob() { |
102 DCHECK(!request_) << "request should be detached at this point"; | 96 DCHECK(!request_) << "request should be detached at this point"; |
103 | 97 |
104 // The connections may have already been cleaned up. It is ok to call | 98 // The connections may have already been cleaned up. It is ok to call |
105 // CleanupConnection again to make sure the resource is properly released. | 99 // CleanupConnection again to make sure the resource is properly released. |
106 // See bug 684997. | 100 // See bug 684997. |
107 CleanupConnection(); | 101 CleanupConnection(); |
108 } | 102 } |
109 | 103 |
110 void URLRequestInetJob::Kill() { | 104 void URLRequestInetJob::Kill() { |
111 CleanupConnection(); | 105 CleanupConnection(); |
112 | 106 |
| 107 { |
| 108 AutoLock locked(loop_lock_); |
| 109 loop_ = NULL; |
| 110 } |
| 111 |
113 // Dispatch the NotifyDone message to the URLRequest | 112 // Dispatch the NotifyDone message to the URLRequest |
114 URLRequestJob::Kill(); | 113 URLRequestJob::Kill(); |
115 } | 114 } |
116 | 115 |
117 void URLRequestInetJob::SetAuth(const wstring& username, | 116 void URLRequestInetJob::SetAuth(const std::wstring& username, |
118 const wstring& password) { | 117 const std::wstring& password) { |
119 DCHECK((proxy_auth_ && proxy_auth_->state == net::AUTH_STATE_NEED_AUTH) || | 118 DCHECK((proxy_auth_ && proxy_auth_->state == net::AUTH_STATE_NEED_AUTH) || |
120 (server_auth_ && server_auth_->state == net::AUTH_STATE_NEED_AUTH)); | 119 (server_auth_ && server_auth_->state == net::AUTH_STATE_NEED_AUTH)); |
121 | 120 |
122 // Proxy gets set first, then WWW. | 121 // Proxy gets set first, then WWW. |
123 net::AuthData* auth = | 122 net::AuthData* auth = |
124 (proxy_auth_ && proxy_auth_->state == net::AUTH_STATE_NEED_AUTH ? | 123 (proxy_auth_ && proxy_auth_->state == net::AUTH_STATE_NEED_AUTH ? |
125 proxy_auth_.get() : server_auth_.get()); | 124 proxy_auth_.get() : server_auth_.get()); |
126 | 125 |
127 if (auth) { | 126 if (auth) { |
128 auth->state = net::AUTH_STATE_HAVE_AUTH; | 127 auth->state = net::AUTH_STATE_HAVE_AUTH; |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
211 // Whether we had an error or the request is pending. | 210 // Whether we had an error or the request is pending. |
212 // Both of these cases return false. | 211 // Both of these cases return false. |
213 return false; | 212 return false; |
214 } | 213 } |
215 | 214 |
216 void URLRequestInetJob::CallOnIOComplete(const AsyncResult& result) { | 215 void URLRequestInetJob::CallOnIOComplete(const AsyncResult& result) { |
217 // It's important to clear this flag before calling OnIOComplete | 216 // It's important to clear this flag before calling OnIOComplete |
218 is_waiting_ = false; | 217 is_waiting_ = false; |
219 | 218 |
220 // the job could have completed with an error while the message was pending | 219 // the job could have completed with an error while the message was pending |
221 if (is_done()) { | 220 if (!is_done()) { |
222 Release(); // may destroy self if last reference | 221 // Verify that our status is currently set to IO_PENDING and |
223 return; | 222 // reset it on success. |
| 223 DCHECK(GetStatus().is_io_pending()); |
| 224 if (result.dwResult && result.dwError == 0) |
| 225 SetStatus(URLRequestStatus()); |
| 226 |
| 227 OnIOComplete(result); |
224 } | 228 } |
225 | 229 |
226 // Verify that our status is currently set to IO_PENDING and | |
227 // reset it on success. | |
228 DCHECK(GetStatus().is_io_pending()); | |
229 if (result.dwResult && result.dwError == 0) | |
230 SetStatus(URLRequestStatus()); | |
231 | |
232 OnIOComplete(result); | |
233 | |
234 Release(); // may destroy self if last reference | 230 Release(); // may destroy self if last reference |
235 } | 231 } |
236 | 232 |
237 bool URLRequestInetJob::ProcessRequestError(int error) { | 233 bool URLRequestInetJob::ProcessRequestError(int error) { |
238 if (error == ERROR_IO_PENDING) { | 234 if (error == ERROR_IO_PENDING) { |
239 DLOG(INFO) << "waiting for WinInet call to complete"; | 235 DLOG(INFO) << "waiting for WinInet call to complete"; |
240 AddRef(); // balanced in CallOnIOComplete | 236 AddRef(); // balanced in CallOnIOComplete |
241 is_waiting_ = true; | 237 is_waiting_ = true; |
242 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); | 238 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); |
243 return true; | 239 return true; |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
301 // | 297 // |
302 // We assert that the error is either of these two because we aren't sure | 298 // We assert that the error is either of these two because we aren't sure |
303 // if any other error values could also indicate this bogus condition, and | 299 // if any other error values could also indicate this bogus condition, and |
304 // we want to notice if we do something wrong that causes a real error. | 300 // we want to notice if we do something wrong that causes a real error. |
305 DWORD last_error = GetLastError(); | 301 DWORD last_error = GetLastError(); |
306 DCHECK(last_error == ERROR_INVALID_HANDLE) << | 302 DCHECK(last_error == ERROR_INVALID_HANDLE) << |
307 "Unknown error when closing handle, possibly leaking job"; | 303 "Unknown error when closing handle, possibly leaking job"; |
308 if (ERROR_IO_PENDING == last_error) { | 304 if (ERROR_IO_PENDING == last_error) { |
309 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); | 305 SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); |
310 | 306 |
311 async_result_.dwError = ERROR_INTERNET_CONNECTION_ABORTED; | 307 AsyncResult result; |
312 async_result_.dwResult = reinterpret_cast<DWORD_PTR>(handle); | 308 result.dwError = ERROR_INTERNET_CONNECTION_ABORTED; |
| 309 result.dwResult = reinterpret_cast<DWORD_PTR>(handle); |
313 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( | 310 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( |
314 this, &URLRequestInetJob::CallOnIOComplete, async_result_)); | 311 this, &URLRequestInetJob::CallOnIOComplete, result)); |
315 } | 312 } |
316 } | 313 } |
317 } | 314 } |
318 | 315 |
319 // static | 316 // static |
320 HINTERNET URLRequestInetJob::GetTheInternet() { | 317 HINTERNET URLRequestInetJob::GetTheInternet() { |
321 return the_internet_; | 318 return the_internet_; |
322 } | 319 } |
323 | 320 |
324 // static | 321 // static |
325 void URLRequestInetJob::InitializeTheInternet(const std::string& user_agent) { | 322 void URLRequestInetJob::InitializeTheInternet(const std::string& user_agent) { |
326 // construct message window for processsing | |
327 HINSTANCE hinst = GetModuleHandle(NULL); | |
328 | |
329 WNDCLASSEX wc = {0}; | |
330 wc.cbSize = sizeof(wc); | |
331 wc.lpfnWndProc = URLRequestWndProc; | |
332 wc.hInstance = hinst; | |
333 wc.lpszClassName = kWndClass; | |
334 RegisterClassEx(&wc); | |
335 | |
336 message_hwnd_ = CreateWindow(kWndClass, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, | |
337 hinst, 0); | |
338 if (!message_hwnd_) { | |
339 NOTREACHED() << "error: " << GetLastError(); | |
340 return; | |
341 } | |
342 | |
343 // Hack attack. We are hitting a deadlock in wininet deinitialization. | 323 // Hack attack. We are hitting a deadlock in wininet deinitialization. |
344 // What is happening is that when we deinitialize, FreeLibrary will be | 324 // What is happening is that when we deinitialize, FreeLibrary will be |
345 // called on wininet. The loader lock is held, and wininet!DllMain is | 325 // called on wininet. The loader lock is held, and wininet!DllMain is |
346 // called. The problem is that wininet tries to do a bunch of cleanup | 326 // called. The problem is that wininet tries to do a bunch of cleanup |
347 // in their DllMain, including calling ICAsyncThread::~ICASyncThread. | 327 // in their DllMain, including calling ICAsyncThread::~ICASyncThread. |
348 // This tries to shutdown the "select thread", and then does a | 328 // This tries to shutdown the "select thread", and then does a |
349 // WaitForSingleObject on the thread with a 5 sec timeout. However the | 329 // WaitForSingleObject on the thread with a 5 sec timeout. However the |
350 // thread they are waiting for cannot exit because the thread shutdown | 330 // thread they are waiting for cannot exit because the thread shutdown |
351 // routine (LdrShutdownThread) is trying to acquire the loader lock. | 331 // routine (LdrShutdownThread) is trying to acquire the loader lock. |
352 // This causes chrome.exe to hang for 5 seconds on shutdown before the | 332 // This causes chrome.exe to hang for 5 seconds on shutdown before the |
(...skipping 13 matching lines...) Expand all Loading... |
366 // Keep track of this message loop so we can catch callers who don't make | 346 // Keep track of this message loop so we can catch callers who don't make |
367 // requests on the same thread. Only do this in debug mode; in release mode | 347 // requests on the same thread. Only do this in debug mode; in release mode |
368 // my_message_loop_ doesn't exist. | 348 // my_message_loop_ doesn't exist. |
369 #ifndef NDEBUG | 349 #ifndef NDEBUG |
370 DCHECK(!my_message_loop_) << "InitializeTheInternet() called twice"; | 350 DCHECK(!my_message_loop_) << "InitializeTheInternet() called twice"; |
371 DCHECK(my_message_loop_ = MessageLoop::current()); | 351 DCHECK(my_message_loop_ = MessageLoop::current()); |
372 #endif | 352 #endif |
373 } | 353 } |
374 | 354 |
375 // static | 355 // static |
376 LRESULT CALLBACK URLRequestInetJob::URLRequestWndProc(HWND hwnd, | |
377 UINT message, | |
378 WPARAM wparam, | |
379 LPARAM lparam) { | |
380 URLRequestInetJob* job = reinterpret_cast<URLRequestInetJob*>(wparam); | |
381 HINTERNET handle = reinterpret_cast<HINTERNET>(lparam); | |
382 | |
383 switch (message) { | |
384 case MSG_REQUEST_COMPLETE: { | |
385 // The callback will be reset if we have closed the handle and deleted | |
386 // the job instance. Call CallOnIOComplete only if the handle still | |
387 // has a valid callback. | |
388 INTERNET_STATUS_CALLBACK callback = NULL; | |
389 DWORD option_buffer_size = sizeof(callback); | |
390 if (InternetQueryOption(handle, INTERNET_OPTION_CALLBACK, | |
391 &callback, &option_buffer_size) | |
392 && (NULL != callback)) { | |
393 const AsyncResult& r = job->async_result_; | |
394 DLOG(INFO) << "REQUEST_COMPLETE: job=" << job << ", result=" << | |
395 (void*) r.dwResult << ", error=" << r.dwError; | |
396 job->CallOnIOComplete(r); | |
397 } | |
398 break; | |
399 } | |
400 default: | |
401 return DefWindowProc(hwnd, message, wparam, lparam); | |
402 } | |
403 | |
404 return 0; | |
405 } | |
406 | |
407 // static | |
408 void CALLBACK URLRequestInetJob::URLRequestStatusCallback( | 356 void CALLBACK URLRequestInetJob::URLRequestStatusCallback( |
409 HINTERNET handle, DWORD_PTR job_id, DWORD status, LPVOID status_info, | 357 HINTERNET handle, DWORD_PTR job_id, DWORD status, LPVOID status_info, |
410 DWORD status_info_len) { | 358 DWORD status_info_len) { |
411 UINT message = 0; | |
412 LPARAM message_param = 0; | |
413 switch (status) { | 359 switch (status) { |
414 case INTERNET_STATUS_REQUEST_COMPLETE: { | 360 case INTERNET_STATUS_REQUEST_COMPLETE: { |
415 message = MSG_REQUEST_COMPLETE; | |
416 DCHECK(status_info_len == sizeof(INTERNET_ASYNC_RESULT)); | |
417 LPINTERNET_ASYNC_RESULT r = | |
418 static_cast<LPINTERNET_ASYNC_RESULT>(status_info); | |
419 URLRequestInetJob* job = reinterpret_cast<URLRequestInetJob*>(job_id); | 361 URLRequestInetJob* job = reinterpret_cast<URLRequestInetJob*>(job_id); |
420 job->async_result_.dwResult = r->dwResult; | 362 |
421 job->async_result_.dwError = r->dwError; | 363 DCHECK(status_info_len == sizeof(AsyncResult)); |
422 message_param = reinterpret_cast<LPARAM>(handle); | 364 AsyncResult* result = static_cast<AsyncResult*>(status_info); |
| 365 |
| 366 AutoLock locked(job->loop_lock_); |
| 367 if (job->loop_) { |
| 368 job->loop_->PostTask(FROM_HERE, NewRunnableMethod( |
| 369 job, &URLRequestInetJob::CallOnIOComplete, *result)); |
| 370 } |
423 break; | 371 break; |
424 } | 372 } |
425 case INTERNET_STATUS_USER_INPUT_REQUIRED: | 373 case INTERNET_STATUS_USER_INPUT_REQUIRED: |
426 case INTERNET_STATUS_STATE_CHANGE: | 374 case INTERNET_STATUS_STATE_CHANGE: |
427 // TODO(darin): This is probably a security problem. Do something better. | 375 // TODO(darin): This is probably a security problem. Do something better. |
428 ResumeSuspendedDownload(handle, 0); | 376 ResumeSuspendedDownload(handle, 0); |
429 break; | 377 break; |
430 } | 378 } |
431 | |
432 if (message) | |
433 PostMessage(URLRequestInetJob::message_hwnd_, message, | |
434 static_cast<WPARAM>(job_id), message_param); | |
435 } | 379 } |
436 | 380 |
OLD | NEW |