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 <atlbase.h>
6 #include <atlcom.h>
7
8 #include "base/strings/string16.h"
9 #include "base/strings/string_util.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "base/win/scoped_bstr.h"
12 #include "base/win/scoped_comptr.h"
13 #include "chrome_frame/html_utils.h"
14 #include "chrome_frame/http_negotiate.h"
15 #include "chrome_frame/registry_list_preferences_holder.h"
16 #include "chrome_frame/test/chrome_frame_test_utils.h"
17 #include "chrome_frame/utils.h"
18 #include "gmock/gmock.h"
19 #include "gtest/gtest.h"
20
21 class HttpNegotiateTest : public testing::Test {
22 protected:
HttpNegotiateTest()23 HttpNegotiateTest() {
24 }
25 };
26
27 class TestHttpNegotiate
28 : public CComObjectRootEx<CComMultiThreadModel>,
29 public IHttpNegotiate {
30 public:
TestHttpNegotiate()31 TestHttpNegotiate()
32 : beginning_transaction_ret_(S_OK), additional_headers_(NULL) {
33 }
34
35 BEGIN_COM_MAP(TestHttpNegotiate)
COM_INTERFACE_ENTRY(IHttpNegotiate)36 COM_INTERFACE_ENTRY(IHttpNegotiate)
37 END_COM_MAP()
38 STDMETHOD(BeginningTransaction)(LPCWSTR url, LPCWSTR headers, // NOLINT
39 DWORD reserved, // NOLINT
40 LPWSTR* additional_headers) { // NOLINT
41 if (additional_headers_) {
42 int len = lstrlenW(additional_headers_);
43 len++;
44 *additional_headers = reinterpret_cast<wchar_t*>(
45 ::CoTaskMemAlloc(len * sizeof(wchar_t)));
46 lstrcpyW(*additional_headers, additional_headers_);
47 }
48 return beginning_transaction_ret_;
49 }
50
STDMETHOD(OnResponse)51 STDMETHOD(OnResponse)(DWORD response_code, LPCWSTR response_header,
52 LPCWSTR request_header,
53 LPWSTR* additional_request_headers) {
54 return S_OK;
55 }
56
57 HRESULT beginning_transaction_ret_;
58 const wchar_t* additional_headers_;
59 };
60
TEST_F(HttpNegotiateTest,BeginningTransaction)61 TEST_F(HttpNegotiateTest, BeginningTransaction) {
62 static const int kBeginningTransactionIndex = 3;
63 CComObjectStackEx<TestHttpNegotiate> test_http;
64 IHttpNegotiate_BeginningTransaction_Fn original =
65 reinterpret_cast<IHttpNegotiate_BeginningTransaction_Fn>(
66 (*reinterpret_cast<void***>(
67 static_cast<IHttpNegotiate*>(
68 &test_http)))[kBeginningTransactionIndex]);
69
70 string16 cf_ua(
71 ASCIIToWide(http_utils::GetDefaultUserAgentHeaderWithCFTag()));
72 string16 cf_tag(
73 ASCIIToWide(http_utils::GetChromeFrameUserAgent()));
74
75 EXPECT_NE(string16::npos, cf_ua.find(L"chromeframe/"));
76
77 struct TestCase {
78 const string16 original_headers_;
79 const string16 delegate_additional_;
80 const string16 expected_additional_;
81 HRESULT delegate_return_value_;
82 } test_cases[] = {
83 { L"Accept: */*\r\n",
84 L"",
85 cf_ua + L"\r\n",
86 S_OK },
87 { L"Accept: */*\r\n",
88 L"",
89 L"",
90 E_OUTOFMEMORY },
91 { L"",
92 L"Accept: */*\r\n",
93 L"Accept: */*\r\n" + cf_ua + L"\r\n",
94 S_OK },
95 { L"User-Agent: Bingo/1.0\r\n",
96 L"",
97 L"User-Agent: Bingo/1.0 " + cf_tag + L"\r\n",
98 S_OK },
99 { L"User-Agent: NotMe/1.0\r\n",
100 L"User-Agent: MeMeMe/1.0\r\n",
101 L"User-Agent: MeMeMe/1.0 " + cf_tag + L"\r\n",
102 S_OK },
103 { L"",
104 L"User-Agent: MeMeMe/1.0\r\n",
105 L"User-Agent: MeMeMe/1.0 " + cf_tag + L"\r\n",
106 S_OK },
107 };
108
109 for (int i = 0; i < arraysize(test_cases); ++i) {
110 TestCase& test = test_cases[i];
111 wchar_t* additional = NULL;
112 test_http.beginning_transaction_ret_ = test.delegate_return_value_;
113 test_http.additional_headers_ = test.delegate_additional_.c_str();
114 HttpNegotiatePatch::BeginningTransaction(original, &test_http,
115 L"http://www.google.com", test.original_headers_.c_str(), 0,
116 &additional);
117 EXPECT_TRUE(additional != NULL);
118
119 if (additional) {
120 // Check against the expected additional headers.
121 EXPECT_EQ(test.expected_additional_, string16(additional));
122 ::CoTaskMemFree(additional);
123 }
124 }
125 }
126
TEST_F(HttpNegotiateTest,BeginningTransactionUARemoval)127 TEST_F(HttpNegotiateTest, BeginningTransactionUARemoval) {
128 static const int kBeginningTransactionIndex = 3;
129 CComObjectStackEx<TestHttpNegotiate> test_http;
130 IHttpNegotiate_BeginningTransaction_Fn original =
131 reinterpret_cast<IHttpNegotiate_BeginningTransaction_Fn>(
132 (*reinterpret_cast<void***>(
133 static_cast<IHttpNegotiate*>(
134 &test_http)))[kBeginningTransactionIndex]);
135
136 string16 nocf_ua(
137 ASCIIToWide(http_utils::RemoveChromeFrameFromUserAgentValue(
138 http_utils::GetDefaultUserAgentHeaderWithCFTag())));
139 string16 cf_ua(
140 ASCIIToWide(http_utils::AddChromeFrameToUserAgentValue(
141 WideToASCII(nocf_ua))));
142
143 EXPECT_EQ(string16::npos, nocf_ua.find(L"chromeframe/"));
144 EXPECT_NE(string16::npos, cf_ua.find(L"chromeframe/"));
145
146 string16 ua_url(L"www.withua.com");
147 string16 no_ua_url(L"www.noua.com");
148
149 RegistryListPreferencesHolder& ua_holder =
150 GetUserAgentPreferencesHolderForTesting();
151 ua_holder.AddStringForTesting(no_ua_url);
152
153 struct TestCase {
154 const string16 url_;
155 const string16 original_headers_;
156 const string16 delegate_additional_;
157 const string16 expected_additional_;
158 } test_cases[] = {
159 { ua_url,
160 L"",
161 L"Accept: */*\r\n" + cf_ua + L"\r\n",
162 L"Accept: */*\r\n" + cf_ua + L"\r\n" },
163 { ua_url,
164 L"",
165 L"Accept: */*\r\n" + nocf_ua + L"\r\n",
166 L"Accept: */*\r\n" + cf_ua + L"\r\n" },
167 { no_ua_url,
168 L"",
169 L"Accept: */*\r\n" + cf_ua + L"\r\n",
170 L"Accept: */*\r\n" + nocf_ua + L"\r\n" },
171 { no_ua_url,
172 L"",
173 L"Accept: */*\r\n" + nocf_ua + L"\r\n",
174 L"Accept: */*\r\n" + nocf_ua + L"\r\n" },
175 };
176
177 for (int i = 0; i < arraysize(test_cases); ++i) {
178 TestCase& test = test_cases[i];
179 wchar_t* additional = NULL;
180 test_http.beginning_transaction_ret_ = S_OK;
181 test_http.additional_headers_ = test.delegate_additional_.c_str();
182 HttpNegotiatePatch::BeginningTransaction(original, &test_http,
183 test.url_.c_str(), test.original_headers_.c_str(), 0,
184 &additional);
185 EXPECT_TRUE(additional != NULL);
186
187 if (additional) {
188 // Check against the expected additional headers.
189 EXPECT_EQ(test.expected_additional_, string16(additional))
190 << "Iteration: " << i;
191 ::CoTaskMemFree(additional);
192 }
193 }
194 }
195
196
197 class TestInternetProtocolSink
198 : public CComObjectRootEx<CComMultiThreadModel>,
199 public IInternetProtocolSink {
200 public:
TestInternetProtocolSink()201 TestInternetProtocolSink() : status_(0) {
202 // Create an instance of IE to fullfill the requirements of being able
203 // to detect whether a sub-frame or top-frame is being loaded (see
204 // IsSubFrameRequest) and to be able to mark an IBrowserService
205 // implementation as a target for CF navigation.
206 HRESULT hr = browser_.CreateInstance(CLSID_InternetExplorer);
207 CHECK(SUCCEEDED(hr));
208 if (SUCCEEDED(hr)) {
209 browser_->Navigate(base::win::ScopedBstr(L"about:blank"),
210 NULL, NULL, NULL, NULL);
211 }
212 }
213
~TestInternetProtocolSink()214 ~TestInternetProtocolSink() {
215 if (browser_)
216 browser_->Quit();
217 }
218
219 BEGIN_COM_MAP(TestInternetProtocolSink)
COM_INTERFACE_ENTRY(IInternetProtocolSink)220 COM_INTERFACE_ENTRY(IInternetProtocolSink)
221 COM_INTERFACE_ENTRY_AGGREGATE(IID_IServiceProvider, browser_)
222 END_COM_MAP()
223
224 // IInternetProtocolSink.
225 STDMETHOD(Switch)(PROTOCOLDATA* data) {
226 NOTREACHED();
227 return S_OK;
228 }
229
STDMETHOD(ReportProgress)230 STDMETHOD(ReportProgress)(ULONG status, LPCWSTR text) {
231 status_ = status;
232 status_text_ = text ? text : L"";
233 return S_OK;
234 }
235
STDMETHOD(ReportData)236 STDMETHOD(ReportData)(DWORD bscf, ULONG progress, ULONG progress_max) {
237 NOTREACHED();
238 return S_OK;
239 }
240
STDMETHOD(ReportResult)241 STDMETHOD(ReportResult)(HRESULT hr, DWORD err, LPCWSTR result) {
242 NOTREACHED();
243 return S_OK;
244 }
245
last_status() const246 ULONG last_status() const {
247 return status_;
248 }
249
last_status_text() const250 const string16& last_status_text() const {
251 return status_text_;
252 }
253
254 protected:
255 ULONG status_;
256 string16 status_text_;
257 base::win::ScopedComPtr<IWebBrowser2> browser_;
258 };
259
260 using testing::AllOf;
261 using testing::ContainsRegex;
262 using testing::HasSubstr;
263
TEST(AppendUserAgent,Append)264 TEST(AppendUserAgent, Append) {
265 EXPECT_THAT(AppendCFUserAgentString(NULL, NULL),
266 testing::ContainsRegex("User-Agent:.+chromeframe.+\r\n"));
267
268 // Check Http headers are reasonably parsed.
269 EXPECT_THAT(AppendCFUserAgentString(L"Bad User-Agent: Age Tuners;\r\n", NULL),
270 AllOf(ContainsRegex("User-Agent:.+chromeframe.+\r\n"),
271 testing::Not(testing::HasSubstr("Age Tuners"))));
272
273 // Honor headers User-Agent, if additional headers does not specify one.
274 EXPECT_THAT(AppendCFUserAgentString(L"User-Agent: A Tense Rug;\r\n", NULL),
275 ContainsRegex("User-Agent: A Tense Rug; chromeframe.+\r\n"));
276
277 // Honor additional headers User-Agent.
278 EXPECT_THAT(AppendCFUserAgentString(L"User-Agent: Near Guest;\r\n",
279 L"User-Agent: Rat see Gun;\r\n"),
280 ContainsRegex("User-Agent: Rat see Gun; chromeframe.+\r\n"));
281
282 // Check additional headers are preserved.
283 EXPECT_THAT(AppendCFUserAgentString(NULL,
284 L"Authorization: A Zoo That I Ruin\r\n"
285 L"User-Agent: Get a Nurse;\r\n"
286 L"Accept-Language: Cleanup a Cat Egg\r\n"),
287 AllOf(ContainsRegex("User-Agent: Get a Nurse; chromeframe.+\r\n"),
288 HasSubstr("Authorization: A Zoo That I Ruin\r\n"),
289 HasSubstr("Accept-Language: Cleanup a Cat Egg\r\n")));
290 }
291