OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 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 "net/cert/cert_net_fetcher.h" | |
6 | |
7 #include <string> | |
8 | |
9 #include "base/compiler_specific.h" | |
10 #include "net/cert/mock_cert_verifier.h" | |
11 #include "net/dns/mock_host_resolver.h" | |
12 #include "net/http/http_server_properties_impl.h" | |
13 #include "net/test/spawned_test_server/spawned_test_server.h" | |
14 #include "net/url_request/url_request_job_factory_impl.h" | |
15 #include "net/url_request/url_request_test_util.h" | |
16 #include "testing/gtest/include/gtest/gtest.h" | |
17 #include "testing/platform_test.h" | |
18 | |
19 // TODO(eroman): Test that cookies aren't sent. | |
20 // TODO(eroman): Request de-duplication | |
21 // TODO(eroman): Cancel duplicated requests within a callback | |
22 // TODO(eroman): Start requests for the same job within a callback | |
23 // TODO(eroman): Delete the CertNetFetcher within callback | |
24 | |
25 using base::ASCIIToUTF16; | |
26 | |
27 namespace net { | |
28 | |
29 namespace { | |
30 | |
31 const base::FilePath::CharType kDocRoot[] = | |
32 FILE_PATH_LITERAL("net/data/cert_net_fetcher_unittest"); | |
33 | |
34 // A non-mock URL request which can access http:// urls. | |
35 class RequestContext : public URLRequestContext { | |
36 public: | |
37 RequestContext() : storage_(this) { | |
38 ProxyConfig no_proxy; | |
39 storage_.set_host_resolver(scoped_ptr<HostResolver>(new MockHostResolver)); | |
40 storage_.set_cert_verifier(new MockCertVerifier); | |
41 storage_.set_transport_security_state(new TransportSecurityState); | |
42 storage_.set_proxy_service(ProxyService::CreateFixed(no_proxy)); | |
43 storage_.set_ssl_config_service(new SSLConfigServiceDefaults); | |
44 storage_.set_http_server_properties( | |
45 scoped_ptr<HttpServerProperties>(new HttpServerPropertiesImpl())); | |
46 | |
47 HttpNetworkSession::Params params; | |
48 params.host_resolver = host_resolver(); | |
49 params.cert_verifier = cert_verifier(); | |
50 params.transport_security_state = transport_security_state(); | |
51 params.proxy_service = proxy_service(); | |
52 params.ssl_config_service = ssl_config_service(); | |
53 params.http_server_properties = http_server_properties(); | |
54 scoped_refptr<HttpNetworkSession> network_session( | |
55 new HttpNetworkSession(params)); | |
56 storage_.set_http_transaction_factory(new HttpCache( | |
57 network_session.get(), HttpCache::DefaultBackend::InMemory(0))); | |
58 URLRequestJobFactoryImpl* job_factory = new URLRequestJobFactoryImpl(); | |
59 storage_.set_job_factory(job_factory); | |
60 } | |
61 | |
62 ~RequestContext() override { AssertNoURLRequests(); } | |
63 | |
64 private: | |
65 URLRequestContextStorage storage_; | |
66 }; | |
67 | |
68 struct FetchResult { | |
69 std::string GetBodyAsString() const { | |
70 return std::string(response_body.begin(), response_body.end()); | |
71 } | |
72 | |
73 int net_error; | |
74 std::vector<uint8_t> response_body; | |
75 }; | |
76 | |
77 // Helper to synchronously wait on the callback to be called. | |
78 // Similar to TestCompletionCallback but built around FetchCallback. | |
79 class TestFetchCallback { | |
80 public: | |
81 TestFetchCallback() | |
82 : callback_( | |
83 base::Bind(&TestFetchCallback::OnCallback, base::Unretained(this))), | |
84 have_result_(false), | |
85 waiting_for_result_(false) {} | |
86 | |
87 const CertNetFetcher::FetchCallback& callback() const { return callback_; } | |
88 | |
89 FetchResult WaitForResult() { | |
90 DCHECK(!waiting_for_result_); | |
91 while (!have_result_) { | |
92 waiting_for_result_ = true; | |
93 base::MessageLoop::current()->Run(); | |
mattm
2015/02/10 05:22:14
use base::RunLoop?
eroman
2015/02/11 03:51:52
I had copied this code from TestCompletionCallback
eroman
2015/02/13 22:11:38
Done.
| |
94 waiting_for_result_ = false; | |
95 } | |
96 have_result_ = false; // Auto-reset for next callback. | |
97 return result_; | |
98 } | |
99 | |
100 bool HasResult() const { return have_result_; } | |
101 | |
102 private: | |
103 void OnCallback(int net_error, const std::vector<uint8_t>& response_body) { | |
104 DCHECK(!have_result_); | |
105 have_result_ = true; | |
106 result_.net_error = net_error; | |
107 result_.response_body = response_body; | |
108 if (waiting_for_result_) | |
109 base::MessageLoop::current()->Quit(); | |
110 } | |
111 | |
112 CertNetFetcher::FetchCallback callback_; | |
113 FetchResult result_; | |
114 bool have_result_; | |
115 bool waiting_for_result_; | |
116 }; | |
117 | |
118 } // namespace | |
119 | |
120 class CertNetFetcherTest : public PlatformTest { | |
121 public: | |
122 CertNetFetcherTest() | |
123 : test_server_(SpawnedTestServer::TYPE_HTTP, | |
124 net::SpawnedTestServer::kLocalhost, | |
125 base::FilePath(kDocRoot)) {} | |
126 | |
127 protected: | |
128 SpawnedTestServer test_server_; | |
129 RequestContext context_; | |
130 }; | |
131 | |
132 scoped_ptr<CertNetFetcher::RequestParams> CreateSimpleRequest(const GURL& url) { | |
133 scoped_ptr<CertNetFetcher::RequestParams> request_params( | |
134 new CertNetFetcher::RequestParams( | |
135 url, CertNetFetcher::REQUEST_TYPE_CA_ISSUERS)); | |
136 return request_params; | |
137 } | |
138 | |
139 // Fetch a few unique URLs using GET in parallel. Each URL has different content | |
140 // and Content-Type. | |
141 TEST_F(CertNetFetcherTest, ParallelFetchNoDupes) { | |
142 ASSERT_TRUE(test_server_.Start()); | |
143 | |
144 CertNetFetcher fetcher(&context_); | |
145 TestFetchCallback callback1; | |
146 TestFetchCallback callback2; | |
147 TestFetchCallback callback3; | |
148 | |
149 // Request a URL with Content-Type "application/pkix-cert" | |
150 GURL url1 = test_server_.GetURL("files/cert.crt"); | |
151 fetcher.Fetch(CreateSimpleRequest(url1), callback1.callback()); | |
152 | |
153 // Request a URL with Content-Type "application/pkix-crl" | |
154 GURL url2 = test_server_.GetURL("files/root.crl"); | |
155 fetcher.Fetch(CreateSimpleRequest(url2), callback2.callback()); | |
156 | |
157 // Request a URL with Content-Type "application/pkcs7-mime" | |
158 GURL url3 = test_server_.GetURL("files/certs.p7c"); | |
159 fetcher.Fetch(CreateSimpleRequest(url3), callback3.callback()); | |
160 | |
161 // Wait for all of the requests to complete. | |
162 FetchResult result1 = callback1.WaitForResult(); | |
163 FetchResult result2 = callback2.WaitForResult(); | |
164 FetchResult result3 = callback3.WaitForResult(); | |
165 | |
166 // Verify the fetch results. | |
167 EXPECT_EQ(OK, result1.net_error); | |
168 EXPECT_EQ("-cert.crt-\n", result1.GetBodyAsString()); | |
169 EXPECT_EQ(OK, result2.net_error); | |
170 EXPECT_EQ("-root.crl-\n", result2.GetBodyAsString()); | |
171 EXPECT_EQ(OK, result3.net_error); | |
172 EXPECT_EQ("-certs.p7c-\n", result3.GetBodyAsString()); | |
173 } | |
174 | |
175 // Fetch a caIssuers URL which has an unexpected extension and Content-Type. | |
176 // The extension is .txt and the Content-Type is text/plain. Despite being | |
177 // unusual this succeeds as the extension and Content-Type are not required to | |
178 // be meaningful. | |
179 TEST_F(CertNetFetcherTest, ContentTypeDoesntMatter) { | |
180 ASSERT_TRUE(test_server_.Start()); | |
181 | |
182 CertNetFetcher fetcher(&context_); | |
183 | |
184 TestFetchCallback callback; | |
185 GURL url = test_server_.GetURL("files/foo.txt"); | |
186 fetcher.Fetch(CreateSimpleRequest(url), callback.callback()); | |
187 FetchResult result = callback.WaitForResult(); | |
188 EXPECT_EQ(OK, result.net_error); | |
189 EXPECT_EQ("-foo.txt-\n", result.GetBodyAsString()); | |
190 } | |
191 | |
192 // Fetch a URLs whose HTTP response code is not 200. These are considered | |
193 // failures. | |
194 TEST_F(CertNetFetcherTest, HttpStatusCode) { | |
195 ASSERT_TRUE(test_server_.Start()); | |
196 | |
197 CertNetFetcher fetcher(&context_); | |
198 | |
199 // Response with a 404. | |
200 { | |
201 TestFetchCallback callback; | |
202 GURL url = test_server_.GetURL("files/404.html"); | |
203 fetcher.Fetch(CreateSimpleRequest(url), callback.callback()); | |
204 FetchResult result = callback.WaitForResult(); | |
205 EXPECT_EQ(ERR_FAILED, result.net_error); | |
206 EXPECT_TRUE(result.response_body.empty()); | |
207 } | |
208 | |
209 // Response with a 500. | |
210 { | |
211 TestFetchCallback callback; | |
212 GURL url = test_server_.GetURL("files/500.html"); | |
213 fetcher.Fetch(CreateSimpleRequest(url), callback.callback()); | |
214 FetchResult result = callback.WaitForResult(); | |
215 EXPECT_EQ(ERR_FAILED, result.net_error); | |
216 EXPECT_TRUE(result.response_body.empty()); | |
217 } | |
218 } | |
219 | |
220 // Fetching a URL with a Content-Disposition header should have no effect. | |
221 TEST_F(CertNetFetcherTest, ContentDisposition) { | |
222 ASSERT_TRUE(test_server_.Start()); | |
223 | |
224 CertNetFetcher fetcher(&context_); | |
225 | |
226 TestFetchCallback callback; | |
227 GURL url = test_server_.GetURL("files/downloadable.js"); | |
228 fetcher.Fetch(CreateSimpleRequest(url), callback.callback()); | |
229 FetchResult result = callback.WaitForResult(); | |
230 EXPECT_EQ(OK, result.net_error); | |
231 EXPECT_EQ("-downloadable.js-\n", result.GetBodyAsString()); | |
232 } | |
233 | |
234 // Verifies that a cachable request will be served from the HTTP cache the | |
235 // second time it is requested. | |
236 TEST_F(CertNetFetcherTest, Cache) { | |
237 ASSERT_TRUE(test_server_.Start()); | |
238 | |
239 CertNetFetcher fetcher(&context_); | |
240 | |
241 // Fetch a URL whose HTTP headers make it cacheable for 1 hour. | |
242 GURL url(test_server_.GetURL("files/cacheable_1hr.crt")); | |
243 { | |
244 TestFetchCallback callback; | |
245 fetcher.Fetch(CreateSimpleRequest(url), callback.callback()); | |
246 FetchResult result = callback.WaitForResult(); | |
247 EXPECT_EQ(OK, result.net_error); | |
248 EXPECT_EQ("-cacheable_1hr.crt-\n", result.GetBodyAsString()); | |
249 } | |
250 | |
251 // Kill the HTTP server. | |
252 ASSERT_TRUE(test_server_.Stop()); | |
253 | |
254 // Fetch again -- will fail unless served from cache. | |
255 { | |
256 TestFetchCallback callback; | |
257 fetcher.Fetch(CreateSimpleRequest(url), callback.callback()); | |
258 FetchResult result = callback.WaitForResult(); | |
259 EXPECT_EQ(OK, result.net_error); | |
260 EXPECT_EQ("-cacheable_1hr.crt-\n", result.GetBodyAsString()); | |
261 } | |
262 } | |
263 | |
264 // Verify that the maximum response body constraints are enforced by fetching a | |
265 // resource that is larger than the limit. | |
266 TEST_F(CertNetFetcherTest, TooLarge) { | |
267 ASSERT_TRUE(test_server_.Start()); | |
268 | |
269 CertNetFetcher fetcher(&context_); | |
270 | |
271 // This file has a response body 12 bytes long. So setting the maximum to 11 | |
272 // bytes will cause it to fail. | |
273 GURL url(test_server_.GetURL("files/certs.p7c")); | |
274 scoped_ptr<CertNetFetcher::RequestParams> params(CreateSimpleRequest(url)); | |
275 params->max_response_size_in_bytes = 11; | |
276 | |
277 TestFetchCallback callback; | |
278 fetcher.Fetch(params.Pass(), callback.callback()); | |
279 FetchResult result = callback.WaitForResult(); | |
280 EXPECT_EQ(ERR_FILE_TOO_BIG, result.net_error); | |
281 EXPECT_TRUE(result.response_body.empty()); | |
282 } | |
283 | |
284 // Set the timeout to 10 milliseconds, and try fetching a URL that takes 5 | |
285 // seconds to complete. It should fail due to a timeout. | |
286 TEST_F(CertNetFetcherTest, Hang) { | |
287 ASSERT_TRUE(test_server_.Start()); | |
288 | |
289 CertNetFetcher fetcher(&context_); | |
290 | |
291 GURL url(test_server_.GetURL("slow/certs.p7c?5.1")); | |
292 scoped_ptr<CertNetFetcher::RequestParams> params(CreateSimpleRequest(url)); | |
293 params->timeout = base::TimeDelta::FromMilliseconds(10); | |
294 | |
295 TestFetchCallback callback; | |
296 fetcher.Fetch(params.Pass(), callback.callback()); | |
297 FetchResult result = callback.WaitForResult(); | |
298 EXPECT_EQ(ERR_TIMED_OUT, result.net_error); | |
299 EXPECT_TRUE(result.response_body.empty()); | |
300 } | |
301 | |
302 // Verify that if a response is gzip-encoded it gets inflated before being | |
303 // returned to the caller. | |
304 TEST_F(CertNetFetcherTest, Gzip) { | |
305 ASSERT_TRUE(test_server_.Start()); | |
306 | |
307 CertNetFetcher fetcher(&context_); | |
308 | |
309 GURL url(test_server_.GetURL("files/gzipped_crl")); | |
310 TestFetchCallback callback; | |
311 fetcher.Fetch(CreateSimpleRequest(url), callback.callback()); | |
312 FetchResult result = callback.WaitForResult(); | |
313 EXPECT_EQ(OK, result.net_error); | |
314 EXPECT_EQ("-gzipped_crl-\n", result.GetBodyAsString()); | |
315 } | |
316 | |
317 // Try fetching an unsupported URL scheme (https). | |
318 TEST_F(CertNetFetcherTest, HttpsNotAllowed) { | |
319 ASSERT_TRUE(test_server_.Start()); | |
320 | |
321 CertNetFetcher fetcher(&context_); | |
322 | |
323 GURL url("https://foopy/foo.crt"); | |
324 TestFetchCallback callback; | |
325 fetcher.Fetch(CreateSimpleRequest(url), callback.callback()); | |
326 // Should NOT complete synchronously despite being a test that could be done | |
327 // immediately. | |
328 EXPECT_FALSE(callback.HasResult()); | |
329 FetchResult result = callback.WaitForResult(); | |
330 EXPECT_EQ(ERR_DISALLOWED_URL_SCHEME, result.net_error); | |
331 EXPECT_TRUE(result.response_body.empty()); | |
332 } | |
333 | |
334 // Try fetching a URL which redirects to https. | |
335 TEST_F(CertNetFetcherTest, RedirectToHttpsNotAllowed) { | |
336 ASSERT_TRUE(test_server_.Start()); | |
337 | |
338 CertNetFetcher fetcher(&context_); | |
339 | |
340 GURL url(test_server_.GetURL("files/redirect_https")); | |
341 TestFetchCallback callback; | |
342 fetcher.Fetch(CreateSimpleRequest(url), callback.callback()); | |
343 FetchResult result = callback.WaitForResult(); | |
344 EXPECT_EQ(ERR_DISALLOWED_URL_SCHEME, result.net_error); | |
345 EXPECT_TRUE(result.response_body.empty()); | |
346 } | |
347 | |
348 // Try fetching an unsupported URL scheme (https) and then immediately | |
349 // cancelling. This is a bit special because this codepath needs to post a task. | |
350 TEST_F(CertNetFetcherTest, CancelHttpsNotAllowed) { | |
351 ASSERT_TRUE(test_server_.Start()); | |
352 | |
353 CertNetFetcher fetcher(&context_); | |
354 | |
355 GURL url("https://foopy/foo.crt"); | |
356 TestFetchCallback callback; | |
357 CertNetFetcher::RequestId id = | |
358 fetcher.Fetch(CreateSimpleRequest(url), callback.callback()); | |
359 | |
360 // Should NOT complete synchronously despite being a test that could be done | |
361 // immediately. | |
362 EXPECT_FALSE(callback.HasResult()); | |
363 | |
364 fetcher.CancelRequest(id); | |
365 } | |
366 | |
367 // Start a few requests, and cancel one of them before running the message loop | |
368 // again. | |
369 TEST_F(CertNetFetcherTest, CancelBeforeRunningMessageLoop) { | |
370 ASSERT_TRUE(test_server_.Start()); | |
371 | |
372 CertNetFetcher fetcher(&context_); | |
373 TestFetchCallback callback1; | |
374 TestFetchCallback callback2; | |
375 TestFetchCallback callback3; | |
376 | |
377 // Request a URL with Content-Type "application/pkix-cert" | |
378 GURL url1 = test_server_.GetURL("files/cert.crt"); | |
379 fetcher.Fetch(CreateSimpleRequest(url1), callback1.callback()); | |
380 | |
381 // Request a URL with Content-Type "application/pkix-crl" | |
382 GURL url2 = test_server_.GetURL("files/root.crl"); | |
383 CertNetFetcher::RequestId id2 = | |
384 fetcher.Fetch(CreateSimpleRequest(url2), callback2.callback()); | |
385 | |
386 // Request a URL with Content-Type "application/pkcs7-mime" | |
387 GURL url3 = test_server_.GetURL("files/certs.p7c"); | |
388 fetcher.Fetch(CreateSimpleRequest(url3), callback3.callback()); | |
389 | |
390 EXPECT_FALSE(callback1.HasResult()); | |
391 EXPECT_FALSE(callback2.HasResult()); | |
392 EXPECT_FALSE(callback3.HasResult()); | |
393 | |
394 // Cancel the second request. | |
395 fetcher.CancelRequest(id2); | |
396 | |
397 // Wait for the non-cancelled requests to complete. | |
398 FetchResult result1 = callback1.WaitForResult(); | |
399 FetchResult result3 = callback3.WaitForResult(); | |
400 | |
401 // Verify the fetch results. | |
402 EXPECT_EQ(OK, result1.net_error); | |
403 EXPECT_EQ("-cert.crt-\n", result1.GetBodyAsString()); | |
404 EXPECT_EQ(OK, result3.net_error); | |
405 EXPECT_EQ("-certs.p7c-\n", result3.GetBodyAsString()); | |
406 | |
407 EXPECT_FALSE(callback2.HasResult()); | |
408 } | |
409 | |
410 // Start several requests, and cancel one of them after the first has completed. | |
411 TEST_F(CertNetFetcherTest, CancelSlowRequest) { | |
412 ASSERT_TRUE(test_server_.Start()); | |
413 | |
414 CertNetFetcher fetcher(&context_); | |
415 TestFetchCallback callback1; | |
416 TestFetchCallback callback2; | |
417 TestFetchCallback callback3; | |
418 TestFetchCallback callback4; | |
419 | |
420 // Request a URL with Content-Type "application/pkix-cert" | |
421 GURL url1 = test_server_.GetURL("files/cert.crt"); | |
422 fetcher.Fetch(CreateSimpleRequest(url1), callback1.callback()); | |
423 | |
424 // This request will take 10 seconds to complete. | |
425 GURL url2 = test_server_.GetURL("slow/certs.p7c?10.1"); | |
426 CertNetFetcher::RequestId id2 = | |
427 fetcher.Fetch(CreateSimpleRequest(url2), callback2.callback()); | |
428 | |
429 // Request a URL with Content-Type "application/pkcs7-mime" | |
430 GURL url3 = test_server_.GetURL("files/certs.p7c"); | |
431 fetcher.Fetch(CreateSimpleRequest(url3), callback3.callback()); | |
432 | |
433 // Request a URL with Content-Type "application/pkcs7-mime" | |
434 GURL url4("ftp://www.not.supported.com/foo"); | |
435 fetcher.Fetch(CreateSimpleRequest(url4), callback4.callback()); | |
436 | |
437 EXPECT_FALSE(callback1.HasResult()); | |
438 EXPECT_FALSE(callback2.HasResult()); | |
439 EXPECT_FALSE(callback3.HasResult()); | |
440 EXPECT_FALSE(callback4.HasResult()); | |
441 | |
442 // Wait for the fast request to complete. | |
443 FetchResult result4 = callback4.WaitForResult(); | |
444 EXPECT_EQ(ERR_DISALLOWED_URL_SCHEME, result4.net_error); | |
445 EXPECT_TRUE(result4.response_body.empty()); | |
446 | |
447 // Cancel the second request. | |
448 fetcher.CancelRequest(id2); | |
449 | |
450 // Wait for the other requests to complete. | |
451 FetchResult result1 = callback1.WaitForResult(); | |
452 FetchResult result3 = callback3.WaitForResult(); | |
453 | |
454 // Verify the fetch results. | |
455 EXPECT_EQ(OK, result1.net_error); | |
456 EXPECT_EQ("-cert.crt-\n", result1.GetBodyAsString()); | |
457 EXPECT_EQ(OK, result3.net_error); | |
458 EXPECT_EQ("-certs.p7c-\n", result3.GetBodyAsString()); | |
459 | |
460 EXPECT_FALSE(callback2.HasResult()); | |
461 } | |
462 | |
463 // Delete a CertNetFetcher with outstanding requests on it. | |
464 TEST_F(CertNetFetcherTest, DeleteCancels) { | |
465 ASSERT_TRUE(test_server_.Start()); | |
466 | |
467 CertNetFetcher fetcher(&context_); | |
468 | |
469 GURL url(test_server_.GetURL("slow/certs.p7c?20.1")); | |
470 TestFetchCallback callback; | |
471 fetcher.Fetch(CreateSimpleRequest(url), callback.callback()); | |
472 | |
473 // Note that the request is never completed, nor cancelled. | |
474 } | |
475 | |
476 } // namespace net | |
OLD | NEW |