OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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 "chrome_frame/urlmon_url_request.h" | 5 #include "chrome_frame/urlmon_url_request.h" |
6 | 6 |
7 #include <wininet.h> | 7 #include <wininet.h> |
8 #include <urlmon.h> | 8 #include <urlmon.h> |
9 | 9 |
10 #include "base/scoped_ptr.h" | 10 #include "base/scoped_ptr.h" |
11 #include "base/string_util.h" | 11 #include "base/string_util.h" |
12 #include "base/logging.h" | 12 #include "base/logging.h" |
13 #include "base/message_loop.h" | 13 #include "base/message_loop.h" |
14 #include "chrome_frame/chrome_frame_activex_base.h" | |
15 #include "chrome_frame/extra_system_apis.h" | 14 #include "chrome_frame/extra_system_apis.h" |
16 #include "chrome_frame/html_utils.h" | 15 #include "chrome_frame/html_utils.h" |
| 16 #include "chrome_frame/urlmon_url_request_private.h" |
17 #include "chrome_frame/urlmon_upload_data_stream.h" | 17 #include "chrome_frame/urlmon_upload_data_stream.h" |
18 #include "chrome_frame/utils.h" | 18 #include "chrome_frame/utils.h" |
19 #include "net/http/http_util.h" | 19 #include "net/http/http_util.h" |
20 #include "net/http/http_response_headers.h" | 20 #include "net/http/http_response_headers.h" |
21 | 21 |
22 static const LARGE_INTEGER kZero = {0}; | 22 static const LARGE_INTEGER kZero = {0}; |
23 static const ULARGE_INTEGER kUnsignedZero = {0}; | 23 static const ULARGE_INTEGER kUnsignedZero = {0}; |
24 int UrlmonUrlRequest::instance_count_ = 0; | |
25 | 24 |
26 // This class wraps the IBindCtx interface which is passed in when our active | 25 // This class wraps the IBindCtx interface which is passed in when our active |
27 // document object is instantiated. The IBindCtx interface is created on | 26 // document object is instantiated. The IBindCtx interface is created on |
28 // the UI thread and hence cannot be used as is on the worker thread which | 27 // the UI thread and hence cannot be used as is on the worker thread which |
29 // handles URL requests. We unmarshal the IBindCtx interface and invoke | 28 // handles URL requests. We unmarshal the IBindCtx interface and invoke |
30 // the corresponding method on the unmarshaled object. The object implementing | 29 // the corresponding method on the unmarshaled object. The object implementing |
31 // the IBindCtx interface also implements IMarshal. However it seems to have a | 30 // the IBindCtx interface also implements IMarshal. However it seems to have a |
32 // bug where in subsequent download requests for the same URL fail. We work | 31 // bug where in subsequent download requests for the same URL fail. We work |
33 // around this issue by using the standard marshaler instead. | 32 // around this issue by using the standard marshaler instead. |
34 class WrappedBindContext : public IBindCtx, | 33 class WrappedBindContext : public IBindCtx, |
(...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
215 DCHECK(marshalled_bind_context_.get() != NULL); | 214 DCHECK(marshalled_bind_context_.get() != NULL); |
216 } | 215 } |
217 return marshalled_bind_context_.QueryInterface(bind_context); | 216 return marshalled_bind_context_.QueryInterface(bind_context); |
218 } | 217 } |
219 | 218 |
220 ScopedComPtr<IStream> marshaled_stream_; | 219 ScopedComPtr<IStream> marshaled_stream_; |
221 ScopedComPtr<IBindCtx> marshalled_bind_context_; | 220 ScopedComPtr<IBindCtx> marshalled_bind_context_; |
222 ScopedComPtr<IMarshal> standard_marshal_; | 221 ScopedComPtr<IMarshal> standard_marshal_; |
223 }; | 222 }; |
224 | 223 |
| 224 STDMETHODIMP UrlmonUrlRequest::SendStream::Write(const void * buffer, |
| 225 ULONG size, |
| 226 ULONG* size_written) { |
| 227 DCHECK(request_); |
| 228 int size_to_write = static_cast<int>( |
| 229 std::min(static_cast<ULONG>(MAXINT), size)); |
| 230 request_->delegate_->OnReadComplete(request_->id(), buffer, |
| 231 size_to_write); |
| 232 if (size_written) |
| 233 *size_written = size_to_write; |
| 234 return S_OK; |
| 235 } |
| 236 |
| 237 int UrlmonUrlRequest::instance_count_ = 0; |
| 238 |
225 UrlmonUrlRequest::UrlmonUrlRequest() | 239 UrlmonUrlRequest::UrlmonUrlRequest() |
226 : pending_read_size_(0), | 240 : pending_read_size_(0), |
227 status_(URLRequestStatus::FAILED, net::ERR_FAILED), | 241 thread_(NULL), |
228 thread_(PlatformThread::CurrentId()), | 242 parent_window_(NULL) { |
229 redirect_status_(0), | |
230 parent_window_(NULL), | |
231 worker_thread_(NULL), | |
232 ignore_redirect_stop_binding_error_(false) { | |
233 DLOG(INFO) << StringPrintf("Created request. Obj: %X", this) | 243 DLOG(INFO) << StringPrintf("Created request. Obj: %X", this) |
234 << " Count: " << ++instance_count_; | 244 << " Count: " << ++instance_count_; |
235 } | 245 } |
236 | 246 |
237 UrlmonUrlRequest::~UrlmonUrlRequest() { | 247 UrlmonUrlRequest::~UrlmonUrlRequest() { |
238 DLOG(INFO) << StringPrintf("Deleted request. Obj: %X", this) | 248 DLOG(INFO) << StringPrintf("Deleted request. Obj: %X", this) |
239 << " Count: " << --instance_count_; | 249 << " Count: " << --instance_count_; |
240 } | 250 } |
241 | 251 |
242 bool UrlmonUrlRequest::Start() { | 252 bool UrlmonUrlRequest::Start() { |
243 DCHECK_EQ(PlatformThread::CurrentId(), thread_); | 253 thread_ = PlatformThread::CurrentId(); |
244 | 254 status_.Start(); |
245 if (!worker_thread_ || !worker_thread_->message_loop()) { | 255 HRESULT hr = StartAsyncDownload(); |
246 NOTREACHED() << __FUNCTION__ << " Urlmon request thread not initialized"; | 256 if (FAILED(hr)) { |
247 return false; | 257 status_.set_result(URLRequestStatus::FAILED, HresultToNetError(hr)); |
| 258 NotifyDelegateAndDie(); |
248 } | 259 } |
249 | |
250 Create(HWND_MESSAGE); | |
251 if (!IsWindow()) { | |
252 NOTREACHED() << "Failed to create urlmon message window: " | |
253 << GetLastError(); | |
254 return false; | |
255 } | |
256 | |
257 // Take a self reference to maintain COM lifetime. This will be released | |
258 // in OnFinalMessage | |
259 AddRef(); | |
260 request_handler()->AddRequest(this); | |
261 | |
262 worker_thread_->message_loop()->PostTask( | |
263 FROM_HERE, NewRunnableMethod(this, &UrlmonUrlRequest::StartAsync)); | |
264 | |
265 return true; | 260 return true; |
266 } | 261 } |
267 | 262 |
268 void UrlmonUrlRequest::Stop() { | 263 void UrlmonUrlRequest::Stop() { |
269 DCHECK_EQ(PlatformThread::CurrentId(), thread_); | 264 DCHECK_EQ(thread_, PlatformThread::CurrentId()); |
| 265 DCHECK((status_.get_state() != Status::DONE) == (binding_ != NULL)); |
| 266 Status::State state = status_.get_state(); |
| 267 switch (state) { |
| 268 case Status::WORKING: |
| 269 status_.Cancel(); |
| 270 binding_->Abort(); |
| 271 break; |
270 | 272 |
271 if (!worker_thread_ || !worker_thread_->message_loop()) { | 273 case Status::ABORTING: |
272 NOTREACHED() << __FUNCTION__ << " Urlmon request thread not initialized"; | 274 status_.Cancel(); |
273 return; | 275 break; |
274 } | |
275 | 276 |
276 // We can remove the request from the map safely here if it is still valid. | 277 case Status::DONE: |
277 // There is an additional reference on the UrlmonUrlRequest instance which | 278 status_.Cancel(); |
278 // is released when the task scheduled by the EndRequest function executes. | 279 NotifyDelegateAndDie(); |
279 request_handler()->RemoveRequest(this); | 280 break; |
280 | |
281 worker_thread_->message_loop()->PostTask( | |
282 FROM_HERE, NewRunnableMethod(this, &UrlmonUrlRequest::StopAsync)); | |
283 } | |
284 | |
285 void UrlmonUrlRequest::StartAsync() { | |
286 DCHECK(worker_thread_ != NULL); | |
287 | |
288 status_.set_status(URLRequestStatus::IO_PENDING); | |
289 HRESULT hr = StartAsyncDownload(); | |
290 if (FAILED(hr)) { | |
291 // Do not call EndRequest() here since it will attempt to free references | |
292 // that have not been established. | |
293 status_.set_os_error(HresultToNetError(hr)); | |
294 status_.set_status(URLRequestStatus::FAILED); | |
295 DLOG(ERROR) << "StartAsyncDownload failed"; | |
296 EndRequest(); | |
297 return; | |
298 } | 281 } |
299 } | 282 } |
300 | 283 |
301 void UrlmonUrlRequest::StopAsync() { | 284 bool UrlmonUrlRequest::Read(int bytes_to_read) { |
302 DCHECK(worker_thread_ != NULL); | 285 DCHECK_EQ(thread_, PlatformThread::CurrentId()); |
| 286 // Re-entrancy check. Thou shall not call Read() while processOnReadComplete!! |
| 287 DCHECK_EQ(0, pending_read_size_); |
| 288 if (pending_read_size_ != 0) |
| 289 return false; |
303 | 290 |
304 if (binding_) { | 291 DCHECK((status_.get_state() != Status::DONE) == (binding_ != NULL)); |
305 binding_->Abort(); | 292 if (status_.get_state() == Status::ABORTING) { |
306 } else { | 293 return true; |
307 status_.set_status(URLRequestStatus::CANCELED); | |
308 status_.set_os_error(net::ERR_FAILED); | |
309 EndRequest(); | |
310 } | |
311 } | |
312 | |
313 void UrlmonUrlRequest::OnFinalMessage(HWND window) { | |
314 m_hWnd = NULL; | |
315 // Release the outstanding reference in the context of the UI thread to | |
316 // ensure that our instance gets deleted in the same thread which created it. | |
317 Release(); | |
318 } | |
319 | |
320 bool UrlmonUrlRequest::Read(int bytes_to_read) { | |
321 DCHECK_EQ(PlatformThread::CurrentId(), thread_); | |
322 | |
323 DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this); | |
324 | |
325 if (!worker_thread_ || !worker_thread_->message_loop()) { | |
326 NOTREACHED() << __FUNCTION__ << " Urlmon request thread not initialized"; | |
327 return false; | |
328 } | 294 } |
329 | 295 |
330 worker_thread_->message_loop()->PostTask( | |
331 FROM_HERE, NewRunnableMethod(this, &UrlmonUrlRequest::ReadAsync, | |
332 bytes_to_read)); | |
333 return true; | |
334 } | |
335 | |
336 void UrlmonUrlRequest::TransferToHost(IUnknown* host) { | |
337 DCHECK_EQ(PlatformThread::CurrentId(), thread_); | |
338 DCHECK(host); | |
339 DCHECK(moniker_); | |
340 if (moniker_) { | |
341 ScopedComPtr<IBindCtx> bind_context; | |
342 CreateBindCtx(0, bind_context.Receive()); | |
343 DCHECK(bind_context); | |
344 NavigateBrowserToMoniker(host, moniker_, NULL, bind_context, NULL); | |
345 moniker_.Release(); | |
346 } | |
347 } | |
348 | |
349 void UrlmonUrlRequest::ReadAsync(int bytes_to_read) { | |
350 // Send cached data if available. | 296 // Send cached data if available. |
351 CComObjectStackEx<SendStream> send_stream; | 297 CComObjectStackEx<SendStream> send_stream; |
352 send_stream.Initialize(this); | 298 send_stream.Initialize(this); |
353 | 299 |
354 size_t bytes_copied = 0; | 300 size_t bytes_copied = 0; |
355 if (cached_data_.is_valid() && cached_data_.Read(&send_stream, bytes_to_read, | 301 if (delegate_ && cached_data_.is_valid() && |
356 &bytes_copied)) { | 302 cached_data_.Read(&send_stream, bytes_to_read, &bytes_copied)) { |
357 DLOG(INFO) << StringPrintf("URL: %s Obj: %X - bytes read from cache: %d", | 303 DLOG(INFO) << StringPrintf("URL: %s Obj: %X - bytes read from cache: %d", |
358 url().c_str(), this, bytes_copied); | 304 url().c_str(), this, bytes_copied); |
359 return; | 305 return true; |
360 } | 306 } |
361 | 307 |
362 // if the request is finished or there's nothing more to read | 308 if (status_.get_state() == Status::WORKING) { |
363 // then end the request | 309 DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this) << |
364 if (!status_.is_io_pending() || !binding_) { | 310 "- Read pending for: " << bytes_to_read; |
365 DLOG(INFO) << StringPrintf("URL: %s Obj: %X. Response finished. Status: %d", | 311 pending_read_size_ = bytes_to_read; |
366 url().c_str(), this, status_.status()); | 312 } else { |
367 EndRequest(); | 313 DLOG(INFO) << StringPrintf("URL: %s Obj: %X. Response finished.", |
368 return; | 314 url().c_str(), this); |
| 315 NotifyDelegateAndDie(); |
369 } | 316 } |
370 | 317 |
371 pending_read_size_ = bytes_to_read; | 318 return true; |
372 DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this) << | |
373 "- Read pending for: " << bytes_to_read; | |
374 } | 319 } |
375 | 320 |
376 STDMETHODIMP UrlmonUrlRequest::OnStartBinding( | 321 HRESULT UrlmonUrlRequest::ConnectToExistingMoniker(IMoniker* moniker, |
377 DWORD reserved, IBinding *binding) { | 322 IBindCtx* context, |
| 323 const std::wstring& url) { |
| 324 if (!moniker || url.empty()) { |
| 325 NOTREACHED() << "Invalid arguments"; |
| 326 return E_INVALIDARG; |
| 327 } |
| 328 |
| 329 DCHECK(moniker_.get() == NULL); |
| 330 DCHECK(bind_context_.get() == NULL); |
| 331 |
| 332 bind_context_ = context; |
| 333 moniker_ = moniker; |
| 334 set_url(WideToUTF8(url)); |
| 335 return S_OK; |
| 336 } |
| 337 |
| 338 void UrlmonUrlRequest::StealMoniker(IMoniker** moniker) { |
| 339 // Could be called in any thread. There should be no race |
| 340 // since moniker_ is not released while we are in manager's request map. |
| 341 *moniker = moniker_.Detach(); |
| 342 } |
| 343 |
| 344 STDMETHODIMP UrlmonUrlRequest::OnStartBinding(DWORD reserved, |
| 345 IBinding *binding) { |
| 346 DCHECK_EQ(thread_, PlatformThread::CurrentId()); |
378 binding_ = binding; | 347 binding_ = binding; |
379 return S_OK; | 348 return S_OK; |
380 } | 349 } |
381 | 350 |
382 STDMETHODIMP UrlmonUrlRequest::GetPriority(LONG *priority) { | 351 STDMETHODIMP UrlmonUrlRequest::GetPriority(LONG *priority) { |
383 if (!priority) | 352 if (!priority) |
384 return E_POINTER; | 353 return E_POINTER; |
385 *priority = THREAD_PRIORITY_NORMAL; | 354 *priority = THREAD_PRIORITY_NORMAL; |
386 return S_OK; | 355 return S_OK; |
387 } | 356 } |
388 | 357 |
389 STDMETHODIMP UrlmonUrlRequest::OnLowResource(DWORD reserved) { | 358 STDMETHODIMP UrlmonUrlRequest::OnLowResource(DWORD reserved) { |
390 return S_OK; | 359 return S_OK; |
391 } | 360 } |
392 | 361 |
393 STDMETHODIMP UrlmonUrlRequest::OnProgress(ULONG progress, ULONG max_progress, | 362 STDMETHODIMP UrlmonUrlRequest::OnProgress(ULONG progress, ULONG max_progress, |
394 ULONG status_code, LPCWSTR status_text) { | 363 ULONG status_code, LPCWSTR status_text) { |
395 static const int kDefaultHttpRedirectCode = 302; | 364 DCHECK_EQ(thread_, PlatformThread::CurrentId()); |
396 | |
397 switch (status_code) { | 365 switch (status_code) { |
398 case BINDSTATUS_REDIRECTING: { | 366 case BINDSTATUS_REDIRECTING: { |
| 367 DLOG(INFO) << "URL: " << url() << " redirected to " << status_text; |
399 // Fetch the redirect status as they aren't all equal (307 in particular | 368 // Fetch the redirect status as they aren't all equal (307 in particular |
400 // retains the HTTP request verb). | 369 // retains the HTTP request verb). |
401 // We assume that valid redirect codes are 301, 302, 303 and 307. If we | 370 int http_code = GetHttpResponseStatus(); |
402 // receive anything else we would abort the request which would | 371 status_.SetRedirected(http_code, WideToUTF8(status_text)); |
403 // eventually result in the request getting cancelled in Chrome. | 372 // Abort. We will inform Chrome in OnStopBinding callback. |
404 int redirect_status = GetHttpResponseStatus(); | 373 binding_->Abort(); |
405 DCHECK(status_text != NULL); | |
406 DLOG(INFO) << "URL: " << url() << " redirected to " | |
407 << status_text; | |
408 redirect_url_ = status_text; | |
409 // At times we receive invalid redirect codes like 0, 200, etc. We | |
410 // default to 302 in this case. | |
411 if (!net::HttpResponseHeaders::IsRedirectResponseCode(redirect_status)) | |
412 redirect_status = kDefaultHttpRedirectCode; | |
413 redirect_status_ = redirect_status; | |
414 // Chrome should decide whether a redirect has to be followed. To achieve | |
415 // this we send over a fake response to Chrome and abort the redirect. | |
416 std::string headers = GetHttpHeaders(); | |
417 OnResponse(0, UTF8ToWide(headers).c_str(), NULL, NULL); | |
418 ignore_redirect_stop_binding_error_ = true; | |
419 DCHECK(binding_ != NULL); | |
420 if (binding_) { | |
421 binding_->Abort(); | |
422 binding_ = NULL; | |
423 } | |
424 return E_ABORT; | 374 return E_ABORT; |
425 } | 375 } |
426 | 376 |
427 default: | 377 default: |
428 DLOG(INFO) << " Obj: " << std::hex << this << " OnProgress(" << url() | 378 DLOG(INFO) << " Obj: " << std::hex << this << " OnProgress(" << url() |
429 << StringPrintf(L") code: %i status: %ls", status_code, status_text); | 379 << StringPrintf(L") code: %i status: %ls", status_code, status_text); |
430 break; | 380 break; |
431 } | 381 } |
432 | 382 |
433 return S_OK; | 383 return S_OK; |
434 } | 384 } |
435 | 385 |
436 STDMETHODIMP UrlmonUrlRequest::OnStopBinding(HRESULT result, LPCWSTR error) { | 386 STDMETHODIMP UrlmonUrlRequest::OnStopBinding(HRESULT result, LPCWSTR error) { |
437 DCHECK(worker_thread_ != NULL); | 387 DCHECK_EQ(thread_, PlatformThread::CurrentId()); |
438 DCHECK_EQ(PlatformThread::CurrentId(), worker_thread_->thread_id()); | 388 DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this) << |
| 389 " - Request stopped, Result: " << std::hex << result; |
| 390 DCHECK(status_.get_state() == Status::WORKING || |
| 391 status_.get_state() == Status::ABORTING); |
| 392 Status::State state = status_.get_state(); |
439 | 393 |
440 DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this) << | 394 // Mark we a are done. |
441 " - Request stopped, Result: " << std::hex << result << | 395 status_.Done(); |
442 " Status: " << status_.status(); | |
443 | 396 |
444 if (FAILED(result)) { | 397 if (state == Status::WORKING) { |
445 status_.set_status(URLRequestStatus::FAILED); | 398 status_.set_result(result); |
446 status_.set_os_error(HresultToNetError(result)); | 399 |
447 EndRequest(); | 400 // The code below seems easy but it is not. :) |
448 } else { | 401 // we cannot have pending read and data_avail at the same time. |
449 status_.set_status(URLRequestStatus::SUCCESS); | 402 DCHECK(!(pending_read_size_ > 0 && cached_data_.is_valid())); |
450 status_.set_os_error(0); | 403 |
451 ReleaseBindings(); | 404 // We have some data, but Chrome has not yet read it. Wait until Chrome |
452 // In most cases we receive the end request notification from Chrome. | 405 // read the remaining of the data and then send the error/success code. |
453 // However at times requests can complete without us receiving any | 406 if (cached_data_.is_valid()) { |
454 // data. In this case we need to inform Chrome that this request has been | 407 ReleaseBindings(); |
455 // completed to prevent Chrome from waiting forever for data for this | 408 return S_OK; |
456 // request. | |
457 if (pending_read_size_) { | |
458 pending_read_size_ = 0; | |
459 OnResponseEnd(status_); | |
460 } | 409 } |
| 410 |
| 411 NotifyDelegateAndDie(); |
| 412 return S_OK; |
461 } | 413 } |
462 | 414 |
| 415 // Status::ABORTING |
| 416 if (status_.was_redirected()) { |
| 417 // Just release bindings here. Chrome will issue EndRequest(request_id) |
| 418 // after processing headers we had provided. |
| 419 std::string headers = GetHttpHeaders(); |
| 420 OnResponse(0, UTF8ToWide(headers).c_str(), NULL, NULL); |
| 421 ReleaseBindings(); |
| 422 return S_OK; |
| 423 } |
| 424 |
| 425 // Stop invoked. |
| 426 NotifyDelegateAndDie(); |
463 return S_OK; | 427 return S_OK; |
464 } | 428 } |
465 | 429 |
466 STDMETHODIMP UrlmonUrlRequest::GetBindInfo(DWORD* bind_flags, | 430 STDMETHODIMP UrlmonUrlRequest::GetBindInfo(DWORD* bind_flags, |
467 BINDINFO *bind_info) { | 431 BINDINFO *bind_info) { |
468 DCHECK(worker_thread_ != NULL); | |
469 DCHECK_EQ(PlatformThread::CurrentId(), worker_thread_->thread_id()); | |
470 | 432 |
471 if ((bind_info == NULL) || (bind_info->cbSize == 0) || (bind_flags == NULL)) | 433 if ((bind_info == NULL) || (bind_info->cbSize == 0) || (bind_flags == NULL)) |
472 return E_INVALIDARG; | 434 return E_INVALIDARG; |
473 | 435 |
474 *bind_flags = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA; | 436 *bind_flags = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA; |
475 | 437 |
476 bool upload_data = false; | 438 bool upload_data = false; |
477 | 439 |
478 if (LowerCaseEqualsASCII(method(), "get")) { | 440 if (LowerCaseEqualsASCII(method(), "get")) { |
479 bind_info->dwBindVerb = BINDVERB_GET; | 441 bind_info->dwBindVerb = BINDVERB_GET; |
480 } else if (LowerCaseEqualsASCII(method(), "post")) { | 442 } else if (LowerCaseEqualsASCII(method(), "post")) { |
481 bind_info->dwBindVerb = BINDVERB_POST; | 443 bind_info->dwBindVerb = BINDVERB_POST; |
482 upload_data = true; | 444 upload_data = true; |
483 } else if (LowerCaseEqualsASCII(method(), "put")) { | 445 } else if (LowerCaseEqualsASCII(method(), "put")) { |
484 bind_info->dwBindVerb = BINDVERB_PUT; | 446 bind_info->dwBindVerb = BINDVERB_PUT; |
485 upload_data = true; | 447 upload_data = true; |
486 } else { | 448 } else { |
487 NOTREACHED() << "Unknown HTTP method."; | 449 NOTREACHED() << "Unknown HTTP method."; |
488 status_.set_status(URLRequestStatus::FAILED); | 450 status_.set_result(URLRequestStatus::FAILED, net::ERR_METHOD_NOT_SUPPORTED); |
489 status_.set_os_error(net::ERR_METHOD_NOT_SUPPORTED); | 451 NotifyDelegateAndDie(); |
490 EndRequest(); | |
491 return E_FAIL; | 452 return E_FAIL; |
492 } | 453 } |
493 | 454 |
494 if (upload_data) { | 455 if (upload_data) { |
495 // Bypass caching proxies on POSTs and PUTs and avoid writing responses to | 456 // Bypass caching proxies on POSTs and PUTs and avoid writing responses to |
496 // these requests to the browser's cache | 457 // these requests to the browser's cache |
497 *bind_flags |= BINDF_GETNEWESTVERSION | BINDF_NOWRITECACHE | | 458 *bind_flags |= BINDF_GETNEWESTVERSION | BINDF_NOWRITECACHE | |
498 BINDF_PRAGMA_NO_CACHE; | 459 BINDF_PRAGMA_NO_CACHE; |
499 | 460 |
500 // Initialize the STGMEDIUM. | 461 // Initialize the STGMEDIUM. |
(...skipping 11 matching lines...) Expand all Loading... |
512 << "POST request with no data!"; | 473 << "POST request with no data!"; |
513 } | 474 } |
514 } | 475 } |
515 | 476 |
516 return S_OK; | 477 return S_OK; |
517 } | 478 } |
518 | 479 |
519 STDMETHODIMP UrlmonUrlRequest::OnDataAvailable(DWORD flags, DWORD size, | 480 STDMETHODIMP UrlmonUrlRequest::OnDataAvailable(DWORD flags, DWORD size, |
520 FORMATETC* formatetc, | 481 FORMATETC* formatetc, |
521 STGMEDIUM* storage) { | 482 STGMEDIUM* storage) { |
522 DCHECK(worker_thread_ != NULL); | |
523 DCHECK_EQ(PlatformThread::CurrentId(), worker_thread_->thread_id()); | |
524 | |
525 DLOG(INFO) << StringPrintf("URL: %s Obj: %X - Bytes available: %d", | 483 DLOG(INFO) << StringPrintf("URL: %s Obj: %X - Bytes available: %d", |
526 url().c_str(), this, size); | 484 url().c_str(), this, size); |
527 | 485 |
528 if (!storage || (storage->tymed != TYMED_ISTREAM)) { | 486 if (!storage || (storage->tymed != TYMED_ISTREAM)) { |
529 NOTREACHED(); | 487 NOTREACHED(); |
530 return E_INVALIDARG; | 488 return E_INVALIDARG; |
531 } | 489 } |
532 | 490 |
533 IStream* read_stream = storage->pstm; | 491 IStream* read_stream = storage->pstm; |
534 if (!read_stream) { | 492 if (!read_stream) { |
(...skipping 21 matching lines...) Expand all Loading... |
556 cached_data_.Read(&send_stream, pending_read_size_, &pending_read_size_); | 514 cached_data_.Read(&send_stream, pending_read_size_, &pending_read_size_); |
557 DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this) << | 515 DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this) << |
558 " - size read: " << pending_read_size_; | 516 " - size read: " << pending_read_size_; |
559 pending_read_size_ = 0; | 517 pending_read_size_ = 0; |
560 } else { | 518 } else { |
561 DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this) << | 519 DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this) << |
562 " - waiting for remote read"; | 520 " - waiting for remote read"; |
563 } | 521 } |
564 | 522 |
565 if (BSCF_LASTDATANOTIFICATION & flags) { | 523 if (BSCF_LASTDATANOTIFICATION & flags) { |
566 status_.set_status(URLRequestStatus::SUCCESS); | |
567 DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this) << | 524 DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this) << |
568 " - end of data."; | 525 " - end of data."; |
569 } | 526 } |
570 | 527 |
571 return S_OK; | 528 return S_OK; |
572 } | 529 } |
573 | 530 |
574 STDMETHODIMP UrlmonUrlRequest::OnObjectAvailable(REFIID iid, IUnknown* object) { | 531 STDMETHODIMP UrlmonUrlRequest::OnObjectAvailable(REFIID iid, IUnknown* object) { |
575 // We are calling BindToStorage on the moniker we should always get called | 532 // We are calling BindToStorage on the moniker we should always get called |
576 // back on OnDataAvailable and should never get OnObjectAvailable | 533 // back on OnDataAvailable and should never get OnObjectAvailable |
577 NOTREACHED(); | 534 NOTREACHED(); |
578 return E_NOTIMPL; | 535 return E_NOTIMPL; |
579 } | 536 } |
580 | 537 |
581 STDMETHODIMP UrlmonUrlRequest::BeginningTransaction(const wchar_t* url, | 538 STDMETHODIMP UrlmonUrlRequest::BeginningTransaction(const wchar_t* url, |
582 const wchar_t* current_headers, DWORD reserved, | 539 const wchar_t* current_headers, DWORD reserved, |
583 wchar_t** additional_headers) { | 540 wchar_t** additional_headers) { |
584 DCHECK(worker_thread_ != NULL); | 541 DCHECK_EQ(thread_, PlatformThread::CurrentId()); |
585 DCHECK_EQ(PlatformThread::CurrentId(), worker_thread_->thread_id()); | |
586 | |
587 if (!additional_headers) { | 542 if (!additional_headers) { |
588 NOTREACHED(); | 543 NOTREACHED(); |
589 return E_POINTER; | 544 return E_POINTER; |
590 } | 545 } |
591 | 546 |
592 DLOG(INFO) << "URL: " << url << " Obj: " << std::hex << this << | 547 DLOG(INFO) << "URL: " << url << " Obj: " << std::hex << this << |
593 " - Request headers: \n" << current_headers; | 548 " - Request headers: \n" << current_headers; |
594 | 549 |
595 if (!binding_) { | 550 if (status_.get_state() == Status::ABORTING) { |
596 // At times the BINDSTATUS_REDIRECTING notification which is sent to the | 551 // At times the BINDSTATUS_REDIRECTING notification which is sent to the |
597 // IBindStatusCallback interface does not have an accompanying HTTP | 552 // IBindStatusCallback interface does not have an accompanying HTTP |
598 // redirect status code, i.e. the attempt to query the HTTP status code | 553 // redirect status code, i.e. the attempt to query the HTTP status code |
599 // from the binding returns 0, 200, etc which are invalid redirect codes. | 554 // from the binding returns 0, 200, etc which are invalid redirect codes. |
600 // We don't want urlmon to follow redirects. We return E_ABORT in our | 555 // We don't want urlmon to follow redirects. We return E_ABORT in our |
601 // IBindStatusCallback::OnProgress function and also abort the binding. | 556 // IBindStatusCallback::OnProgress function and also abort the binding. |
602 // However urlmon still tries to establish a transaction with the | 557 // However urlmon still tries to establish a transaction with the |
603 // redirected URL which confuses the web server. | 558 // redirected URL which confuses the web server. |
604 // Fix is to abort the attempted transaction. | 559 // Fix is to abort the attempted transaction. |
605 DCHECK(ignore_redirect_stop_binding_error_); | |
606 DLOG(WARNING) << __FUNCTION__ | 560 DLOG(WARNING) << __FUNCTION__ |
607 << ": Aborting connection to URL:" | 561 << ": Aborting connection to URL:" |
608 << url | 562 << url |
609 << " as the binding has been aborted"; | 563 << " as the binding has been aborted"; |
610 return E_ABORT; | 564 return E_ABORT; |
611 } | 565 } |
612 | 566 |
613 HRESULT hr = S_OK; | 567 HRESULT hr = S_OK; |
614 | 568 |
615 std::string new_headers; | 569 std::string new_headers; |
(...skipping 26 matching lines...) Expand all Loading... |
642 new_headers.size()); | 596 new_headers.size()); |
643 } | 597 } |
644 } | 598 } |
645 | 599 |
646 return hr; | 600 return hr; |
647 } | 601 } |
648 | 602 |
649 STDMETHODIMP UrlmonUrlRequest::OnResponse(DWORD dwResponseCode, | 603 STDMETHODIMP UrlmonUrlRequest::OnResponse(DWORD dwResponseCode, |
650 const wchar_t* response_headers, const wchar_t* request_headers, | 604 const wchar_t* response_headers, const wchar_t* request_headers, |
651 wchar_t** additional_headers) { | 605 wchar_t** additional_headers) { |
652 DCHECK(worker_thread_ != NULL); | |
653 DLOG(INFO) << __FUNCTION__ << " " << url() << std::endl << " headers: " << | 606 DLOG(INFO) << __FUNCTION__ << " " << url() << std::endl << " headers: " << |
654 std::endl << response_headers; | 607 std::endl << response_headers; |
655 DCHECK_EQ(PlatformThread::CurrentId(), worker_thread_->thread_id()); | 608 DCHECK_EQ(thread_, PlatformThread::CurrentId()); |
656 | |
657 if (!binding_) { | 609 if (!binding_) { |
658 DCHECK(redirect_url_.empty() == false); | |
659 DLOG(WARNING) << __FUNCTION__ | 610 DLOG(WARNING) << __FUNCTION__ |
660 << ": Ignoring as the binding was aborted due to a redirect"; | 611 << ": Ignoring as the binding was aborted due to a redirect"; |
661 return S_OK; | 612 return S_OK; |
662 } | 613 } |
663 | 614 |
664 std::string raw_headers = WideToUTF8(response_headers); | 615 std::string raw_headers = WideToUTF8(response_headers); |
665 | 616 |
666 // Security check for frame busting headers. We don't honor the headers | 617 // Security check for frame busting headers. We don't honor the headers |
667 // as-such, but instead simply kill requests which we've been asked to | 618 // as-such, but instead simply kill requests which we've been asked to |
668 // look for if they specify a value for "X-Frame-Options" other than | 619 // look for if they specify a value for "X-Frame-Options" other than |
669 // "ALLOWALL" (the others are "deny" and "sameorigin"). This puts the onus | 620 // "ALLOWALL" (the others are "deny" and "sameorigin"). This puts the onus |
670 // on the user of the UrlRequest to specify whether or not requests should | 621 // on the user of the UrlRequest to specify whether or not requests should |
671 // be inspected. For ActiveDocuments, the answer is "no", since WebKit's | 622 // be inspected. For ActiveDocuments, the answer is "no", since WebKit's |
672 // detection/handling is sufficient and since ActiveDocuments cannot be | 623 // detection/handling is sufficient and since ActiveDocuments cannot be |
673 // hosted as iframes. For NPAPI and ActiveX documents, the Initialize() | 624 // hosted as iframes. For NPAPI and ActiveX documents, the Initialize() |
674 // function of the PluginUrlRequest object allows them to specify how they'd | 625 // function of the PluginUrlRequest object allows them to specify how they'd |
675 // like requests handled. Both should set enable_frame_busting_ to true to | 626 // like requests handled. Both should set enable_frame_busting_ to true to |
676 // avoid CSRF attacks. Should WebKit's handling of this ever change, we will | 627 // avoid CSRF attacks. Should WebKit's handling of this ever change, we will |
677 // need to re-visit how and when frames are killed to better mirror a policy | 628 // need to re-visit how and when frames are killed to better mirror a policy |
678 // which may do something other than kill the sub-document outright. | 629 // which may do something other than kill the sub-document outright. |
679 | 630 |
680 // NOTE(slightlyoff): We don't use net::HttpResponseHeaders here because | 631 // NOTE(slightlyoff): We don't use net::HttpResponseHeaders here because |
681 // of lingering ICU/base_noicu issues. | 632 // of lingering ICU/base_noicu issues. |
682 if (frame_busting_enabled_) { | 633 if (enable_frame_busting_) { |
683 std::string http_headers = net::HttpUtil::AssembleRawHeaders( | 634 std::string http_headers = net::HttpUtil::AssembleRawHeaders( |
684 raw_headers.c_str(), raw_headers.length()); | 635 raw_headers.c_str(), raw_headers.length()); |
685 if (http_utils::HasFrameBustingHeader(http_headers)) { | 636 if (http_utils::HasFrameBustingHeader(http_headers)) { |
686 DLOG(ERROR) << "X-Frame-Options header other than ALLOWALL " << | 637 DLOG(ERROR) << "X-Frame-Options header other than ALLOWALL " << |
687 "detected, navigation canceled"; | 638 "detected, navigation canceled"; |
688 return E_FAIL; | 639 return E_FAIL; |
689 } | 640 } |
690 } | 641 } |
691 | 642 |
692 std::wstring url_for_persistent_cookies = | |
693 redirect_url_.empty() ? UTF8ToWide(url()) : redirect_url_; | |
694 | 643 |
| 644 std::string url_for_persistent_cookies; |
695 std::string persistent_cookies; | 645 std::string persistent_cookies; |
696 | 646 |
697 DWORD cookie_size = 0; // NOLINT | 647 if (status_.was_redirected()) |
698 // Note that there's really no way for us here to distinguish session cookies | 648 url_for_persistent_cookies = status_.get_redirection().utf8_url; |
699 // from persistent cookies here. Session cookies should get filtered | 649 |
700 // out on the chrome side as to not be added again. | 650 if (url_for_persistent_cookies.empty()) |
701 InternetGetCookie(url_for_persistent_cookies.c_str(), NULL, NULL, | 651 url_for_persistent_cookies = url(); |
702 &cookie_size); | 652 |
703 if (cookie_size) { | 653 // Grab cookies for the specific Url from WININET. |
704 scoped_array<wchar_t> cookies(new wchar_t[cookie_size + 1]); | 654 { |
705 if (!InternetGetCookie(url_for_persistent_cookies.c_str(), NULL, | 655 DWORD cookie_size = 0; // NOLINT |
706 cookies.get(), &cookie_size)) { | 656 std::wstring url = UTF8ToWide(url_for_persistent_cookies); |
707 NOTREACHED() << "InternetGetCookie failed. Error: " << GetLastError(); | 657 |
708 } else { | 658 // Note that there's really no way for us here to distinguish session |
709 persistent_cookies = WideToUTF8(cookies.get()); | 659 // cookies from persistent cookies here. Session cookies should get |
| 660 // filtered out on the chrome side as to not be added again. |
| 661 InternetGetCookie(url.c_str(), NULL, NULL, &cookie_size); |
| 662 if (cookie_size) { |
| 663 scoped_array<wchar_t> cookies(new wchar_t[cookie_size + 1]); |
| 664 if (!InternetGetCookie(url.c_str(), NULL, cookies.get(), &cookie_size)) { |
| 665 NOTREACHED() << "InternetGetCookie failed. Error: " << GetLastError(); |
| 666 } else { |
| 667 persistent_cookies = WideToUTF8(cookies.get()); |
| 668 } |
710 } | 669 } |
711 } | 670 } |
712 | 671 |
713 OnResponseStarted("", | 672 // Inform the delegate. |
714 raw_headers.c_str(), | 673 delegate_->OnResponseStarted(id(), |
715 0, | 674 "", // mime_type |
716 base::Time(), | 675 raw_headers.c_str(), // headers |
| 676 0, // size |
| 677 base::Time(), // last_modified |
717 persistent_cookies, | 678 persistent_cookies, |
718 redirect_url_.empty() ? std::string() : | 679 status_.get_redirection().utf8_url, |
719 WideToUTF8(redirect_url_), | 680 status_.get_redirection().http_code); |
720 redirect_status_); | |
721 | |
722 return S_OK; | 681 return S_OK; |
723 } | 682 } |
724 | 683 |
725 STDMETHODIMP UrlmonUrlRequest::GetWindow(const GUID& guid_reason, | 684 STDMETHODIMP UrlmonUrlRequest::GetWindow(const GUID& guid_reason, |
726 HWND* parent_window) { | 685 HWND* parent_window) { |
727 if (!parent_window) { | 686 if (!parent_window) { |
728 return E_INVALIDARG; | 687 return E_INVALIDARG; |
729 } | 688 } |
730 | 689 |
731 #ifndef NDEBUG | 690 #ifndef NDEBUG |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
811 } | 770 } |
812 | 771 |
813 default: { | 772 default: { |
814 NOTREACHED() << "Unhandled security problem : " << problem; | 773 NOTREACHED() << "Unhandled security problem : " << problem; |
815 break; | 774 break; |
816 } | 775 } |
817 } | 776 } |
818 return hr; | 777 return hr; |
819 } | 778 } |
820 | 779 |
821 HRESULT UrlmonUrlRequest::ConnectToExistingMoniker(IMoniker* moniker, | |
822 IBindCtx* context, | |
823 const std::wstring& url) { | |
824 if (!moniker || url.empty()) { | |
825 NOTREACHED() << "Invalid arguments"; | |
826 return E_INVALIDARG; | |
827 } | |
828 | |
829 DCHECK(moniker_.get() == NULL); | |
830 DCHECK(bind_context_.get() == NULL); | |
831 | |
832 CComObject<WrappedBindContext>* bind_context = NULL; | |
833 HRESULT hr = CComObject<WrappedBindContext>::CreateInstance(&bind_context); | |
834 if (FAILED(hr)) { | |
835 NOTREACHED() << "Failed to instantiate wrapped bind context. Error:" << hr; | |
836 return hr; | |
837 } | |
838 | |
839 bind_context->AddRef(); | |
840 hr = bind_context->Initialize(context); | |
841 DCHECK(SUCCEEDED(hr)); | |
842 | |
843 hr = bind_context->QueryInterface(bind_context_.Receive()); | |
844 bind_context->Release(); | |
845 | |
846 if (FAILED(hr)) { | |
847 NOTREACHED() << "Failed to QI for IBindCtx on wrapper. Error:" << hr; | |
848 return hr; | |
849 } | |
850 | |
851 moniker_ = moniker; | |
852 set_url(WideToUTF8(url)); | |
853 return S_OK; | |
854 } | |
855 | |
856 HRESULT UrlmonUrlRequest::StartAsyncDownload() { | 780 HRESULT UrlmonUrlRequest::StartAsyncDownload() { |
857 HRESULT hr = E_FAIL; | 781 HRESULT hr = E_FAIL; |
858 if (moniker_.get() == NULL) { | 782 if (moniker_.get() == NULL) { |
859 std::wstring wide_url = UTF8ToWide(url()); | 783 std::wstring wide_url = UTF8ToWide(url()); |
860 hr = CreateURLMonikerEx(NULL, wide_url.c_str(), moniker_.Receive(), | 784 hr = CreateURLMonikerEx(NULL, wide_url.c_str(), moniker_.Receive(), |
861 URL_MK_UNIFORM); | 785 URL_MK_UNIFORM); |
862 if (FAILED(hr)) { | 786 if (FAILED(hr)) { |
863 NOTREACHED() << "CreateURLMonikerEx failed. Error: " << hr; | 787 NOTREACHED() << "CreateURLMonikerEx failed. Error: " << hr; |
864 } else { | 788 } else { |
865 hr = CreateAsyncBindCtx(0, this, NULL, bind_context_.Receive()); | 789 hr = CreateAsyncBindCtx(0, this, NULL, bind_context_.Receive()); |
866 DCHECK(SUCCEEDED(hr)) << "CreateAsyncBindCtx failed. Error: " << hr; | 790 DCHECK(SUCCEEDED(hr)) << "CreateAsyncBindCtx failed. Error: " << hr; |
867 } | 791 } |
868 } else { | 792 } else { |
869 DCHECK(bind_context_.get() != NULL); | 793 DCHECK(bind_context_.get() != NULL); |
870 hr = RegisterBindStatusCallback(bind_context_, this, NULL, 0); | 794 hr = RegisterBindStatusCallback(bind_context_, this, NULL, 0); |
871 } | 795 } |
872 | 796 |
873 if (SUCCEEDED(hr)) { | 797 if (SUCCEEDED(hr)) { |
874 ScopedComPtr<IStream> stream; | 798 ScopedComPtr<IStream> stream; |
875 hr = moniker_->BindToStorage(bind_context_, NULL, __uuidof(IStream), | 799 hr = moniker_->BindToStorage(bind_context_, NULL, __uuidof(IStream), |
876 reinterpret_cast<void**>(stream.Receive())); | 800 reinterpret_cast<void**>(stream.Receive())); |
| 801 // Even if hr == S_OK, binding_ could be NULL if the entire request |
| 802 // finish synchronously but then we still get all the callbacks etc. |
| 803 if (hr == S_OK) { |
| 804 DCHECK(binding_ != NULL || status_.get_state() == Status::DONE); |
| 805 } |
| 806 |
877 if (FAILED(hr)) { | 807 if (FAILED(hr)) { |
878 // TODO(joshia): Look into. This currently fails for: | 808 // TODO(joshia): Look into. This currently fails for: |
879 // http://user2:secret@localhost:1337/auth-basic?set-cookie-if-challenged | 809 // http://user2:secret@localhost:1337/auth-basic?set-cookie-if-challenged |
880 // when running the UrlRequest unit tests. | 810 // when running the UrlRequest unit tests. |
881 DLOG(ERROR) << | 811 DLOG(ERROR) << |
882 StringPrintf("IUrlMoniker::BindToStorage failed. Error: 0x%08X.", hr) | 812 StringPrintf("IUrlMoniker::BindToStorage failed. Error: 0x%08X.", hr) |
883 << std::endl << url(); | 813 << std::endl << url(); |
884 DCHECK(hr == MK_E_SYNTAX); | 814 DCHECK(hr == MK_E_SYNTAX); |
885 } | 815 } |
886 } | 816 } |
887 | 817 |
888 DLOG_IF(ERROR, FAILED(hr)) | 818 DLOG_IF(ERROR, FAILED(hr)) |
889 << StringPrintf(L"StartAsyncDownload failed: 0x%08X", hr); | 819 << StringPrintf(L"StartAsyncDownload failed: 0x%08X", hr); |
890 | 820 |
891 return hr; | 821 return hr; |
892 } | 822 } |
893 | 823 |
894 void UrlmonUrlRequest::EndRequest() { | 824 void UrlmonUrlRequest::NotifyDelegateAndDie() { |
| 825 DCHECK_EQ(thread_, PlatformThread::CurrentId()); |
895 DLOG(INFO) << __FUNCTION__; | 826 DLOG(INFO) << __FUNCTION__; |
896 | 827 PluginUrlRequestDelegate* delegate = delegate_; |
897 // In case of a redirect notification we prevent urlmon from following the | 828 delegate_ = NULL; |
898 // redirect and rely on Chrome, in which case AutomationMsg_RequestEnd | 829 ReleaseBindings(); |
899 // IPC will be sent over by Chrome to end this request. | 830 if (delegate) { |
900 if (!ignore_redirect_stop_binding_error_) { | 831 delegate->OnResponseEnd(id(), status_.get_result()); |
901 // Special case. If the last request was a redirect and the current OS | |
902 // error value is E_ACCESSDENIED, that means an unsafe redirect was | |
903 // attempted. In that case, correct the OS error value to be the more | |
904 // specific ERR_UNSAFE_REDIRECT error value. | |
905 if (!status_.is_success() && status_.os_error() == net::ERR_ACCESS_DENIED) { | |
906 int status = GetHttpResponseStatus(); | |
907 if (status >= 300 && status < 400) { | |
908 redirect_status_ = status; // store the latest redirect status value. | |
909 status_.set_os_error(net::ERR_UNSAFE_REDIRECT); | |
910 } | |
911 } | |
912 OnResponseEnd(status_); | |
913 } else { | |
914 ignore_redirect_stop_binding_error_ = false; | |
915 } | 832 } |
916 | |
917 ReleaseBindings(); | |
918 // Remove the request mapping and release the outstanding reference to us in | |
919 // the context of the UI thread. | |
920 // We should not access any members of the UrlmonUrlRequest object after this | |
921 // as the object would be deleted. | |
922 PostTask(FROM_HERE, | |
923 NewRunnableMethod(this, &UrlmonUrlRequest::EndRequestInternal)); | |
924 } | |
925 | |
926 void UrlmonUrlRequest::EndRequestInternal() { | |
927 // The request object could have been removed from the map in the | |
928 // OnRequestEnd callback which executes on receiving the | |
929 // AutomationMsg_RequestEnd IPC from Chrome. | |
930 request_handler()->RemoveRequest(this); | |
931 // The current instance could get destroyed in the context of DestroyWindow. | |
932 // We should not access the object after this. | |
933 DestroyWindow(); | |
934 } | 833 } |
935 | 834 |
936 int UrlmonUrlRequest::GetHttpResponseStatus() const { | 835 int UrlmonUrlRequest::GetHttpResponseStatus() const { |
937 if (binding_ == NULL) { | 836 if (binding_ == NULL) { |
938 DLOG(WARNING) << "GetHttpResponseStatus - no binding_"; | 837 DLOG(WARNING) << "GetHttpResponseStatus - no binding_"; |
939 return 0; | 838 return 0; |
940 } | 839 } |
941 | 840 |
942 int http_status = 0; | 841 int http_status = 0; |
943 | 842 |
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1089 ret = net::ERR_ACCESS_DENIED; | 988 ret = net::ERR_ACCESS_DENIED; |
1090 break; | 989 break; |
1091 | 990 |
1092 default: | 991 default: |
1093 DLOG(WARNING) | 992 DLOG(WARNING) |
1094 << StringPrintf("TODO: translate HRESULT 0x%08X to net::Error", hr); | 993 << StringPrintf("TODO: translate HRESULT 0x%08X to net::Error", hr); |
1095 break; | 994 break; |
1096 } | 995 } |
1097 return ret; | 996 return ret; |
1098 } | 997 } |
| 998 |
| 999 |
| 1000 bool UrlmonUrlRequestManager::IsThreadSafe() { |
| 1001 return true; |
| 1002 } |
| 1003 |
| 1004 void UrlmonUrlRequestManager::UseMonikerForUrl(IMoniker* moniker, |
| 1005 IBindCtx* bind_ctx, |
| 1006 const std::wstring& url) { |
| 1007 DCHECK(NULL == moniker_for_url_.get()); |
| 1008 moniker_for_url_.reset(new MonikerForUrl()); |
| 1009 moniker_for_url_->moniker = moniker; |
| 1010 moniker_for_url_->url = url; |
| 1011 |
| 1012 CComObject<WrappedBindContext>* ctx = NULL; |
| 1013 CComObject<WrappedBindContext>::CreateInstance(&ctx); |
| 1014 ctx->Initialize(bind_ctx); |
| 1015 ctx->QueryInterface(moniker_for_url_->bind_ctx.Receive()); |
| 1016 DCHECK(moniker_for_url_->bind_ctx.get()); |
| 1017 } |
| 1018 |
| 1019 void UrlmonUrlRequestManager::StartRequest(int request_id, |
| 1020 const IPC::AutomationURLRequest& request_info) { |
| 1021 if (stopping_) { |
| 1022 return; |
| 1023 } |
| 1024 |
| 1025 if (!worker_thread_.IsRunning()) |
| 1026 worker_thread_.Start(); |
| 1027 |
| 1028 MonikerForUrl* use_moniker = NULL; |
| 1029 if (moniker_for_url_.get()) { |
| 1030 if (GURL(moniker_for_url_->url) == GURL(request_info.url)) { |
| 1031 use_moniker = moniker_for_url_.release(); |
| 1032 } |
| 1033 } |
| 1034 |
| 1035 worker_thread_.message_loop()->PostTask(FROM_HERE, |
| 1036 NewRunnableMethod(this, &UrlmonUrlRequestManager::StartRequestWorker, |
| 1037 request_id, request_info, use_moniker)); |
| 1038 } |
| 1039 |
| 1040 void UrlmonUrlRequestManager::StartRequestWorker(int request_id, |
| 1041 const IPC::AutomationURLRequest& request_info, |
| 1042 MonikerForUrl* use_moniker) { |
| 1043 DCHECK_EQ(worker_thread_.thread_id(), PlatformThread::CurrentId()); |
| 1044 scoped_ptr<MonikerForUrl> moniker_for_url(use_moniker); |
| 1045 |
| 1046 if (stopping_) |
| 1047 return; |
| 1048 |
| 1049 DCHECK(LookupRequest(request_id).get() == NULL); |
| 1050 |
| 1051 CComObject<UrlmonUrlRequest>* new_request = NULL; |
| 1052 CComObject<UrlmonUrlRequest>::CreateInstance(&new_request); |
| 1053 |
| 1054 new_request->Initialize(static_cast<PluginUrlRequestDelegate*>(this), |
| 1055 request_id, |
| 1056 request_info.url, |
| 1057 request_info.method, |
| 1058 request_info.referrer, |
| 1059 request_info.extra_request_headers, |
| 1060 request_info.upload_data, |
| 1061 enable_frame_busting_); |
| 1062 |
| 1063 // Shall we use an existing moniker? |
| 1064 if (moniker_for_url.get()) { |
| 1065 new_request->ConnectToExistingMoniker(moniker_for_url->moniker, |
| 1066 moniker_for_url->bind_ctx, |
| 1067 moniker_for_url->url); |
| 1068 } |
| 1069 |
| 1070 DCHECK(LookupRequest(request_id).get() == NULL); |
| 1071 request_map_[request_id] = new_request; |
| 1072 map_empty_.Reset(); |
| 1073 |
| 1074 new_request->Start(); |
| 1075 } |
| 1076 |
| 1077 void UrlmonUrlRequestManager::ReadRequest(int request_id, int bytes_to_read) { |
| 1078 if (stopping_) |
| 1079 return; |
| 1080 |
| 1081 worker_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, |
| 1082 &UrlmonUrlRequestManager::ReadRequestWorker, request_id, bytes_to_read)); |
| 1083 } |
| 1084 |
| 1085 void UrlmonUrlRequestManager::ReadRequestWorker(int request_id, |
| 1086 int bytes_to_read) { |
| 1087 DCHECK_EQ(worker_thread_.thread_id(), PlatformThread::CurrentId()); |
| 1088 scoped_refptr<UrlmonUrlRequest> request = LookupRequest(request_id); |
| 1089 // if zero, it may just have had network error. |
| 1090 if (request) { |
| 1091 request->Read(bytes_to_read); |
| 1092 } |
| 1093 } |
| 1094 |
| 1095 void UrlmonUrlRequestManager::EndRequest(int request_id) { |
| 1096 if (stopping_) |
| 1097 return; |
| 1098 |
| 1099 worker_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, |
| 1100 &UrlmonUrlRequestManager::EndRequestWorker, request_id)); |
| 1101 } |
| 1102 |
| 1103 void UrlmonUrlRequestManager::EndRequestWorker(int request_id) { |
| 1104 DCHECK_EQ(worker_thread_.thread_id(), PlatformThread::CurrentId()); |
| 1105 scoped_refptr<UrlmonUrlRequest> request = LookupRequest(request_id); |
| 1106 if (request) { |
| 1107 request->Stop(); |
| 1108 } |
| 1109 } |
| 1110 |
| 1111 void UrlmonUrlRequestManager::StopAll() { |
| 1112 if (stopping_) |
| 1113 return; |
| 1114 |
| 1115 stopping_ = true; |
| 1116 |
| 1117 if (!worker_thread_.IsRunning()) |
| 1118 return; |
| 1119 |
| 1120 worker_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, |
| 1121 &UrlmonUrlRequestManager::StopAllWorker)); |
| 1122 |
| 1123 // Note we may not call worker_thread_.Stop() here. The MessageLoop's quit |
| 1124 // task will be serialized after request::Stop tasks, but requests may |
| 1125 // not quit immediately. CoUninitialize has a modal message loop, but it |
| 1126 // does not help in this case. |
| 1127 // Normally we call binding->Abort() and expect OnStopBinding() callback |
| 1128 // where we inform UrlmonUrlRequestManager that request is dead. |
| 1129 // The problem is that while waiting for OnStopBinding(), Quit Task may be |
| 1130 // picked up and executed, thus exiting the thread. |
| 1131 map_empty_.Wait(); |
| 1132 worker_thread_.Stop(); |
| 1133 DCHECK_EQ(0, UrlmonUrlRequest::instance_count_); |
| 1134 } |
| 1135 |
| 1136 void UrlmonUrlRequestManager::StopAllWorker() { |
| 1137 DCHECK_EQ(worker_thread_.thread_id(), PlatformThread::CurrentId()); |
| 1138 DCHECK_EQ(true, stopping_); |
| 1139 |
| 1140 std::vector<scoped_refptr<UrlmonUrlRequest> > request_list; |
| 1141 // We copy the pending requests into a temporary vector as the Stop |
| 1142 // function in the request could also try to delete the request from |
| 1143 // the request map and the iterator could end up being invalid. |
| 1144 for (RequestMap::iterator it = request_map_.begin(); |
| 1145 it != request_map_.end(); ++it) { |
| 1146 DCHECK(it->second != NULL); |
| 1147 request_list.push_back(it->second); |
| 1148 } |
| 1149 |
| 1150 for (std::vector<scoped_refptr<UrlmonUrlRequest> >::size_type index = 0; |
| 1151 index < request_list.size(); ++index) { |
| 1152 request_list[index]->Stop(); |
| 1153 } |
| 1154 } |
| 1155 |
| 1156 void UrlmonUrlRequestManager::OnResponseStarted(int request_id, |
| 1157 const char* mime_type, const char* headers, int size, |
| 1158 base::Time last_modified, const std::string& peristent_cookies, |
| 1159 const std::string& redirect_url, int redirect_status) { |
| 1160 DCHECK_EQ(worker_thread_.thread_id(), PlatformThread::CurrentId()); |
| 1161 DCHECK(LookupRequest(request_id).get() != NULL); |
| 1162 delegate_->OnResponseStarted(request_id, mime_type, headers, size, |
| 1163 last_modified, peristent_cookies, redirect_url, redirect_status); |
| 1164 } |
| 1165 |
| 1166 void UrlmonUrlRequestManager::OnReadComplete(int request_id, const void* buffer, |
| 1167 int len) { |
| 1168 DCHECK_EQ(worker_thread_.thread_id(), PlatformThread::CurrentId()); |
| 1169 DCHECK(LookupRequest(request_id).get() != NULL); |
| 1170 delegate_->OnReadComplete(request_id, buffer, len); |
| 1171 } |
| 1172 |
| 1173 void UrlmonUrlRequestManager::OnResponseEnd(int request_id, |
| 1174 const URLRequestStatus& status) { |
| 1175 DCHECK_EQ(worker_thread_.thread_id(), PlatformThread::CurrentId()); |
| 1176 RequestMap::size_type n = request_map_.erase(request_id); |
| 1177 DCHECK_EQ(1, n); |
| 1178 |
| 1179 if (request_map_.size() == 0) |
| 1180 map_empty_.Signal(); |
| 1181 |
| 1182 // Inform delegate unless the request has been explicitly cancelled. |
| 1183 if (status.status() != URLRequestStatus::CANCELED) |
| 1184 delegate_->OnResponseEnd(request_id, status); |
| 1185 } |
| 1186 |
| 1187 scoped_refptr<UrlmonUrlRequest> UrlmonUrlRequestManager::LookupRequest( |
| 1188 int request_id) { |
| 1189 RequestMap::iterator it = request_map_.find(request_id); |
| 1190 if (request_map_.end() != it) |
| 1191 return it->second; |
| 1192 return NULL; |
| 1193 } |
| 1194 |
| 1195 UrlmonUrlRequestManager::UrlmonUrlRequestManager() |
| 1196 : stopping_(false), worker_thread_("UrlMon fetch thread"), |
| 1197 map_empty_(true, true) { |
| 1198 } |
| 1199 |
| 1200 UrlmonUrlRequestManager::~UrlmonUrlRequestManager() { |
| 1201 StopAll(); |
| 1202 } |
| 1203 |
| 1204 // Called from UI thread. |
| 1205 void UrlmonUrlRequestManager::StealMonikerFromRequest(int request_id, |
| 1206 IMoniker** moniker) { |
| 1207 if (stopping_) |
| 1208 return; |
| 1209 |
| 1210 base::WaitableEvent done(true, false); |
| 1211 worker_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, |
| 1212 &UrlmonUrlRequestManager::StealMonikerFromRequestWorker, |
| 1213 request_id, moniker, &done)); |
| 1214 |
| 1215 // Wait until moniker is grabbed from a request in the worker thread. |
| 1216 done.Wait(); |
| 1217 } |
| 1218 |
| 1219 void UrlmonUrlRequestManager::StealMonikerFromRequestWorker(int request_id, |
| 1220 IMoniker** moniker, base::WaitableEvent* done) { |
| 1221 if (!stopping_) { |
| 1222 scoped_refptr<UrlmonUrlRequest> request = LookupRequest(request_id); |
| 1223 if (request) { |
| 1224 request->StealMoniker(moniker); |
| 1225 request->Stop(); |
| 1226 } |
| 1227 } |
| 1228 |
| 1229 done->Signal(); |
| 1230 } |
OLD | NEW |