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