| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <atlbase.h> |
| #include <atlcom.h> |
| |
| #include "base/strings/string16.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/win/scoped_bstr.h" |
| #include "base/win/scoped_comptr.h" |
| #include "chrome_frame/html_utils.h" |
| #include "chrome_frame/http_negotiate.h" |
| #include "chrome_frame/registry_list_preferences_holder.h" |
| #include "chrome_frame/test/chrome_frame_test_utils.h" |
| #include "chrome_frame/utils.h" |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| |
| class HttpNegotiateTest : public testing::Test { |
| protected: |
| HttpNegotiateTest() { |
| } |
| }; |
| |
| class TestHttpNegotiate |
| : public CComObjectRootEx<CComMultiThreadModel>, |
| public IHttpNegotiate { |
| public: |
| TestHttpNegotiate() |
| : beginning_transaction_ret_(S_OK), additional_headers_(NULL) { |
| } |
| |
| BEGIN_COM_MAP(TestHttpNegotiate) |
| COM_INTERFACE_ENTRY(IHttpNegotiate) |
| END_COM_MAP() |
| STDMETHOD(BeginningTransaction)(LPCWSTR url, LPCWSTR headers, // NOLINT |
| DWORD reserved, // NOLINT |
| LPWSTR* additional_headers) { // NOLINT |
| if (additional_headers_) { |
| int len = lstrlenW(additional_headers_); |
| len++; |
| *additional_headers = reinterpret_cast<wchar_t*>( |
| ::CoTaskMemAlloc(len * sizeof(wchar_t))); |
| lstrcpyW(*additional_headers, additional_headers_); |
| } |
| return beginning_transaction_ret_; |
| } |
| |
| STDMETHOD(OnResponse)(DWORD response_code, LPCWSTR response_header, |
| LPCWSTR request_header, |
| LPWSTR* additional_request_headers) { |
| return S_OK; |
| } |
| |
| HRESULT beginning_transaction_ret_; |
| const wchar_t* additional_headers_; |
| }; |
| |
| TEST_F(HttpNegotiateTest, BeginningTransaction) { |
| static const int kBeginningTransactionIndex = 3; |
| CComObjectStackEx<TestHttpNegotiate> test_http; |
| IHttpNegotiate_BeginningTransaction_Fn original = |
| reinterpret_cast<IHttpNegotiate_BeginningTransaction_Fn>( |
| (*reinterpret_cast<void***>( |
| static_cast<IHttpNegotiate*>( |
| &test_http)))[kBeginningTransactionIndex]); |
| |
| string16 cf_ua( |
| ASCIIToWide(http_utils::GetDefaultUserAgentHeaderWithCFTag())); |
| string16 cf_tag( |
| ASCIIToWide(http_utils::GetChromeFrameUserAgent())); |
| |
| EXPECT_NE(string16::npos, cf_ua.find(L"chromeframe/")); |
| |
| struct TestCase { |
| const string16 original_headers_; |
| const string16 delegate_additional_; |
| const string16 expected_additional_; |
| HRESULT delegate_return_value_; |
| } test_cases[] = { |
| { L"Accept: */*\r\n", |
| L"", |
| cf_ua + L"\r\n", |
| S_OK }, |
| { L"Accept: */*\r\n", |
| L"", |
| L"", |
| E_OUTOFMEMORY }, |
| { L"", |
| L"Accept: */*\r\n", |
| L"Accept: */*\r\n" + cf_ua + L"\r\n", |
| S_OK }, |
| { L"User-Agent: Bingo/1.0\r\n", |
| L"", |
| L"User-Agent: Bingo/1.0 " + cf_tag + L"\r\n", |
| S_OK }, |
| { L"User-Agent: NotMe/1.0\r\n", |
| L"User-Agent: MeMeMe/1.0\r\n", |
| L"User-Agent: MeMeMe/1.0 " + cf_tag + L"\r\n", |
| S_OK }, |
| { L"", |
| L"User-Agent: MeMeMe/1.0\r\n", |
| L"User-Agent: MeMeMe/1.0 " + cf_tag + L"\r\n", |
| S_OK }, |
| }; |
| |
| for (int i = 0; i < arraysize(test_cases); ++i) { |
| TestCase& test = test_cases[i]; |
| wchar_t* additional = NULL; |
| test_http.beginning_transaction_ret_ = test.delegate_return_value_; |
| test_http.additional_headers_ = test.delegate_additional_.c_str(); |
| HttpNegotiatePatch::BeginningTransaction(original, &test_http, |
| L"http://www.google.com", test.original_headers_.c_str(), 0, |
| &additional); |
| EXPECT_TRUE(additional != NULL); |
| |
| if (additional) { |
| // Check against the expected additional headers. |
| EXPECT_EQ(test.expected_additional_, string16(additional)); |
| ::CoTaskMemFree(additional); |
| } |
| } |
| } |
| |
| TEST_F(HttpNegotiateTest, BeginningTransactionUARemoval) { |
| static const int kBeginningTransactionIndex = 3; |
| CComObjectStackEx<TestHttpNegotiate> test_http; |
| IHttpNegotiate_BeginningTransaction_Fn original = |
| reinterpret_cast<IHttpNegotiate_BeginningTransaction_Fn>( |
| (*reinterpret_cast<void***>( |
| static_cast<IHttpNegotiate*>( |
| &test_http)))[kBeginningTransactionIndex]); |
| |
| string16 nocf_ua( |
| ASCIIToWide(http_utils::RemoveChromeFrameFromUserAgentValue( |
| http_utils::GetDefaultUserAgentHeaderWithCFTag()))); |
| string16 cf_ua( |
| ASCIIToWide(http_utils::AddChromeFrameToUserAgentValue( |
| WideToASCII(nocf_ua)))); |
| |
| EXPECT_EQ(string16::npos, nocf_ua.find(L"chromeframe/")); |
| EXPECT_NE(string16::npos, cf_ua.find(L"chromeframe/")); |
| |
| string16 ua_url(L"www.withua.com"); |
| string16 no_ua_url(L"www.noua.com"); |
| |
| RegistryListPreferencesHolder& ua_holder = |
| GetUserAgentPreferencesHolderForTesting(); |
| ua_holder.AddStringForTesting(no_ua_url); |
| |
| struct TestCase { |
| const string16 url_; |
| const string16 original_headers_; |
| const string16 delegate_additional_; |
| const string16 expected_additional_; |
| } test_cases[] = { |
| { ua_url, |
| L"", |
| L"Accept: */*\r\n" + cf_ua + L"\r\n", |
| L"Accept: */*\r\n" + cf_ua + L"\r\n" }, |
| { ua_url, |
| L"", |
| L"Accept: */*\r\n" + nocf_ua + L"\r\n", |
| L"Accept: */*\r\n" + cf_ua + L"\r\n" }, |
| { no_ua_url, |
| L"", |
| L"Accept: */*\r\n" + cf_ua + L"\r\n", |
| L"Accept: */*\r\n" + nocf_ua + L"\r\n" }, |
| { no_ua_url, |
| L"", |
| L"Accept: */*\r\n" + nocf_ua + L"\r\n", |
| L"Accept: */*\r\n" + nocf_ua + L"\r\n" }, |
| }; |
| |
| for (int i = 0; i < arraysize(test_cases); ++i) { |
| TestCase& test = test_cases[i]; |
| wchar_t* additional = NULL; |
| test_http.beginning_transaction_ret_ = S_OK; |
| test_http.additional_headers_ = test.delegate_additional_.c_str(); |
| HttpNegotiatePatch::BeginningTransaction(original, &test_http, |
| test.url_.c_str(), test.original_headers_.c_str(), 0, |
| &additional); |
| EXPECT_TRUE(additional != NULL); |
| |
| if (additional) { |
| // Check against the expected additional headers. |
| EXPECT_EQ(test.expected_additional_, string16(additional)) |
| << "Iteration: " << i; |
| ::CoTaskMemFree(additional); |
| } |
| } |
| } |
| |
| |
| class TestInternetProtocolSink |
| : public CComObjectRootEx<CComMultiThreadModel>, |
| public IInternetProtocolSink { |
| public: |
| TestInternetProtocolSink() : status_(0) { |
| // Create an instance of IE to fullfill the requirements of being able |
| // to detect whether a sub-frame or top-frame is being loaded (see |
| // IsSubFrameRequest) and to be able to mark an IBrowserService |
| // implementation as a target for CF navigation. |
| HRESULT hr = browser_.CreateInstance(CLSID_InternetExplorer); |
| CHECK(SUCCEEDED(hr)); |
| if (SUCCEEDED(hr)) { |
| browser_->Navigate(base::win::ScopedBstr(L"about:blank"), |
| NULL, NULL, NULL, NULL); |
| } |
| } |
| |
| ~TestInternetProtocolSink() { |
| if (browser_) |
| browser_->Quit(); |
| } |
| |
| BEGIN_COM_MAP(TestInternetProtocolSink) |
| COM_INTERFACE_ENTRY(IInternetProtocolSink) |
| COM_INTERFACE_ENTRY_AGGREGATE(IID_IServiceProvider, browser_) |
| END_COM_MAP() |
| |
| // IInternetProtocolSink. |
| STDMETHOD(Switch)(PROTOCOLDATA* data) { |
| NOTREACHED(); |
| return S_OK; |
| } |
| |
| STDMETHOD(ReportProgress)(ULONG status, LPCWSTR text) { |
| status_ = status; |
| status_text_ = text ? text : L""; |
| return S_OK; |
| } |
| |
| STDMETHOD(ReportData)(DWORD bscf, ULONG progress, ULONG progress_max) { |
| NOTREACHED(); |
| return S_OK; |
| } |
| |
| STDMETHOD(ReportResult)(HRESULT hr, DWORD err, LPCWSTR result) { |
| NOTREACHED(); |
| return S_OK; |
| } |
| |
| ULONG last_status() const { |
| return status_; |
| } |
| |
| const string16& last_status_text() const { |
| return status_text_; |
| } |
| |
| protected: |
| ULONG status_; |
| string16 status_text_; |
| base::win::ScopedComPtr<IWebBrowser2> browser_; |
| }; |
| |
| using testing::AllOf; |
| using testing::ContainsRegex; |
| using testing::HasSubstr; |
| |
| TEST(AppendUserAgent, Append) { |
| EXPECT_THAT(AppendCFUserAgentString(NULL, NULL), |
| testing::ContainsRegex("User-Agent:.+chromeframe.+\r\n")); |
| |
| // Check Http headers are reasonably parsed. |
| EXPECT_THAT(AppendCFUserAgentString(L"Bad User-Agent: Age Tuners;\r\n", NULL), |
| AllOf(ContainsRegex("User-Agent:.+chromeframe.+\r\n"), |
| testing::Not(testing::HasSubstr("Age Tuners")))); |
| |
| // Honor headers User-Agent, if additional headers does not specify one. |
| EXPECT_THAT(AppendCFUserAgentString(L"User-Agent: A Tense Rug;\r\n", NULL), |
| ContainsRegex("User-Agent: A Tense Rug; chromeframe.+\r\n")); |
| |
| // Honor additional headers User-Agent. |
| EXPECT_THAT(AppendCFUserAgentString(L"User-Agent: Near Guest;\r\n", |
| L"User-Agent: Rat see Gun;\r\n"), |
| ContainsRegex("User-Agent: Rat see Gun; chromeframe.+\r\n")); |
| |
| // Check additional headers are preserved. |
| EXPECT_THAT(AppendCFUserAgentString(NULL, |
| L"Authorization: A Zoo That I Ruin\r\n" |
| L"User-Agent: Get a Nurse;\r\n" |
| L"Accept-Language: Cleanup a Cat Egg\r\n"), |
| AllOf(ContainsRegex("User-Agent: Get a Nurse; chromeframe.+\r\n"), |
| HasSubstr("Authorization: A Zoo That I Ruin\r\n"), |
| HasSubstr("Accept-Language: Cleanup a Cat Egg\r\n"))); |
| } |