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

Side by Side Diff: net/url_request/url_request_inet_job.cc

Issue 11502: Fix the FTP implementation. Our change to make the MessageLoopForIO not... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 12 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « net/url_request/url_request_inet_job.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
OLDNEW
« no previous file with comments | « net/url_request/url_request_inet_job.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698