OLD | NEW |
| (Empty) |
1 // Copyright 2007-2010 Google Inc. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 // ======================================================================== | |
15 | |
16 // | |
17 // Tests Get over http and https, using direct connection and wpad proxy. | |
18 // | |
19 // TODO(omaha): missing Post unit tests | |
20 | |
21 #include <windows.h> | |
22 #include <winhttp.h> | |
23 #include <atlstr.h> | |
24 #include "base/basictypes.h" | |
25 #include "omaha/base/app_util.h" | |
26 #include "omaha/base/const_addresses.h" | |
27 #include "omaha/base/error.h" | |
28 #include "omaha/base/scope_guard.h" | |
29 #include "omaha/base/string.h" | |
30 #include "omaha/net/network_config.h" | |
31 #include "omaha/net/simple_request.h" | |
32 #include "omaha/testing/unit_test.h" | |
33 | |
34 namespace omaha { | |
35 | |
36 const TCHAR* kBigFileUrl = | |
37 _T("http://dl.google.com/dl/edgedl/update2/UpdateData_10M.bin"); | |
38 | |
39 DWORD WINAPI PauseAndResumeThreadProc(void* parameter) { | |
40 SimpleRequest* simple_request = reinterpret_cast<SimpleRequest*>(parameter); | |
41 ASSERT1(simple_request); | |
42 | |
43 bool request_paused = false; | |
44 | |
45 // Loop even times so the download thread won't be blocked by Pause() after | |
46 // loop exit. | |
47 for (int i = 0; i < 10; ++i) { | |
48 ::Sleep(100); | |
49 | |
50 if (request_paused) { | |
51 simple_request->Resume(); | |
52 } else { | |
53 simple_request->Pause(); | |
54 } | |
55 | |
56 request_paused = !request_paused; | |
57 } | |
58 | |
59 return 0; | |
60 } | |
61 | |
62 DWORD WINAPI CancelRequestThreadProc(void* parameter) { | |
63 SimpleRequest* simple_request = reinterpret_cast<SimpleRequest*>(parameter); | |
64 ASSERT1(simple_request); | |
65 | |
66 // Wait a short period of time so the download can start. Assumes the file | |
67 // is large enough so that download will not complete within the sleep time. | |
68 ::Sleep(500); | |
69 | |
70 simple_request->Cancel(); | |
71 return 0; | |
72 } | |
73 | |
74 class SimpleRequestTest : public testing::Test { | |
75 protected: | |
76 SimpleRequestTest() {} | |
77 | |
78 void SimpleGet(const CString& url, const ProxyConfig& config); | |
79 void SimpleDownloadFile(const CString& url, | |
80 const CString& filename, | |
81 const ProxyConfig& config); | |
82 void SimpleDownloadFilePauseAndResume(const CString& url, | |
83 const CString& filename, | |
84 const ProxyConfig& config); | |
85 void SimpleDownloadFileCancellation(const CString& filename, bool do_cancel); | |
86 void SimpleGetHostNotFound(const CString& url, const ProxyConfig& config); | |
87 | |
88 void SimpleGetFileNotFound(const CString& url, const ProxyConfig& config); | |
89 | |
90 void SimpleGetRedirect(const CString& url, const ProxyConfig& config); | |
91 | |
92 void PrepareRequest(const CString& url, | |
93 const ProxyConfig& config, | |
94 SimpleRequest* simple_request); | |
95 }; | |
96 | |
97 void SimpleRequestTest::PrepareRequest(const CString& url, | |
98 const ProxyConfig& config, | |
99 SimpleRequest* simple_request) { | |
100 ASSERT_TRUE(simple_request); | |
101 NetworkConfig* network_config = NULL; | |
102 EXPECT_HRESULT_SUCCEEDED( | |
103 NetworkConfigManager::Instance().GetUserNetworkConfig(&network_config)); | |
104 | |
105 HINTERNET handle = network_config->session().session_handle; | |
106 simple_request->set_session_handle(handle); | |
107 simple_request->set_url(url); | |
108 simple_request->set_proxy_configuration(config); | |
109 | |
110 CString user_agent_header; | |
111 user_agent_header.Format(_T("User-Agent: %s\r\n"), | |
112 simple_request->user_agent()); | |
113 simple_request->set_additional_headers(user_agent_header); | |
114 } | |
115 | |
116 void SimpleRequestTest::SimpleGet(const CString& url, | |
117 const ProxyConfig& config) { | |
118 SimpleRequest simple_request; | |
119 PrepareRequest(url, config, &simple_request); | |
120 EXPECT_HRESULT_SUCCEEDED(simple_request.Send()); | |
121 EXPECT_EQ(HTTP_STATUS_OK, simple_request.GetHttpStatusCode()); | |
122 CString response = Utf8BufferToWideChar(simple_request.GetResponse()); | |
123 | |
124 // robots.txt response contains "User-agent: *". This is not the "User-Agent" | |
125 // http header. | |
126 EXPECT_NE(-1, response.Find(_T("User-agent: *"))); | |
127 CString content_type; | |
128 simple_request.QueryHeadersString(WINHTTP_QUERY_CONTENT_TYPE, | |
129 NULL, &content_type); | |
130 EXPECT_STREQ(_T("text/plain"), content_type); | |
131 | |
132 // Uses custom query for content type and it should match what we just got. | |
133 CString content_type_from_custom_query; | |
134 simple_request.QueryHeadersString(WINHTTP_QUERY_CUSTOM, | |
135 _T("Content-Type"), | |
136 &content_type_from_custom_query); | |
137 EXPECT_STREQ(content_type, content_type_from_custom_query); | |
138 | |
139 EXPECT_FALSE(simple_request.GetResponseHeaders().IsEmpty()); | |
140 | |
141 // Check the user agent went out with the request. | |
142 CString user_agent; | |
143 simple_request.QueryHeadersString( | |
144 WINHTTP_QUERY_FLAG_REQUEST_HEADERS | WINHTTP_QUERY_USER_AGENT, | |
145 WINHTTP_HEADER_NAME_BY_INDEX, | |
146 &user_agent); | |
147 EXPECT_STREQ(simple_request.user_agent(), user_agent); | |
148 } | |
149 | |
150 void SimpleRequestTest::SimpleDownloadFile(const CString& url, | |
151 const CString& filename, | |
152 const ProxyConfig& config) { | |
153 SimpleRequest simple_request; | |
154 PrepareRequest(url, config, &simple_request); | |
155 simple_request.set_filename(filename); | |
156 | |
157 EXPECT_HRESULT_SUCCEEDED(simple_request.Send()); | |
158 | |
159 int http_status = simple_request.GetHttpStatusCode(); | |
160 EXPECT_TRUE(http_status == HTTP_STATUS_OK || | |
161 http_status == HTTP_STATUS_PARTIAL_CONTENT); | |
162 } | |
163 | |
164 void SimpleRequestTest::SimpleDownloadFilePauseAndResume( | |
165 const CString& url, | |
166 const CString& filename, | |
167 const ProxyConfig& config) { | |
168 const int kNumThreads = 3; | |
169 const int kMaxWaitTimeMs = 10000; | |
170 SimpleRequest simple_request; | |
171 PrepareRequest(url, config, &simple_request); | |
172 simple_request.set_filename(filename); | |
173 | |
174 // Testing call Pause before Send. | |
175 simple_request.Pause(); | |
176 simple_request.Resume(); | |
177 HANDLE pause_and_resume_threads[kNumThreads] = { NULL }; | |
178 | |
179 // Now create some threads to run Pause/Resume in the middle of sending. | |
180 for (int i = 0; i < kNumThreads; ++i) { | |
181 pause_and_resume_threads[i] = ::CreateThread(NULL, | |
182 0, | |
183 PauseAndResumeThreadProc, | |
184 &simple_request, | |
185 0, | |
186 NULL); | |
187 EXPECT_TRUE(pause_and_resume_threads[i] != NULL); | |
188 } | |
189 | |
190 EXPECT_HRESULT_SUCCEEDED(simple_request.Send()); | |
191 | |
192 int http_status = simple_request.GetHttpStatusCode(); | |
193 EXPECT_TRUE(http_status == HTTP_STATUS_OK || | |
194 http_status == HTTP_STATUS_PARTIAL_CONTENT); | |
195 | |
196 DWORD result = ::WaitForMultipleObjects(arraysize(pause_and_resume_threads), | |
197 pause_and_resume_threads, | |
198 true, // Wait all threads to exit. | |
199 kMaxWaitTimeMs); | |
200 EXPECT_NE(result, WAIT_FAILED); | |
201 EXPECT_NE(result, WAIT_TIMEOUT); | |
202 | |
203 for (int i = 0; i < kNumThreads; ++i) { | |
204 ::CloseHandle(pause_and_resume_threads[i]); | |
205 } | |
206 } | |
207 | |
208 void SimpleRequestTest::SimpleDownloadFileCancellation( | |
209 const CString& filename, bool do_cancel) { | |
210 SimpleRequest simple_request; | |
211 PrepareRequest(kBigFileUrl, ProxyConfig(), &simple_request); | |
212 simple_request.set_filename(filename); | |
213 | |
214 scoped_handle cancel_thread_handle; | |
215 if (do_cancel) { | |
216 reset(cancel_thread_handle, ::CreateThread(NULL, | |
217 0, | |
218 CancelRequestThreadProc, | |
219 &simple_request, | |
220 0, NULL)); | |
221 } | |
222 | |
223 HRESULT hr = simple_request.Send(); | |
224 if (do_cancel) { | |
225 EXPECT_TRUE(hr == GOOPDATE_E_CANCELLED || SUCCEEDED(hr)); | |
226 ::WaitForSingleObject(get(cancel_thread_handle), INFINITE); | |
227 } else { | |
228 EXPECT_HRESULT_SUCCEEDED(hr); | |
229 } | |
230 } | |
231 | |
232 void SimpleRequestTest::SimpleGetHostNotFound(const CString& url, | |
233 const ProxyConfig& config) { | |
234 SimpleRequest simple_request; | |
235 PrepareRequest(url, config, &simple_request); | |
236 | |
237 HRESULT hr = simple_request.Send(); | |
238 int status_code = simple_request.GetHttpStatusCode(); | |
239 | |
240 if (config.auto_detect) { | |
241 // Either a direct connection or a proxy will be used in the case of | |
242 // proxy auto detect. | |
243 // | |
244 // When a proxy is detected, the proxy server can return some html content | |
245 // indicating an error has occured. The status codes will be different, | |
246 // depending on how each proxy is configured. This may be a flaky unit test. | |
247 if (FAILED(hr)) { | |
248 EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_WINHTTP_NAME_NOT_RESOLVED), hr); | |
249 } else { | |
250 if (String_StartsWith(url, kHttpsProtoScheme, true)) { | |
251 EXPECT_EQ(HTTP_STATUS_NOT_FOUND, status_code); | |
252 } else { | |
253 EXPECT_EQ(HTTP_STATUS_SERVICE_UNAVAIL, status_code); | |
254 } | |
255 } | |
256 } else { | |
257 EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_WINHTTP_NAME_NOT_RESOLVED), hr); | |
258 EXPECT_EQ(0, status_code); | |
259 } | |
260 } | |
261 | |
262 void SimpleRequestTest::SimpleGetFileNotFound(const CString& url, | |
263 const ProxyConfig& config) { | |
264 SimpleRequest simple_request; | |
265 PrepareRequest(url, config, &simple_request); | |
266 EXPECT_HRESULT_SUCCEEDED(simple_request.Send()); | |
267 EXPECT_EQ(HTTP_STATUS_NOT_FOUND, simple_request.GetHttpStatusCode()); | |
268 } | |
269 | |
270 void SimpleRequestTest::SimpleGetRedirect(const CString& url, | |
271 const ProxyConfig& config) { | |
272 SimpleRequest simple_request; | |
273 PrepareRequest(url, config, &simple_request); | |
274 EXPECT_HRESULT_SUCCEEDED(simple_request.Send()); | |
275 | |
276 int http_status = simple_request.GetHttpStatusCode(); | |
277 EXPECT_TRUE(http_status == HTTP_STATUS_OK || | |
278 http_status == HTTP_STATUS_PARTIAL_CONTENT); | |
279 } | |
280 | |
281 // | |
282 // http tests. | |
283 // | |
284 // http get, direct connection. | |
285 TEST_F(SimpleRequestTest, HttpGetDirect) { | |
286 if (IsTestRunByLocalSystem()) { | |
287 return; | |
288 } | |
289 | |
290 SimpleGet(_T("http://www.google.com/robots.txt"), ProxyConfig()); | |
291 } | |
292 | |
293 // http get, direct connection, download to file | |
294 TEST_F(SimpleRequestTest, HttpDownloadDirect) { | |
295 if (IsTestRunByLocalSystem()) { | |
296 return; | |
297 } | |
298 | |
299 CString temp_file; | |
300 EXPECT_TRUE(::GetTempFileName(app_util::GetModuleDirectory(NULL), | |
301 _T("SRT"), | |
302 0, | |
303 CStrBuf(temp_file, MAX_PATH))); | |
304 ScopeGuard guard = MakeGuard(::DeleteFile, temp_file); | |
305 | |
306 SimpleDownloadFile( | |
307 _T("http://dl.google.com/update2/UpdateData.bin"), | |
308 temp_file, ProxyConfig()); | |
309 } | |
310 | |
311 // http get, direct connection, download to file, pause/resume | |
312 // Download same file twice with and without pause/resume. The downloaded | |
313 // files should be same. | |
314 TEST_F(SimpleRequestTest, HttpDownloadDirectPauseAndResume) { | |
315 if (!ShouldRunLargeTest()) { | |
316 return; | |
317 } | |
318 | |
319 // Download the same URL with and without pause/resume. | |
320 CString temp_file1; | |
321 EXPECT_TRUE(::GetTempFileName(app_util::GetModuleDirectory(NULL), | |
322 _T("SRT"), | |
323 0, | |
324 CStrBuf(temp_file1, MAX_PATH))); | |
325 ScopeGuard guard = MakeGuard(::DeleteFile, temp_file1); | |
326 | |
327 SimpleDownloadFilePauseAndResume(kBigFileUrl, temp_file1, ProxyConfig()); | |
328 | |
329 CString temp_file2; | |
330 EXPECT_TRUE(::GetTempFileName(app_util::GetModuleDirectory(NULL), | |
331 _T("SRT"), | |
332 0, | |
333 CStrBuf(temp_file2, MAX_PATH))); | |
334 ScopeGuard guard2 = MakeGuard(::DeleteFile, temp_file2); | |
335 | |
336 SimpleDownloadFile(kBigFileUrl, temp_file2, ProxyConfig()); | |
337 | |
338 // Compares that the downloaded files are equal | |
339 scoped_hfile file_handle1(::CreateFile(temp_file1, GENERIC_READ, | |
340 FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)); | |
341 EXPECT_NE(get(file_handle1), INVALID_HANDLE_VALUE); | |
342 | |
343 scoped_hfile file_handle2(::CreateFile(temp_file2, GENERIC_READ, | |
344 FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)); | |
345 | |
346 // Compare file size | |
347 DWORD file_size = GetFileSize(get(file_handle1), NULL); | |
348 EXPECT_EQ(file_size, GetFileSize(get(file_handle2), NULL)); | |
349 | |
350 // Compare file contents | |
351 const int kBufferLength = 1024; | |
352 BYTE buffer1[kBufferLength] = {0}; | |
353 BYTE buffer2[kBufferLength] = {0}; | |
354 | |
355 for (int i = static_cast<int>(file_size); i > 0; i -= kBufferLength) { | |
356 int bytes_to_read = std::min(kBufferLength, i); | |
357 DWORD num_bytes_got = 0; | |
358 | |
359 EXPECT_TRUE(ReadFile(get(file_handle1), buffer1, | |
360 bytes_to_read, &num_bytes_got, NULL)); | |
361 EXPECT_EQ(num_bytes_got, static_cast<DWORD>(bytes_to_read)); | |
362 EXPECT_TRUE(ReadFile(get(file_handle2), buffer2, | |
363 bytes_to_read, &num_bytes_got, NULL)); | |
364 EXPECT_EQ(num_bytes_got, static_cast<DWORD>(bytes_to_read)); | |
365 EXPECT_EQ(memcmp(buffer1, buffer2, bytes_to_read), 0); | |
366 } | |
367 | |
368 // Make sure that file handles are closed first so that the delete guards | |
369 // can delete the temp files. | |
370 reset(file_handle1); | |
371 reset(file_handle2); | |
372 } | |
373 | |
374 // http get, direct connection, negative test. | |
375 TEST_F(SimpleRequestTest, HttpGetDirectHostNotFound) { | |
376 if (IsTestRunByLocalSystem()) { | |
377 return; | |
378 } | |
379 | |
380 SimpleGetHostNotFound(_T("http://no_such_host.google.com/"), ProxyConfig()); | |
381 } | |
382 | |
383 // http get, direct connection, negative test. | |
384 TEST_F(SimpleRequestTest, HttpGetDirectFileNotFound) { | |
385 if (IsTestRunByLocalSystem()) { | |
386 return; | |
387 } | |
388 | |
389 SimpleGetFileNotFound(_T("http://tools.google.com/no_such_file"), | |
390 ProxyConfig()); | |
391 } | |
392 | |
393 // http get, proxy wpad. | |
394 TEST_F(SimpleRequestTest, HttpGetProxy) { | |
395 ProxyConfig config; | |
396 config.auto_detect = true; | |
397 SimpleGet(_T("http://www.google.com/robots.txt"), config); | |
398 } | |
399 | |
400 // http get, proxy wpad, negative test. | |
401 TEST_F(SimpleRequestTest, HttpGetProxyHostNotFound) { | |
402 ProxyConfig config; | |
403 config.auto_detect = true; | |
404 SimpleGetHostNotFound(_T("http://no_such_host.google.com/"), config); | |
405 } | |
406 | |
407 // http get, proxy wpad. | |
408 TEST_F(SimpleRequestTest, HttpGetProxyFileNotFound) { | |
409 ProxyConfig config; | |
410 config.auto_detect = true; | |
411 SimpleGetFileNotFound(_T("http://tools.google.com/no_such_file"), config); | |
412 } | |
413 | |
414 | |
415 // | |
416 // https tests. | |
417 // | |
418 // https get, direct. | |
419 TEST_F(SimpleRequestTest, HttpsGetDirect) { | |
420 if (IsTestRunByLocalSystem()) { | |
421 return; | |
422 } | |
423 | |
424 SimpleGet(_T("https://www.google.com/robots.txt"), ProxyConfig()); | |
425 } | |
426 | |
427 // https get, direct, negative test. | |
428 TEST_F(SimpleRequestTest, HttpsGetDirectHostNotFound) { | |
429 SimpleGetHostNotFound(_T("https://no_such_host.google.com/"), ProxyConfig()); | |
430 } | |
431 | |
432 // https get, direct connection, negative test. | |
433 TEST_F(SimpleRequestTest, HttpsGetDirectFileNotFound) { | |
434 if (IsTestRunByLocalSystem()) { | |
435 return; | |
436 } | |
437 | |
438 SimpleGetFileNotFound(_T("https://tools.google.com/no_such_file"), | |
439 ProxyConfig()); | |
440 } | |
441 | |
442 // https get, proxy wpad. | |
443 TEST_F(SimpleRequestTest, HttpsGetProxy) { | |
444 ProxyConfig config; | |
445 config.auto_detect = true; | |
446 SimpleGet(_T("https://www.google.com/robots.txt"), config); | |
447 } | |
448 | |
449 // https get, proxy wpad, negative test. | |
450 TEST_F(SimpleRequestTest, HttpsGetProxyHostNotFound) { | |
451 ProxyConfig config; | |
452 config.auto_detect = true; | |
453 SimpleGetHostNotFound(_T("https://no_such_host.google.com/"), config); | |
454 } | |
455 | |
456 // https get, proxy wpad, negative test. | |
457 TEST_F(SimpleRequestTest, HttpsGetProxyFileNotFound) { | |
458 ProxyConfig config; | |
459 config.auto_detect = true; | |
460 SimpleGetFileNotFound(_T("https://tools.google.com/no_such_file"), config); | |
461 } | |
462 | |
463 // Should not be able to reuse the object once canceled, even if closed. | |
464 TEST_F(SimpleRequestTest, Cancel_CannotReuse) { | |
465 SimpleRequest simple_request; | |
466 PrepareRequest(_T("http:\\foo\\"), ProxyConfig(), &simple_request); | |
467 EXPECT_HRESULT_SUCCEEDED(simple_request.Cancel()); | |
468 EXPECT_EQ(GOOPDATE_E_CANCELLED, simple_request.Send()); | |
469 EXPECT_HRESULT_SUCCEEDED(simple_request.Close()); | |
470 EXPECT_EQ(GOOPDATE_E_CANCELLED, simple_request.Send()); | |
471 } | |
472 | |
473 TEST_F(SimpleRequestTest, Cancel_ShouldDeleteTempFile) { | |
474 CString temp_file; | |
475 EXPECT_TRUE(::GetTempFileName(app_util::GetModuleDirectory(NULL), | |
476 _T("SRT"), | |
477 0, | |
478 CStrBuf(temp_file, MAX_PATH))); | |
479 ScopeGuard guard = MakeGuard(::DeleteFile, temp_file); | |
480 | |
481 // Verify that cancellation should remove partially downloaded file. | |
482 bool do_cancel = true; | |
483 SimpleDownloadFileCancellation(temp_file, do_cancel); | |
484 EXPECT_EQ(INVALID_FILE_ATTRIBUTES, ::GetFileAttributes(temp_file)); | |
485 EXPECT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError()); | |
486 | |
487 if (ShouldRunLargeTest()) { | |
488 // Verify that target file is preserved if download is not cancelled. | |
489 do_cancel = false; | |
490 SimpleDownloadFileCancellation(temp_file, do_cancel); | |
491 EXPECT_NE(INVALID_FILE_ATTRIBUTES, ::GetFileAttributes(temp_file)); | |
492 } | |
493 } | |
494 | |
495 // Http get request should follow redirects. The url below redirects to | |
496 // https://tools.google.com/service/update2/oneclick and then it returns | |
497 // 200 OK and some xml body. | |
498 // TODO(omaha): Pick a new URL since this service is now obsolete and could | |
499 // be removed at some point. | |
500 TEST_F(SimpleRequestTest, HttpGet_Redirect) { | |
501 if (IsTestRunByLocalSystem()) { | |
502 return; | |
503 } | |
504 | |
505 SimpleGetRedirect(_T("http://tools.google.com/service/update2/oneclick"), | |
506 ProxyConfig()); | |
507 } | |
508 | |
509 } // namespace omaha | |
510 | |
OLD | NEW |