Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(437)

Side by Side Diff: net/cert/cert_net_fetcher_unittest.cc

Issue 908863004: Initial implementation for CertNetFetcher. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: add a missing header Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698