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

Side by Side Diff: chrome_frame/urlmon_url_request.cc

Issue 126143005: Remove Chrome Frame code and resources. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: sync to r244038 Created 6 years, 11 months 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 | « chrome_frame/urlmon_url_request.h ('k') | chrome_frame/urlmon_url_request_private.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome_frame/urlmon_url_request.h"
6
7 #include <urlmon.h>
8 #include <wininet.h>
9
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/threading/platform_thread.h"
19 #include "base/threading/thread.h"
20 #include "chrome/common/automation_messages.h"
21 #include "chrome_frame/bind_context_info.h"
22 #include "chrome_frame/chrome_frame_activex_base.h"
23 #include "chrome_frame/extra_system_apis.h"
24 #include "chrome_frame/html_utils.h"
25 #include "chrome_frame/urlmon_upload_data_stream.h"
26 #include "chrome_frame/urlmon_url_request_private.h"
27 #include "chrome_frame/utils.h"
28 #include "net/base/load_flags.h"
29 #include "net/http/http_response_headers.h"
30 #include "net/http/http_util.h"
31
32 #define IS_HTTP_SUCCESS_CODE(code) (code >= 200 && code <= 299)
33
34 UrlmonUrlRequest::UrlmonUrlRequest()
35 : pending_read_size_(0),
36 headers_received_(false),
37 calling_delegate_(0),
38 thread_(NULL),
39 parent_window_(NULL),
40 privileged_mode_(false),
41 pending_(false),
42 is_expecting_download_(true),
43 cleanup_transaction_(false) {
44 DVLOG(1) << __FUNCTION__ << me();
45 }
46
47 UrlmonUrlRequest::~UrlmonUrlRequest() {
48 DVLOG(1) << __FUNCTION__ << me();
49 }
50
51 std::string UrlmonUrlRequest::me() const {
52 return base::StringPrintf(" id: %i Obj: %X ", id(), this);
53 }
54
55 bool UrlmonUrlRequest::Start() {
56 DVLOG(1) << __FUNCTION__ << me() << url();
57 DCHECK(thread_ == 0 || thread_ == base::PlatformThread::CurrentId());
58 thread_ = base::PlatformThread::CurrentId();
59 status_.Start();
60 // Initialize the net::HostPortPair structure from the url initially. We may
61 // not receive the ip address of the host if the request is satisfied from
62 // the cache.
63 socket_address_ = net::HostPortPair::FromURL(GURL(url()));
64 // The UrlmonUrlRequest instance can get destroyed in the context of
65 // StartAsyncDownload if BindToStorage finishes synchronously with an error.
66 // Grab a reference to protect against this.
67 scoped_refptr<UrlmonUrlRequest> ref(this);
68 HRESULT hr = StartAsyncDownload();
69 if (FAILED(hr) && status_.get_state() != UrlmonUrlRequest::Status::DONE) {
70 status_.Done();
71 status_.set_result(net::URLRequestStatus::FAILED, HresultToNetError(hr));
72 NotifyDelegateAndDie();
73 }
74 return true;
75 }
76
77 void UrlmonUrlRequest::Stop() {
78 DCHECK_EQ(thread_, base::PlatformThread::CurrentId());
79 DCHECK((status_.get_state() != Status::DONE) == (binding_ != NULL));
80 Status::State state = status_.get_state();
81 delegate_ = NULL;
82
83 // If DownloadInHost is already requested, we will quit soon anyway.
84 if (terminate_requested())
85 return;
86
87 switch (state) {
88 case Status::WORKING:
89 status_.Cancel();
90 if (binding_)
91 binding_->Abort();
92 break;
93
94 case Status::ABORTING:
95 status_.Cancel();
96 break;
97
98 case Status::DONE:
99 status_.Cancel();
100 NotifyDelegateAndDie();
101 break;
102 }
103 }
104
105 bool UrlmonUrlRequest::Read(int bytes_to_read) {
106 DCHECK_EQ(thread_, base::PlatformThread::CurrentId());
107 DCHECK_GE(bytes_to_read, 0);
108 DCHECK_EQ(0, calling_delegate_);
109 DVLOG(1) << __FUNCTION__ << me();
110
111 is_expecting_download_ = false;
112
113 // Re-entrancy check. Thou shall not call Read() while process OnReadComplete!
114 DCHECK_EQ(0u, pending_read_size_);
115 if (pending_read_size_ != 0)
116 return false;
117
118 DCHECK((status_.get_state() != Status::DONE) == (binding_ != NULL));
119 if (status_.get_state() == Status::ABORTING)
120 return true;
121
122 // Send data if available.
123 size_t bytes_copied = 0;
124 if ((bytes_copied = SendDataToDelegate(bytes_to_read))) {
125 DVLOG(1) << __FUNCTION__ << me() << " bytes read: " << bytes_copied;
126 return true;
127 }
128
129 if (status_.get_state() == Status::WORKING) {
130 DVLOG(1) << __FUNCTION__ << me() << " pending: " << bytes_to_read;
131 pending_read_size_ = bytes_to_read;
132 } else {
133 DVLOG(1) << __FUNCTION__ << me() << " Response finished.";
134 NotifyDelegateAndDie();
135 }
136
137 return true;
138 }
139
140 HRESULT UrlmonUrlRequest::InitPending(const GURL& url, IMoniker* moniker,
141 IBindCtx* bind_context,
142 bool enable_frame_busting,
143 bool privileged_mode,
144 HWND notification_window,
145 IStream* cache) {
146 DVLOG(1) << __FUNCTION__ << me() << url.spec();
147 DCHECK(bind_context_ == NULL);
148 DCHECK(moniker_ == NULL);
149 DCHECK(cache_ == NULL);
150 DCHECK(thread_ == 0 || thread_ == base::PlatformThread::CurrentId());
151 thread_ = base::PlatformThread::CurrentId();
152 bind_context_ = bind_context;
153 moniker_ = moniker;
154 enable_frame_busting_ = enable_frame_busting;
155 privileged_mode_ = privileged_mode;
156 parent_window_ = notification_window;
157 cache_ = cache;
158 set_url(url.spec());
159 set_pending(true);
160
161 // Request has already started and data is fetched. We will get the
162 // GetBindInfo call as per contract but the return values are
163 // ignored. So just set "get" as a method to make our GetBindInfo
164 // implementation happy.
165 method_ = "get";
166 return S_OK;
167 }
168
169 void UrlmonUrlRequest::TerminateBind(const TerminateBindCallback& callback) {
170 DCHECK_EQ(thread_, base::PlatformThread::CurrentId());
171 DVLOG(1) << __FUNCTION__ << me();
172 cleanup_transaction_ = false;
173 if (status_.get_state() == Status::DONE) {
174 // Binding is stopped. Note result could be an error.
175 callback.Run(moniker_, bind_context_, upload_data_,
176 request_headers_.c_str());
177 } else {
178 // WORKING (ABORTING?). Save the callback.
179 // Now we will return INET_TERMINATE_BIND from ::OnDataAvailable() and in
180 // ::OnStopBinding will invoke the callback passing our moniker and
181 // bind context.
182 terminate_bind_callback_ = callback;
183 if (pending_data_) {
184 // For downloads to work correctly, we must induce a call to
185 // OnDataAvailable so that we can download INET_E_TERMINATED_BIND and
186 // get IE into the correct state.
187 // To accomplish this we read everything that's readily available in
188 // the current stream. Once we've reached the end of the stream we
189 // should get E_PENDING back and then later we'll get that call
190 // to OnDataAvailable.
191 std::string data;
192 base::win::ScopedComPtr<IStream> read_stream(pending_data_);
193 HRESULT hr;
194 while ((hr = ReadStream(read_stream, 0xffff, &data)) == S_OK) {
195 // Just drop the data.
196 }
197 DLOG_IF(WARNING, hr != E_PENDING) << __FUNCTION__ <<
198 base::StringPrintf(" expected E_PENDING but got 0x%08X", hr);
199 }
200 }
201 }
202
203 size_t UrlmonUrlRequest::SendDataToDelegate(size_t bytes_to_read) {
204 return 0;
205 }
206
207 STDMETHODIMP UrlmonUrlRequest::OnStartBinding(DWORD reserved,
208 IBinding* binding) {
209 DCHECK_EQ(thread_, base::PlatformThread::CurrentId());
210 binding_ = binding;
211 if (pending_) {
212 response_headers_ = GetHttpHeadersFromBinding(binding_);
213 DCHECK(!response_headers_.empty());
214 }
215 return S_OK;
216 }
217
218 STDMETHODIMP UrlmonUrlRequest::GetPriority(LONG *priority) {
219 if (!priority)
220 return E_POINTER;
221 *priority = THREAD_PRIORITY_NORMAL;
222 return S_OK;
223 }
224
225 STDMETHODIMP UrlmonUrlRequest::OnLowResource(DWORD reserved) {
226 return S_OK;
227 }
228
229 STDMETHODIMP UrlmonUrlRequest::OnProgress(ULONG progress, ULONG max_progress,
230 ULONG status_code, LPCWSTR status_text) {
231 DCHECK_EQ(thread_, base::PlatformThread::CurrentId());
232
233 if (status_.get_state() != Status::WORKING)
234 return S_OK;
235
236 // Ignore any notifications received while we are in the pending state
237 // waiting for the request to be initiated by Chrome.
238 if (pending_ && status_code != BINDSTATUS_REDIRECTING)
239 return S_OK;
240
241 if (!delegate_) {
242 DVLOG(1) << "Invalid delegate";
243 return S_OK;
244 }
245
246 switch (status_code) {
247 case BINDSTATUS_CONNECTING: {
248 if (status_text) {
249 socket_address_.set_host(base::WideToUTF8(status_text));
250 }
251 break;
252 }
253
254 case BINDSTATUS_REDIRECTING: {
255 // If we receive a redirect for the initial pending request initiated
256 // when our document loads we should stash it away and inform Chrome
257 // accordingly when it requests data for the original URL.
258 base::win::ScopedComPtr<BindContextInfo> info;
259 BindContextInfo::FromBindContext(bind_context_, info.Receive());
260 DCHECK(info);
261 GURL previously_redirected(info ? info->GetUrl() : std::wstring());
262 if (GURL(status_text) != previously_redirected) {
263 DVLOG(1) << __FUNCTION__ << me() << "redirect from " << url()
264 << " to " << status_text;
265 // Fetch the redirect status as they aren't all equal (307 in particular
266 // retains the HTTP request verb).
267 int http_code = GetHttpResponseStatusFromBinding(binding_);
268 status_.SetRedirected(http_code, base::WideToUTF8(status_text));
269 // Abort. We will inform Chrome in OnStopBinding callback.
270 binding_->Abort();
271 return E_ABORT;
272 }
273 break;
274 }
275
276 case BINDSTATUS_COOKIE_SENT:
277 delegate_->AddPrivacyDataForUrl(url(), "", COOKIEACTION_READ);
278 break;
279
280 case BINDSTATUS_COOKIE_SUPPRESSED:
281 delegate_->AddPrivacyDataForUrl(url(), "", COOKIEACTION_SUPPRESS);
282 break;
283
284 case BINDSTATUS_COOKIE_STATE_ACCEPT:
285 delegate_->AddPrivacyDataForUrl(url(), "", COOKIEACTION_ACCEPT);
286 break;
287
288 case BINDSTATUS_COOKIE_STATE_REJECT:
289 delegate_->AddPrivacyDataForUrl(url(), "", COOKIEACTION_REJECT);
290 break;
291
292 case BINDSTATUS_COOKIE_STATE_LEASH:
293 delegate_->AddPrivacyDataForUrl(url(), "", COOKIEACTION_LEASH);
294 break;
295
296 case BINDSTATUS_COOKIE_STATE_DOWNGRADE:
297 delegate_->AddPrivacyDataForUrl(url(), "", COOKIEACTION_DOWNGRADE);
298 break;
299
300 case BINDSTATUS_COOKIE_STATE_UNKNOWN:
301 NOTREACHED() << L"Unknown cookie state received";
302 break;
303
304 default:
305 DVLOG(1) << __FUNCTION__ << me()
306 << base::StringPrintf(L"code: %i status: %ls", status_code,
307 status_text);
308 break;
309 }
310
311 return S_OK;
312 }
313
314 STDMETHODIMP UrlmonUrlRequest::OnStopBinding(HRESULT result, LPCWSTR error) {
315 DCHECK_EQ(thread_, base::PlatformThread::CurrentId());
316 DVLOG(1) << __FUNCTION__ << me()
317 << "- Request stopped, Result: " << std::hex << result;
318 DCHECK(status_.get_state() == Status::WORKING ||
319 status_.get_state() == Status::ABORTING);
320
321 Status::State state = status_.get_state();
322
323 // Mark we a are done.
324 status_.Done();
325
326 if (result == INET_E_TERMINATED_BIND) {
327 if (terminate_requested()) {
328 terminate_bind_callback_.Run(moniker_, bind_context_, upload_data_,
329 request_headers_.c_str());
330 } else {
331 cleanup_transaction_ = true;
332 }
333 // We may have returned INET_E_TERMINATED_BIND from OnDataAvailable.
334 result = S_OK;
335 }
336
337 if (state == Status::WORKING) {
338 status_.set_result(result);
339
340 if (FAILED(result)) {
341 int http_code = GetHttpResponseStatusFromBinding(binding_);
342 // For certain requests like empty POST requests the server can return
343 // back a HTTP success code in the range 200 to 299. We need to flag
344 // these requests as succeeded.
345 if (IS_HTTP_SUCCESS_CODE(http_code)) {
346 // If this DCHECK fires it means that the server returned a HTTP
347 // success code outside the standard range 200-206. We need to confirm
348 // if the following code path is correct.
349 DCHECK_LE(http_code, 206);
350 status_.set_result(S_OK);
351 std::string headers = GetHttpHeadersFromBinding(binding_);
352 OnResponse(0, base::UTF8ToWide(headers).c_str(), NULL, NULL);
353 } else if (net::HttpResponseHeaders::IsRedirectResponseCode(http_code) &&
354 result == E_ACCESSDENIED) {
355 // Special case. If the last request was a redirect and the current OS
356 // error value is E_ACCESSDENIED, that means an unsafe redirect was
357 // attempted. In that case, correct the OS error value to be the more
358 // specific ERR_UNSAFE_REDIRECT error value.
359 status_.set_result(net::URLRequestStatus::FAILED,
360 net::ERR_UNSAFE_REDIRECT);
361 }
362 }
363
364 // The code below seems easy but it is not. :)
365 // The network policy in Chrome network is that error code/end_of_stream
366 // should be returned only as a result of read (or start) request.
367 // Here are the possible cases:
368 // pending_data_|pending_read
369 // FALSE |FALSE => EndRequest if no headers, otherwise wait for Read.
370 // FALSE |TRUE => EndRequest.
371 // TRUE |FALSE => Wait for Read.
372 // TRUE |TRUE => Something went wrong!!
373
374 if (pending_data_) {
375 DCHECK_EQ(pending_read_size_, 0UL);
376 ReleaseBindings();
377 return S_OK;
378 }
379
380 if (headers_received_ && pending_read_size_ == 0) {
381 ReleaseBindings();
382 return S_OK;
383 }
384
385 // No headers or there is a pending read from Chrome.
386 NotifyDelegateAndDie();
387 return S_OK;
388 }
389
390 // Status::ABORTING
391 if (status_.was_redirected()) {
392 // Just release bindings here. Chrome will issue EndRequest(request_id)
393 // after processing headers we had provided.
394 if (!pending_) {
395 std::string headers = GetHttpHeadersFromBinding(binding_);
396 OnResponse(0, base::UTF8ToWide(headers).c_str(), NULL, NULL);
397 }
398 ReleaseBindings();
399 return S_OK;
400 }
401
402 // Stop invoked.
403 NotifyDelegateAndDie();
404 return S_OK;
405 }
406
407 STDMETHODIMP UrlmonUrlRequest::GetBindInfo(DWORD* bind_flags,
408 BINDINFO* bind_info) {
409 if ((bind_info == NULL) || (bind_info->cbSize == 0) || (bind_flags == NULL))
410 return E_INVALIDARG;
411
412 *bind_flags = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA;
413
414 bind_info->dwOptionsFlags = INTERNET_FLAG_NO_AUTO_REDIRECT;
415 bind_info->dwOptions = BINDINFO_OPTIONS_WININETFLAG;
416
417 // TODO(ananta)
418 // Look into whether the other load flags need to be supported in chrome
419 // frame.
420 if (load_flags_ & net::LOAD_VALIDATE_CACHE)
421 *bind_flags |= BINDF_RESYNCHRONIZE;
422
423 if (load_flags_ & net::LOAD_BYPASS_CACHE)
424 *bind_flags |= BINDF_GETNEWESTVERSION;
425
426 if (LowerCaseEqualsASCII(method(), "get")) {
427 bind_info->dwBindVerb = BINDVERB_GET;
428 } else if (LowerCaseEqualsASCII(method(), "post")) {
429 bind_info->dwBindVerb = BINDVERB_POST;
430 } else if (LowerCaseEqualsASCII(method(), "put")) {
431 bind_info->dwBindVerb = BINDVERB_PUT;
432 } else {
433 std::wstring verb(base::ASCIIToWide(StringToUpperASCII(method())));
434 bind_info->dwBindVerb = BINDVERB_CUSTOM;
435 bind_info->szCustomVerb = reinterpret_cast<wchar_t*>(
436 ::CoTaskMemAlloc((verb.length() + 1) * sizeof(wchar_t)));
437 lstrcpyW(bind_info->szCustomVerb, verb.c_str());
438 }
439
440 if (bind_info->dwBindVerb == BINDVERB_POST ||
441 bind_info->dwBindVerb == BINDVERB_PUT ||
442 post_data_len() > 0) {
443 // Bypass caching proxies on upload requests and avoid writing responses to
444 // the browser's cache.
445 *bind_flags |= BINDF_GETNEWESTVERSION | BINDF_PRAGMA_NO_CACHE;
446
447 // Attempt to avoid storing the response for upload requests.
448 // See http://crbug.com/55918
449 if (resource_type_ != ResourceType::MAIN_FRAME)
450 *bind_flags |= BINDF_NOWRITECACHE;
451
452 // Initialize the STGMEDIUM.
453 memset(&bind_info->stgmedData, 0, sizeof(STGMEDIUM));
454 bind_info->grfBindInfoF = 0;
455
456 if (bind_info->dwBindVerb != BINDVERB_CUSTOM)
457 bind_info->szCustomVerb = NULL;
458
459 if ((post_data_len() || is_chunked_upload()) &&
460 get_upload_data(&bind_info->stgmedData.pstm) == S_OK) {
461 bind_info->stgmedData.tymed = TYMED_ISTREAM;
462 if (!is_chunked_upload()) {
463 bind_info->cbstgmedData = static_cast<DWORD>(post_data_len());
464 }
465 DVLOG(1) << __FUNCTION__ << me() << method()
466 << " request with " << base::Int64ToString(post_data_len())
467 << " bytes. url=" << url();
468 } else {
469 DVLOG(1) << __FUNCTION__ << me() << "POST request with no data!";
470 }
471 }
472 return S_OK;
473 }
474
475 STDMETHODIMP UrlmonUrlRequest::OnDataAvailable(DWORD flags, DWORD size,
476 FORMATETC* formatetc,
477 STGMEDIUM* storage) {
478 DCHECK_EQ(thread_, base::PlatformThread::CurrentId());
479 DVLOG(1) << __FUNCTION__ << me() << "bytes available: " << size;
480
481 if (terminate_requested()) {
482 DVLOG(1) << " Download requested. INET_E_TERMINATED_BIND returned";
483 return INET_E_TERMINATED_BIND;
484 }
485
486 if (!storage || (storage->tymed != TYMED_ISTREAM)) {
487 NOTREACHED();
488 return E_INVALIDARG;
489 }
490
491 IStream* read_stream = storage->pstm;
492 if (!read_stream) {
493 NOTREACHED();
494 return E_UNEXPECTED;
495 }
496
497 // Some requests such as HEAD have zero data.
498 if (size > 0)
499 pending_data_ = read_stream;
500
501 if (pending_read_size_) {
502 size_t bytes_copied = SendDataToDelegate(pending_read_size_);
503 DVLOG(1) << __FUNCTION__ << me() << "size read: " << bytes_copied;
504 } else {
505 DVLOG(1) << __FUNCTION__ << me() << "- waiting for remote read";
506 }
507
508 if (BSCF_LASTDATANOTIFICATION & flags) {
509 if (!is_expecting_download_ || pending()) {
510 DVLOG(1) << __FUNCTION__ << me() << "EOF";
511 return S_OK;
512 }
513 // Always return INET_E_TERMINATED_BIND to allow bind context reuse
514 // if DownloadToHost is suddenly requested.
515 DVLOG(1) << __FUNCTION__ << " EOF: INET_E_TERMINATED_BIND returned";
516 return INET_E_TERMINATED_BIND;
517 }
518 return S_OK;
519 }
520
521 STDMETHODIMP UrlmonUrlRequest::OnObjectAvailable(REFIID iid, IUnknown* object) {
522 // We are calling BindToStorage on the moniker we should always get called
523 // back on OnDataAvailable and should never get OnObjectAvailable
524 NOTREACHED();
525 return E_NOTIMPL;
526 }
527
528 STDMETHODIMP UrlmonUrlRequest::BeginningTransaction(const wchar_t* url,
529 const wchar_t* current_headers, DWORD reserved,
530 wchar_t** additional_headers) {
531 DCHECK_EQ(thread_, base::PlatformThread::CurrentId());
532 if (!additional_headers) {
533 NOTREACHED();
534 return E_POINTER;
535 }
536
537 DVLOG(1) << __FUNCTION__ << me() << "headers: \n" << current_headers;
538
539 if (status_.get_state() == Status::ABORTING) {
540 // At times the BINDSTATUS_REDIRECTING notification which is sent to the
541 // IBindStatusCallback interface does not have an accompanying HTTP
542 // redirect status code, i.e. the attempt to query the HTTP status code
543 // from the binding returns 0, 200, etc which are invalid redirect codes.
544 // We don't want urlmon to follow redirects. We return E_ABORT in our
545 // IBindStatusCallback::OnProgress function and also abort the binding.
546 // However urlmon still tries to establish a transaction with the
547 // redirected URL which confuses the web server.
548 // Fix is to abort the attempted transaction.
549 DLOG(WARNING) << __FUNCTION__ << me()
550 << ": Aborting connection to URL:"
551 << url
552 << " as the binding has been aborted";
553 return E_ABORT;
554 }
555
556 HRESULT hr = S_OK;
557
558 std::string new_headers;
559 if (is_chunked_upload()) {
560 new_headers = base::StringPrintf("Transfer-Encoding: chunked\r\n");
561 }
562
563 if (!extra_headers().empty()) {
564 // TODO(robertshield): We may need to sanitize headers on POST here.
565 new_headers += extra_headers();
566 }
567
568 if (!referrer().empty()) {
569 // Referrer is famously misspelled in HTTP:
570 new_headers += base::StringPrintf("Referer: %s\r\n", referrer().c_str());
571 }
572
573 // In the rare case if "User-Agent" string is already in |current_headers|.
574 // We send Chrome's user agent in requests initiated within ChromeFrame to
575 // enable third party content in pages rendered in ChromeFrame to correctly
576 // send content for Chrome as the third party content may not be equipped to
577 // identify chromeframe as the user agent. This also ensures that the user
578 // agent reported in scripts in chrome frame is consistent with that sent
579 // in outgoing requests.
580 std::string user_agent = http_utils::AddChromeFrameToUserAgentValue(
581 http_utils::GetChromeUserAgent());
582 new_headers += ReplaceOrAddUserAgent(current_headers, user_agent);
583
584 if (!new_headers.empty()) {
585 *additional_headers = reinterpret_cast<wchar_t*>(
586 CoTaskMemAlloc((new_headers.size() + 1) * sizeof(wchar_t)));
587
588 if (*additional_headers == NULL) {
589 NOTREACHED();
590 hr = E_OUTOFMEMORY;
591 } else {
592 lstrcpynW(*additional_headers, base::ASCIIToWide(new_headers).c_str(),
593 new_headers.size());
594 }
595 }
596 request_headers_ = new_headers;
597 return hr;
598 }
599
600 STDMETHODIMP UrlmonUrlRequest::OnResponse(DWORD dwResponseCode,
601 const wchar_t* response_headers, const wchar_t* request_headers,
602 wchar_t** additional_headers) {
603 return S_OK;
604 }
605
606 STDMETHODIMP UrlmonUrlRequest::GetWindow(const GUID& guid_reason,
607 HWND* parent_window) {
608 if (!parent_window)
609 return E_INVALIDARG;
610
611 #ifndef NDEBUG
612 wchar_t guid[40] = {0};
613 ::StringFromGUID2(guid_reason, guid, arraysize(guid));
614 const wchar_t* str = guid;
615 if (guid_reason == IID_IAuthenticate)
616 str = L"IAuthenticate";
617 else if (guid_reason == IID_IHttpSecurity)
618 str = L"IHttpSecurity";
619 else if (guid_reason == IID_IWindowForBindingUI)
620 str = L"IWindowForBindingUI";
621 DVLOG(1) << __FUNCTION__ << me() << "GetWindow: " << str;
622 #endif
623 // We should return a non-NULL HWND as parent. Otherwise no dialog is shown.
624 // TODO(iyengar): This hits when running the URL request tests.
625 DLOG_IF(WARNING, !::IsWindow(parent_window_))
626 << "UrlmonUrlRequest::GetWindow - no window!";
627 *parent_window = parent_window_;
628 return S_OK;
629 }
630
631 STDMETHODIMP UrlmonUrlRequest::Authenticate(HWND* parent_window,
632 LPWSTR* user_name,
633 LPWSTR* password) {
634 if (!parent_window)
635 return E_INVALIDARG;
636
637 if (privileged_mode_)
638 return E_ACCESSDENIED;
639
640 DCHECK(::IsWindow(parent_window_));
641 *parent_window = parent_window_;
642 return S_OK;
643 }
644
645 STDMETHODIMP UrlmonUrlRequest::OnSecurityProblem(DWORD problem) {
646 // Urlmon notifies the client of authentication problems, certificate
647 // errors, etc by querying the object implementing the IBindStatusCallback
648 // interface for the IHttpSecurity interface. If this interface is not
649 // implemented then Urlmon checks for the problem codes defined below
650 // and performs actions as defined below:-
651 // It invokes the ReportProgress method of the protocol sink with
652 // these problem codes and eventually invokes the ReportResult method
653 // on the protocol sink which ends up in a call to the OnStopBinding
654 // method of the IBindStatusCallBack interface.
655
656 // MSHTML's implementation of the IBindStatusCallback interface does not
657 // implement the IHttpSecurity interface. However it handles the
658 // OnStopBinding call with a HRESULT of 0x800c0019 and navigates to
659 // an interstitial page which presents the user with a choice of whether
660 // to abort the navigation.
661
662 // In our OnStopBinding implementation we stop the navigation and inform
663 // Chrome about the result. Ideally Chrome should behave in a manner similar
664 // to IE, i.e. display the SSL error interstitial page and if the user
665 // decides to proceed anyway we would turn off SSL warnings for that
666 // particular navigation and allow IE to download the content.
667 // We would need to return the certificate information to Chrome for display
668 // purposes. Currently we only return a dummy certificate to Chrome.
669 // At this point we decided that it is a lot of work at this point and
670 // decided to go with the easier option of implementing the IHttpSecurity
671 // interface and replicating the checks performed by Urlmon. This
672 // causes Urlmon to display a dialog box on the same lines as IE6.
673 DVLOG(1) << __FUNCTION__ << me() << "Security problem : " << problem;
674
675 // On IE6 the default IBindStatusCallback interface does not implement the
676 // IHttpSecurity interface and thus causes IE to put up a certificate error
677 // dialog box. We need to emulate this behavior for sites with mismatched
678 // certificates to work.
679 if (GetIEVersion() == IE_6)
680 return S_FALSE;
681
682 HRESULT hr = E_ABORT;
683
684 switch (problem) {
685 case ERROR_INTERNET_SEC_CERT_REV_FAILED: {
686 hr = RPC_E_RETRY;
687 break;
688 }
689
690 case ERROR_INTERNET_SEC_CERT_DATE_INVALID:
691 case ERROR_INTERNET_SEC_CERT_CN_INVALID:
692 case ERROR_INTERNET_INVALID_CA: {
693 hr = S_FALSE;
694 break;
695 }
696
697 default: {
698 NOTREACHED() << "Unhandled security problem : " << problem;
699 break;
700 }
701 }
702 return hr;
703 }
704
705 HRESULT UrlmonUrlRequest::StartAsyncDownload() {
706 DVLOG(1) << __FUNCTION__ << me() << url();
707 HRESULT hr = E_FAIL;
708 DCHECK((moniker_ && bind_context_) || (!moniker_ && !bind_context_));
709
710 if (!moniker_.get()) {
711 std::wstring wide_url = base::UTF8ToWide(url());
712 hr = CreateURLMonikerEx(NULL, wide_url.c_str(), moniker_.Receive(),
713 URL_MK_UNIFORM);
714 if (FAILED(hr)) {
715 NOTREACHED() << "CreateURLMonikerEx failed. Error: " << hr;
716 return hr;
717 }
718 }
719
720 if (bind_context_.get() == NULL) {
721 hr = ::CreateAsyncBindCtxEx(NULL, 0, this, NULL,
722 bind_context_.Receive(), 0);
723 DCHECK(SUCCEEDED(hr)) << "CreateAsyncBindCtxEx failed. Error: " << hr;
724 } else {
725 // Use existing bind context.
726 hr = ::RegisterBindStatusCallback(bind_context_, this, NULL, 0);
727 DCHECK(SUCCEEDED(hr)) << "RegisterBindStatusCallback failed. Error: " << hr;
728 }
729
730 if (SUCCEEDED(hr)) {
731 base::win::ScopedComPtr<IStream> stream;
732
733 // BindToStorage may complete synchronously.
734 // We still get all the callbacks - OnStart/StopBinding, this may result
735 // in destruction of our object. It's fine but we access some members
736 // below for debug info. :)
737 base::win::ScopedComPtr<IHttpSecurity> self(this);
738
739 // Inform our moniker patch this binding should not be tortured.
740 base::win::ScopedComPtr<BindContextInfo> info;
741 BindContextInfo::FromBindContext(bind_context_, info.Receive());
742 DCHECK(info);
743 if (info)
744 info->set_chrome_request(true);
745
746 hr = moniker_->BindToStorage(bind_context_, NULL, __uuidof(IStream),
747 reinterpret_cast<void**>(stream.Receive()));
748 if (hr == S_OK)
749 DCHECK(binding_ != NULL || status_.get_state() == Status::DONE);
750
751 if (FAILED(hr)) {
752 // TODO(joshia): Look into. This currently fails for:
753 // http://user2:secret@localhost:1337/auth-basic?set-cookie-if-challenged
754 // when running the UrlRequest unit tests.
755 DLOG(ERROR) << __FUNCTION__ << me() <<
756 base::StringPrintf("IUrlMoniker::BindToStorage failed 0x%08X.", hr);
757 // In most cases we'll get a MK_E_SYNTAX error here but if we abort
758 // the navigation ourselves such as in the case of seeing something
759 // else than ALLOWALL in X-Frame-Options.
760 }
761 }
762
763 DLOG_IF(ERROR, FAILED(hr)) << me() <<
764 base::StringPrintf(L"StartAsyncDownload failed: 0x%08X", hr);
765
766 return hr;
767 }
768
769 void UrlmonUrlRequest::NotifyDelegateAndDie() {
770 }
771
772 void UrlmonUrlRequest::TerminateTransaction() {
773 if (cleanup_transaction_ && bind_context_ && moniker_) {
774 // We return INET_E_TERMINATED_BIND from our OnDataAvailable implementation
775 // to ensure that the transaction stays around if Chrome decides to issue
776 // a download request when it finishes inspecting the headers received in
777 // OnResponse. However this causes the urlmon transaction object to leak.
778 // To workaround this we save away the IInternetProtocol interface which is
779 // implemented by the urlmon CTransaction object in our BindContextInfo
780 // instance which is maintained per bind context. Invoking Terminate
781 // on this with the special flags 0x2000000 cleanly releases the
782 // transaction.
783 static const int kUrlmonTerminateTransactionFlags = 0x2000000;
784 base::win::ScopedComPtr<BindContextInfo> info;
785 BindContextInfo::FromBindContext(bind_context_, info.Receive());
786 DCHECK(info);
787 if (info && info->protocol()) {
788 info->protocol()->Terminate(kUrlmonTerminateTransactionFlags);
789 }
790 }
791 bind_context_.Release();
792 }
793
794 void UrlmonUrlRequest::ReleaseBindings() {
795 binding_.Release();
796 // Do not release bind_context here!
797 // We may get DownloadToHost request and therefore we want the bind_context
798 // to be available.
799 if (bind_context_)
800 ::RevokeBindStatusCallback(bind_context_, this);
801 }
802
803 net::Error UrlmonUrlRequest::HresultToNetError(HRESULT hr) {
804 const int kInvalidHostName = 0x8007007b;
805 // Useful reference:
806 // http://msdn.microsoft.com/en-us/library/ms775145(VS.85).aspx
807
808 net::Error ret = net::ERR_UNEXPECTED;
809
810 switch (hr) {
811 case S_OK:
812 ret = net::OK;
813 break;
814
815 case MK_E_SYNTAX:
816 ret = net::ERR_INVALID_URL;
817 break;
818
819 case INET_E_CANNOT_CONNECT:
820 ret = net::ERR_CONNECTION_FAILED;
821 break;
822
823 case INET_E_DOWNLOAD_FAILURE:
824 case INET_E_CONNECTION_TIMEOUT:
825 case E_ABORT:
826 ret = net::ERR_CONNECTION_ABORTED;
827 break;
828
829 case INET_E_DATA_NOT_AVAILABLE:
830 ret = net::ERR_EMPTY_RESPONSE;
831 break;
832
833 case INET_E_RESOURCE_NOT_FOUND:
834 // To behave more closely to the chrome network stack, we translate this
835 // error value as tunnel connection failed. This error value is tested
836 // in the ProxyTunnelRedirectTest and UnexpectedServerAuthTest tests.
837 ret = net::ERR_TUNNEL_CONNECTION_FAILED;
838 break;
839
840 // The following error codes can be returned while processing an invalid
841 // url. http://msdn.microsoft.com/en-us/library/bb250493(v=vs.85).aspx
842 case INET_E_INVALID_URL:
843 case INET_E_UNKNOWN_PROTOCOL:
844 case INET_E_REDIRECT_FAILED:
845 case INET_E_SECURITY_PROBLEM:
846 case kInvalidHostName:
847 case E_INVALIDARG:
848 case E_OUTOFMEMORY:
849 ret = net::ERR_INVALID_URL;
850 break;
851
852 case INET_E_INVALID_CERTIFICATE:
853 ret = net::ERR_CERT_INVALID;
854 break;
855
856 case E_ACCESSDENIED:
857 ret = net::ERR_ACCESS_DENIED;
858 break;
859
860 default:
861 DLOG(WARNING)
862 << base::StringPrintf("TODO: translate HRESULT 0x%08X to net::Error",
863 hr);
864 break;
865 }
866 return ret;
867 }
868
869
870 PluginUrlRequestManager::ThreadSafeFlags
871 UrlmonUrlRequestManager::GetThreadSafeFlags() {
872 return PluginUrlRequestManager::NOT_THREADSAFE;
873 }
874
875 void UrlmonUrlRequestManager::SetInfoForUrl(const std::wstring& url,
876 IMoniker* moniker, LPBC bind_ctx) {
877 CComObject<UrlmonUrlRequest>* new_request = NULL;
878 CComObject<UrlmonUrlRequest>::CreateInstance(&new_request);
879 if (new_request) {
880 GURL start_url(url);
881 DCHECK(start_url.is_valid());
882 DCHECK(pending_request_ == NULL);
883
884 base::win::ScopedComPtr<BindContextInfo> info;
885 BindContextInfo::FromBindContext(bind_ctx, info.Receive());
886 DCHECK(info);
887 IStream* cache = info ? info->cache() : NULL;
888 pending_request_ = new_request;
889 pending_request_->InitPending(start_url, moniker, bind_ctx,
890 enable_frame_busting_, privileged_mode_,
891 notification_window_, cache);
892 // Start the request
893 bool is_started = pending_request_->Start();
894 DCHECK(is_started);
895 }
896 }
897
898 void UrlmonUrlRequestManager::BindTerminated(IMoniker* moniker,
899 IBindCtx* bind_ctx,
900 IStream* post_data,
901 const char* request_headers) {
902 }
903
904 scoped_refptr<UrlmonUrlRequest> UrlmonUrlRequestManager::LookupRequest(
905 int request_id, RequestMap* request_map) {
906 RequestMap::iterator it = request_map->find(request_id);
907 if (request_map->end() != it)
908 return it->second;
909 return NULL;
910 }
911
912 UrlmonUrlRequestManager::UrlmonUrlRequestManager()
913 : stopping_(false), notification_window_(NULL),
914 privileged_mode_(false),
915 container_(NULL),
916 background_worker_thread_enabled_(true) {
917 background_thread_.reset(new base::Thread("cf_iexplore_background_thread"));
918 background_thread_->init_com_with_mta(false);
919 background_worker_thread_enabled_ =
920 GetConfigBool(true, kUseBackgroundThreadForSubResources);
921 if (background_worker_thread_enabled_) {
922 base::Thread::Options options;
923 options.message_loop_type = base::MessageLoop::TYPE_UI;
924 background_thread_->StartWithOptions(options);
925 }
926 }
927
928 UrlmonUrlRequestManager::~UrlmonUrlRequestManager() {
929 }
930
931 void UrlmonUrlRequestManager::AddPrivacyDataForUrl(
932 const std::string& url, const std::string& policy_ref,
933 int32 flags) {
934 DCHECK(!url.empty());
935
936 bool fire_privacy_event = false;
937
938 if (privacy_info_.privacy_records.empty())
939 flags |= PRIVACY_URLISTOPLEVEL;
940
941 if (!privacy_info_.privacy_impacted) {
942 if (flags & (COOKIEACTION_ACCEPT | COOKIEACTION_REJECT |
943 COOKIEACTION_DOWNGRADE)) {
944 privacy_info_.privacy_impacted = true;
945 fire_privacy_event = true;
946 }
947 }
948
949 PrivacyInfo::PrivacyEntry& privacy_entry =
950 privacy_info_.privacy_records[base::UTF8ToWide(url)];
951
952 privacy_entry.flags |= flags;
953 privacy_entry.policy_ref = base::UTF8ToWide(policy_ref);
954
955 if (fire_privacy_event && IsWindow(notification_window_)) {
956 PostMessage(notification_window_, WM_FIRE_PRIVACY_CHANGE_NOTIFICATION, 1,
957 0);
958 }
959 }
OLDNEW
« no previous file with comments | « chrome_frame/urlmon_url_request.h ('k') | chrome_frame/urlmon_url_request_private.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698