| 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 |