Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / net / cert_net / cert_net_fetcher_impl_unittest.cc
blob0c2ee356879ca69cd15697531a4d4796b661c4b0
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.
5 #include "net/cert_net/cert_net_fetcher_impl.h"
7 #include <string>
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"
20 // TODO(eroman): Test that cookies aren't sent.
22 using base::ASCIIToUTF16;
24 namespace net {
26 namespace {
28 const base::FilePath::CharType kDocRoot[] =
29 FILE_PATH_LITERAL("net/data/cert_net_fetcher_impl_unittest");
31 // A non-mock URLRequestContext which can access http:// urls.
32 class RequestContext : public URLRequestContext {
33 public:
34 RequestContext() : storage_(this) {
35 ProxyConfig no_proxy;
36 storage_.set_host_resolver(scoped_ptr<HostResolver>(new MockHostResolver));
37 storage_.set_cert_verifier(make_scoped_ptr(new MockCertVerifier).Pass());
38 storage_.set_transport_security_state(
39 make_scoped_ptr(new TransportSecurityState));
40 storage_.set_proxy_service(ProxyService::CreateFixed(no_proxy));
41 storage_.set_ssl_config_service(new SSLConfigServiceDefaults);
42 storage_.set_http_server_properties(
43 scoped_ptr<HttpServerProperties>(new HttpServerPropertiesImpl()));
45 HttpNetworkSession::Params params;
46 params.host_resolver = host_resolver();
47 params.cert_verifier = cert_verifier();
48 params.transport_security_state = transport_security_state();
49 params.proxy_service = proxy_service();
50 params.ssl_config_service = ssl_config_service();
51 params.http_server_properties = http_server_properties();
52 scoped_refptr<HttpNetworkSession> network_session(
53 new HttpNetworkSession(params));
54 storage_.set_http_transaction_factory(
55 make_scoped_ptr(new HttpCache(network_session.get(),
56 HttpCache::DefaultBackend::InMemory(0)))
57 .Pass());
58 storage_.set_job_factory(
59 make_scoped_ptr(new URLRequestJobFactoryImpl()).Pass());
62 ~RequestContext() override { AssertNoURLRequests(); }
64 private:
65 URLRequestContextStorage storage_;
68 class FetchResult {
69 public:
70 FetchResult(Error net_error, const std::vector<uint8_t>& response_body)
71 : net_error_(net_error), response_body_(response_body) {}
73 void VerifySuccess(const std::string& expected_body) {
74 EXPECT_EQ(OK, net_error_);
75 EXPECT_EQ(expected_body,
76 std::string(response_body_.begin(), response_body_.end()));
79 void VerifyFailure(Error expected_error) {
80 EXPECT_EQ(expected_error, net_error_);
81 EXPECT_EQ(0u, response_body_.size());
84 private:
85 const Error net_error_;
86 const std::vector<uint8_t> response_body_;
89 // Helper to synchronously wait for the fetch completion. This is similar to
90 // net's TestCompletionCallback, but built around FetchCallback.
91 class TestFetchCallback {
92 public:
93 TestFetchCallback()
94 : callback_(base::Bind(&TestFetchCallback::OnCallback,
95 base::Unretained(this))) {}
97 const CertNetFetcher::FetchCallback& callback() const { return callback_; }
99 scoped_ptr<FetchResult> WaitForResult() {
100 DCHECK(quit_closure_.is_null());
101 while (!HasResult()) {
102 base::RunLoop run_loop;
103 quit_closure_ = run_loop.QuitClosure();
104 run_loop.Run();
105 quit_closure_.Reset();
107 return result_.Pass();
110 bool HasResult() const { return result_.get(); }
112 // Sets an extra action (in addition to recording the result) that is run when
113 // the FetchCallback is invoked.
114 void set_extra_closure(const base::Closure& closure) {
115 extra_closure_ = closure;
118 private:
119 void OnCallback(Error net_error, const std::vector<uint8_t>& response_body) {
120 DCHECK(!HasResult());
121 result_.reset(new FetchResult(net_error, response_body));
123 if (!extra_closure_.is_null())
124 extra_closure_.Run();
126 if (!quit_closure_.is_null())
127 quit_closure_.Run();
130 CertNetFetcher::FetchCallback callback_;
131 scoped_ptr<FetchResult> result_;
132 base::Closure quit_closure_;
133 base::Closure extra_closure_;
136 } // namespace
138 class CertNetFetcherImplTest : public PlatformTest {
139 public:
140 CertNetFetcherImplTest()
141 : test_server_(SpawnedTestServer::TYPE_HTTP,
142 SpawnedTestServer::kLocalhost,
143 base::FilePath(kDocRoot)) {
144 context_.set_network_delegate(&network_delegate_);
147 protected:
148 SpawnedTestServer test_server_;
149 TestNetworkDelegate network_delegate_;
150 RequestContext context_;
153 // Helper to start an AIA fetch using default parameters.
154 WARN_UNUSED_RESULT scoped_ptr<CertNetFetcher::Request> StartRequest(
155 CertNetFetcher* fetcher,
156 const GURL& url,
157 const TestFetchCallback& callback) {
158 return fetcher->FetchCaIssuers(url, CertNetFetcher::DEFAULT,
159 CertNetFetcher::DEFAULT, callback.callback());
162 // Fetch a few unique URLs using GET in parallel. Each URL has a different body
163 // and Content-Type.
164 TEST_F(CertNetFetcherImplTest, ParallelFetchNoDuplicates) {
165 ASSERT_TRUE(test_server_.Start());
167 CertNetFetcherImpl fetcher(&context_);
168 TestFetchCallback callback1;
169 TestFetchCallback callback2;
170 TestFetchCallback callback3;
172 // Request a URL with Content-Type "application/pkix-cert"
173 GURL url1 = test_server_.GetURL("files/cert.crt");
174 scoped_ptr<CertNetFetcher::Request> request1 =
175 StartRequest(&fetcher, url1, callback1);
177 // Request a URL with Content-Type "application/pkix-crl"
178 GURL url2 = test_server_.GetURL("files/root.crl");
179 scoped_ptr<CertNetFetcher::Request> request2 =
180 StartRequest(&fetcher, url2, callback2);
182 // Request a URL with Content-Type "application/pkcs7-mime"
183 GURL url3 = test_server_.GetURL("files/certs.p7c");
184 scoped_ptr<CertNetFetcher::Request> request3 =
185 StartRequest(&fetcher, url3, callback3);
187 // Wait for all of the requests to complete.
188 scoped_ptr<FetchResult> result1 = callback1.WaitForResult();
189 scoped_ptr<FetchResult> result2 = callback2.WaitForResult();
190 scoped_ptr<FetchResult> result3 = callback3.WaitForResult();
192 // Verify the fetch results.
193 result1->VerifySuccess("-cert.crt-\n");
194 result2->VerifySuccess("-root.crl-\n");
195 result3->VerifySuccess("-certs.p7c-\n");
197 EXPECT_EQ(3, network_delegate_.created_requests());
200 // Fetch a caIssuers URL which has an unexpected extension and Content-Type.
201 // The extension is .txt and the Content-Type is text/plain. Despite being
202 // unusual this succeeds as the extension and Content-Type are not required to
203 // be meaningful.
204 TEST_F(CertNetFetcherImplTest, ContentTypeDoesntMatter) {
205 ASSERT_TRUE(test_server_.Start());
207 CertNetFetcherImpl fetcher(&context_);
209 TestFetchCallback callback;
210 GURL url = test_server_.GetURL("files/foo.txt");
211 scoped_ptr<CertNetFetcher::Request> request =
212 StartRequest(&fetcher, url, callback);
213 scoped_ptr<FetchResult> result = callback.WaitForResult();
214 result->VerifySuccess("-foo.txt-\n");
217 // Fetch a URLs whose HTTP response code is not 200. These are considered
218 // failures.
219 TEST_F(CertNetFetcherImplTest, HttpStatusCode) {
220 ASSERT_TRUE(test_server_.Start());
222 CertNetFetcherImpl fetcher(&context_);
224 // Response was HTTP status 404.
226 TestFetchCallback callback;
227 GURL url = test_server_.GetURL("files/404.html");
228 scoped_ptr<CertNetFetcher::Request> request =
229 StartRequest(&fetcher, url, callback);
230 scoped_ptr<FetchResult> result = callback.WaitForResult();
231 result->VerifyFailure(ERR_FAILED);
234 // Response was HTTP status 500.
236 TestFetchCallback callback;
237 GURL url = test_server_.GetURL("files/500.html");
238 scoped_ptr<CertNetFetcher::Request> request =
239 StartRequest(&fetcher, url, callback);
240 scoped_ptr<FetchResult> result = callback.WaitForResult();
241 result->VerifyFailure(ERR_FAILED);
245 // Fetching a URL with a Content-Disposition header should have no effect.
246 TEST_F(CertNetFetcherImplTest, ContentDisposition) {
247 ASSERT_TRUE(test_server_.Start());
249 CertNetFetcherImpl fetcher(&context_);
251 TestFetchCallback callback;
252 GURL url = test_server_.GetURL("files/downloadable.js");
253 scoped_ptr<CertNetFetcher::Request> request =
254 StartRequest(&fetcher, url, callback);
255 scoped_ptr<FetchResult> result = callback.WaitForResult();
256 result->VerifySuccess("-downloadable.js-\n");
259 // Verifies that a cachable request will be served from the HTTP cache the
260 // second time it is requested.
261 TEST_F(CertNetFetcherImplTest, Cache) {
262 ASSERT_TRUE(test_server_.Start());
264 CertNetFetcherImpl fetcher(&context_);
266 // Fetch a URL whose HTTP headers make it cacheable for 1 hour.
267 GURL url(test_server_.GetURL("files/cacheable_1hr.crt"));
269 TestFetchCallback callback;
271 scoped_ptr<CertNetFetcher::Request> request =
272 StartRequest(&fetcher, url, callback);
273 scoped_ptr<FetchResult> result = callback.WaitForResult();
274 result->VerifySuccess("-cacheable_1hr.crt-\n");
277 EXPECT_EQ(1, network_delegate_.created_requests());
279 // Kill the HTTP server.
280 ASSERT_TRUE(test_server_.Stop());
282 // Fetch again -- will fail unless served from cache.
284 TestFetchCallback callback;
285 scoped_ptr<CertNetFetcher::Request> request =
286 StartRequest(&fetcher, url, callback);
287 scoped_ptr<FetchResult> result = callback.WaitForResult();
288 result->VerifySuccess("-cacheable_1hr.crt-\n");
291 EXPECT_EQ(2, network_delegate_.created_requests());
294 // Verify that the maximum response body constraints are enforced by fetching a
295 // resource that is larger than the limit.
296 TEST_F(CertNetFetcherImplTest, TooLarge) {
297 ASSERT_TRUE(test_server_.Start());
299 CertNetFetcherImpl fetcher(&context_);
301 // This file has a response body 12 bytes long. So setting the maximum to 11
302 // bytes will cause it to fail.
303 GURL url(test_server_.GetURL("files/certs.p7c"));
304 TestFetchCallback callback;
305 scoped_ptr<CertNetFetcher::Request> request = fetcher.FetchCaIssuers(
306 url, CertNetFetcher::DEFAULT, 11, callback.callback());
308 scoped_ptr<FetchResult> result = callback.WaitForResult();
309 result->VerifyFailure(ERR_FILE_TOO_BIG);
312 // Set the timeout to 10 milliseconds, and try fetching a URL that takes 5
313 // seconds to complete. It should fail due to a timeout.
314 TEST_F(CertNetFetcherImplTest, Hang) {
315 ASSERT_TRUE(test_server_.Start());
317 CertNetFetcherImpl fetcher(&context_);
319 GURL url(test_server_.GetURL("slow/certs.p7c?5"));
320 TestFetchCallback callback;
321 scoped_ptr<CertNetFetcher::Request> request = fetcher.FetchCaIssuers(
322 url, 10, CertNetFetcher::DEFAULT, callback.callback());
323 scoped_ptr<FetchResult> result = callback.WaitForResult();
324 result->VerifyFailure(ERR_TIMED_OUT);
327 // Verify that if a response is gzip-encoded it gets inflated before being
328 // returned to the caller.
329 TEST_F(CertNetFetcherImplTest, Gzip) {
330 ASSERT_TRUE(test_server_.Start());
332 CertNetFetcherImpl fetcher(&context_);
334 GURL url(test_server_.GetURL("files/gzipped_crl"));
335 TestFetchCallback callback;
336 scoped_ptr<CertNetFetcher::Request> request =
337 StartRequest(&fetcher, url, callback);
338 scoped_ptr<FetchResult> result = callback.WaitForResult();
339 result->VerifySuccess("-gzipped_crl-\n");
342 // Try fetching an unsupported URL scheme (https).
343 TEST_F(CertNetFetcherImplTest, HttpsNotAllowed) {
344 ASSERT_TRUE(test_server_.Start());
346 CertNetFetcherImpl fetcher(&context_);
348 GURL url("https://foopy/foo.crt");
349 TestFetchCallback callback;
350 scoped_ptr<CertNetFetcher::Request> request =
351 StartRequest(&fetcher, url, callback);
352 // Should NOT complete synchronously despite being a test that could be done
353 // immediately.
354 EXPECT_FALSE(callback.HasResult());
355 scoped_ptr<FetchResult> result = callback.WaitForResult();
356 result->VerifyFailure(ERR_DISALLOWED_URL_SCHEME);
358 // No request was created because the URL scheme was unsupported.
359 EXPECT_EQ(0, network_delegate_.created_requests());
362 // Try fetching a URL which redirects to https.
363 TEST_F(CertNetFetcherImplTest, RedirectToHttpsNotAllowed) {
364 ASSERT_TRUE(test_server_.Start());
366 CertNetFetcherImpl fetcher(&context_);
368 GURL url(test_server_.GetURL("files/redirect_https"));
369 TestFetchCallback callback;
371 scoped_ptr<CertNetFetcher::Request> request =
372 StartRequest(&fetcher, url, callback);
373 scoped_ptr<FetchResult> result = callback.WaitForResult();
374 result->VerifyFailure(ERR_DISALLOWED_URL_SCHEME);
376 EXPECT_EQ(1, network_delegate_.created_requests());
379 // Try fetching an unsupported URL scheme (https) and then immediately
380 // cancelling. This is a bit special because this codepath needs to post a task.
381 TEST_F(CertNetFetcherImplTest, CancelHttpsNotAllowed) {
382 ASSERT_TRUE(test_server_.Start());
384 CertNetFetcherImpl fetcher(&context_);
386 GURL url("https://foopy/foo.crt");
387 TestFetchCallback callback;
388 scoped_ptr<CertNetFetcher::Request> request =
389 StartRequest(&fetcher, url, callback);
391 // Cancel the request.
392 request.reset();
394 // Spin the message loop to increase chance of catching a bug.
395 base::RunLoop().RunUntilIdle();
397 // Should NOT complete synchronously despite being a test that could be done
398 // immediately.
399 EXPECT_FALSE(callback.HasResult());
401 EXPECT_EQ(0, network_delegate_.created_requests());
404 // Start a few requests, and cancel one of them before running the message loop
405 // again.
406 TEST_F(CertNetFetcherImplTest, CancelBeforeRunningMessageLoop) {
407 ASSERT_TRUE(test_server_.Start());
409 CertNetFetcherImpl fetcher(&context_);
410 TestFetchCallback callback1;
411 TestFetchCallback callback2;
412 TestFetchCallback callback3;
414 GURL url1 = test_server_.GetURL("files/cert.crt");
415 scoped_ptr<CertNetFetcher::Request> request1 =
416 StartRequest(&fetcher, url1, callback1);
418 GURL url2 = test_server_.GetURL("files/root.crl");
419 scoped_ptr<CertNetFetcher::Request> request2 =
420 StartRequest(&fetcher, url2, callback2);
422 GURL url3 = test_server_.GetURL("files/certs.p7c");
424 scoped_ptr<CertNetFetcher::Request> request3 =
425 StartRequest(&fetcher, url3, callback3);
427 EXPECT_EQ(3, network_delegate_.created_requests());
428 EXPECT_FALSE(callback1.HasResult());
429 EXPECT_FALSE(callback2.HasResult());
430 EXPECT_FALSE(callback3.HasResult());
432 // Cancel the second request.
433 request2.reset();
435 // Wait for the non-cancelled requests to complete.
436 scoped_ptr<FetchResult> result1 = callback1.WaitForResult();
437 scoped_ptr<FetchResult> result3 = callback3.WaitForResult();
439 // Verify the fetch results.
440 result1->VerifySuccess("-cert.crt-\n");
441 result3->VerifySuccess("-certs.p7c-\n");
443 EXPECT_FALSE(callback2.HasResult());
446 // Start several requests, and cancel one of them after the first has completed.
447 // NOTE: The python test server is single threaded and can only service one
448 // request at a time. After a socket is opened by the server it waits for it to
449 // be completed, and any subsequent request will hang until the first socket is
450 // closed.
451 // Cancelling the first request can therefore be problematic, since if
452 // cancellation is done after the socket is opened but before reading/writing,
453 // then the socket is re-cycled and things will be stalled until the cleanup
454 // timer (10 seconds) closes it.
455 // To work around this, the last request is cancelled, and hope that the
456 // requests are given opened sockets in a FIFO order.
457 // TODO(eroman): Make this more robust.
458 TEST_F(CertNetFetcherImplTest, CancelAfterRunningMessageLoop) {
459 ASSERT_TRUE(test_server_.Start());
461 CertNetFetcherImpl fetcher(&context_);
462 TestFetchCallback callback1;
463 TestFetchCallback callback2;
464 TestFetchCallback callback3;
466 GURL url1 = test_server_.GetURL("files/cert.crt");
468 scoped_ptr<CertNetFetcher::Request> request1 =
469 StartRequest(&fetcher, url1, callback1);
471 GURL url2 = test_server_.GetURL("files/certs.p7c");
472 scoped_ptr<CertNetFetcher::Request> request2 =
473 StartRequest(&fetcher, url2, callback2);
475 GURL url3("ftp://www.not.supported.com/foo");
476 scoped_ptr<CertNetFetcher::Request> request3 =
477 StartRequest(&fetcher, url3, callback3);
479 EXPECT_FALSE(callback1.HasResult());
480 EXPECT_FALSE(callback2.HasResult());
481 EXPECT_FALSE(callback3.HasResult());
483 // Wait for the ftp request to complete (it should complete right away since
484 // it doesn't even try to connect to the server).
485 scoped_ptr<FetchResult> result3 = callback3.WaitForResult();
486 result3->VerifyFailure(ERR_DISALLOWED_URL_SCHEME);
488 // Cancel the second outstanding request.
489 request2.reset();
491 // Wait for the first request to complete.
492 scoped_ptr<FetchResult> result2 = callback1.WaitForResult();
494 // Verify the fetch results.
495 result2->VerifySuccess("-cert.crt-\n");
498 // Delete a CertNetFetcherImpl with outstanding requests on it.
499 TEST_F(CertNetFetcherImplTest, DeleteCancels) {
500 ASSERT_TRUE(test_server_.Start());
502 scoped_ptr<CertNetFetcherImpl> fetcher(new CertNetFetcherImpl(&context_));
504 GURL url(test_server_.GetURL("slow/certs.p7c?20"));
505 TestFetchCallback callback;
506 scoped_ptr<CertNetFetcher::Request> request =
507 StartRequest(fetcher.get(), url, callback);
509 // Destroy the fetcher before the outstanding request.
510 fetcher.reset();
513 // Fetch the same URLs in parallel and verify that only 1 request is made per
514 // URL.
515 TEST_F(CertNetFetcherImplTest, ParallelFetchDuplicates) {
516 ASSERT_TRUE(test_server_.Start());
518 CertNetFetcherImpl fetcher(&context_);
520 GURL url1 = test_server_.GetURL("files/cert.crt");
521 GURL url2 = test_server_.GetURL("files/root.crl");
523 // Issue 3 requests for url1, and 3 requests for url2
524 TestFetchCallback callback1;
525 scoped_ptr<CertNetFetcher::Request> request1 =
526 StartRequest(&fetcher, url1, callback1);
528 TestFetchCallback callback2;
529 scoped_ptr<CertNetFetcher::Request> request2 =
530 StartRequest(&fetcher, url2, callback2);
532 TestFetchCallback callback3;
533 scoped_ptr<CertNetFetcher::Request> request3 =
534 StartRequest(&fetcher, url1, callback3);
536 TestFetchCallback callback4;
537 scoped_ptr<CertNetFetcher::Request> request4 =
538 StartRequest(&fetcher, url2, callback4);
540 TestFetchCallback callback5;
541 scoped_ptr<CertNetFetcher::Request> request5 =
542 StartRequest(&fetcher, url2, callback5);
544 TestFetchCallback callback6;
545 scoped_ptr<CertNetFetcher::Request> request6 =
546 StartRequest(&fetcher, url1, callback6);
548 // Cancel all but one of the requests for url1.
549 request1.reset();
550 request3.reset();
552 // Wait for the remaining requests to finish.
553 scoped_ptr<FetchResult> result2 = callback2.WaitForResult();
554 scoped_ptr<FetchResult> result4 = callback4.WaitForResult();
555 scoped_ptr<FetchResult> result5 = callback5.WaitForResult();
556 scoped_ptr<FetchResult> result6 = callback6.WaitForResult();
558 // Verify that none of the cancelled requests for url1 completed (since they
559 // were cancelled).
560 EXPECT_FALSE(callback1.HasResult());
561 EXPECT_FALSE(callback3.HasResult());
563 // Verify the fetch results.
564 result2->VerifySuccess("-root.crl-\n");
565 result4->VerifySuccess("-root.crl-\n");
566 result5->VerifySuccess("-root.crl-\n");
567 result6->VerifySuccess("-cert.crt-\n");
569 // Verify that only 2 URLRequests were started even though 6 requests were
570 // issued.
571 EXPECT_EQ(2, network_delegate_.created_requests());
574 // Cancel a request and then start another one for the same URL.
575 TEST_F(CertNetFetcherImplTest, CancelThenStart) {
576 ASSERT_TRUE(test_server_.Start());
578 CertNetFetcherImpl fetcher(&context_);
579 TestFetchCallback callback1;
580 TestFetchCallback callback2;
581 TestFetchCallback callback3;
583 GURL url = test_server_.GetURL("files/cert.crt");
585 scoped_ptr<CertNetFetcher::Request> request1 =
586 StartRequest(&fetcher, url, callback1);
587 request1.reset();
589 scoped_ptr<CertNetFetcher::Request> request2 =
590 StartRequest(&fetcher, url, callback2);
592 scoped_ptr<CertNetFetcher::Request> request3 =
593 StartRequest(&fetcher, url, callback3);
594 request3.reset();
596 // All but |request2| were canceled.
597 scoped_ptr<FetchResult> result = callback2.WaitForResult();
599 result->VerifySuccess("-cert.crt-\n");
601 EXPECT_FALSE(callback1.HasResult());
602 EXPECT_FALSE(callback3.HasResult());
604 // One URLRequest that was cancelled, then another right afterwards.
605 EXPECT_EQ(2, network_delegate_.created_requests());
608 // Start duplicate requests and then cancel all of them.
609 TEST_F(CertNetFetcherImplTest, CancelAll) {
610 ASSERT_TRUE(test_server_.Start());
612 CertNetFetcherImpl fetcher(&context_);
613 TestFetchCallback callback[3];
614 scoped_ptr<CertNetFetcher::Request> request[3];
616 GURL url = test_server_.GetURL("files/cert.crt");
618 for (size_t i = 0; i < arraysize(callback); ++i) {
619 request[i] = StartRequest(&fetcher, url, callback[i]);
622 // Cancel all the requests.
623 for (size_t i = 0; i < arraysize(request); ++i)
624 request[i].reset();
626 EXPECT_EQ(1, network_delegate_.created_requests());
628 for (size_t i = 0; i < arraysize(request); ++i)
629 EXPECT_FALSE(callback[i].HasResult());
632 void DeleteCertNetFetcher(CertNetFetcher* fetcher) {
633 delete fetcher;
636 // Delete the CertNetFetcherImpl within a request callback.
637 TEST_F(CertNetFetcherImplTest, DeleteWithinCallback) {
638 ASSERT_TRUE(test_server_.Start());
640 // Deleted by callback2.
641 CertNetFetcher* fetcher = new CertNetFetcherImpl(&context_);
643 GURL url = test_server_.GetURL("files/cert.crt");
645 TestFetchCallback callback[4];
646 scoped_ptr<CertNetFetcher::Request> reqs[4];
647 callback[1].set_extra_closure(base::Bind(DeleteCertNetFetcher, fetcher));
649 for (size_t i = 0; i < arraysize(callback); ++i)
650 reqs[i] = StartRequest(fetcher, url, callback[i]);
652 EXPECT_EQ(1, network_delegate_.created_requests());
654 callback[1].WaitForResult();
656 // Assume requests for the same URL are executed in FIFO order.
657 EXPECT_TRUE(callback[0].HasResult());
658 EXPECT_FALSE(callback[2].HasResult());
659 EXPECT_FALSE(callback[3].HasResult());
662 void FetchRequest(CertNetFetcher* fetcher,
663 const GURL& url,
664 TestFetchCallback* callback,
665 scoped_ptr<CertNetFetcher::Request>* request) {
666 *request = StartRequest(fetcher, url, *callback);
669 // Make a request during callback for the same URL.
670 TEST_F(CertNetFetcherImplTest, FetchWithinCallback) {
671 ASSERT_TRUE(test_server_.Start());
673 CertNetFetcherImpl fetcher(&context_);
675 GURL url = test_server_.GetURL("files/cert.crt");
677 TestFetchCallback callback[5];
678 scoped_ptr<CertNetFetcher::Request> req[5];
679 callback[1].set_extra_closure(
680 base::Bind(FetchRequest, &fetcher, url, &callback[4], &req[4]));
682 for (size_t i = 0; i < arraysize(callback) - 1; ++i)
683 req[i] = StartRequest(&fetcher, url, callback[i]);
685 EXPECT_EQ(1, network_delegate_.created_requests());
687 for (size_t i = 0; i < arraysize(callback); ++i) {
688 scoped_ptr<FetchResult> result = callback[i].WaitForResult();
689 result->VerifySuccess("-cert.crt-\n");
692 // The fetch started within a callback should have started a new request
693 // rather than attaching to the current job.
694 EXPECT_EQ(2, network_delegate_.created_requests());
697 void CancelRequest(scoped_ptr<CertNetFetcher::Request>* request) {
698 request->reset();
701 // Cancel a request while executing a callback for the same job.
702 TEST_F(CertNetFetcherImplTest, CancelWithinCallback) {
703 ASSERT_TRUE(test_server_.Start());
705 CertNetFetcherImpl fetcher(&context_);
707 GURL url = test_server_.GetURL("files/cert.crt");
709 TestFetchCallback callback[4];
710 scoped_ptr<CertNetFetcher::Request> request[4];
712 for (size_t i = 0; i < arraysize(callback); ++i)
713 request[i] = StartRequest(&fetcher, url, callback[i]);
715 // Cancel request[2] when the callback for request[1] runs.
716 callback[1].set_extra_closure(base::Bind(CancelRequest, &request[2]));
718 EXPECT_EQ(1, network_delegate_.created_requests());
720 for (size_t i = 0; i < arraysize(request); ++i) {
721 if (i == 2)
722 continue;
724 scoped_ptr<FetchResult> result = callback[i].WaitForResult();
725 result->VerifySuccess("-cert.crt-\n");
728 // request[2] was cancelled.
729 EXPECT_FALSE(callback[2].HasResult());
732 // Cancel the final request while executing a callback for the same job. Ensure
733 // that the job is not deleted twice.
734 TEST_F(CertNetFetcherImplTest, CancelLastRequestWithinCallback) {
735 ASSERT_TRUE(test_server_.Start());
737 CertNetFetcherImpl fetcher(&context_);
739 GURL url = test_server_.GetURL("files/cert.crt");
741 TestFetchCallback callback1;
742 scoped_ptr<CertNetFetcher::Request> request1 =
743 StartRequest(&fetcher, url, callback1);
745 TestFetchCallback callback2;
746 scoped_ptr<CertNetFetcher::Request> request2 =
747 StartRequest(&fetcher, url, callback1);
749 // Cancel request2 when the callback for request1 runs.
750 callback1.set_extra_closure(base::Bind(CancelRequest, &request2));
752 EXPECT_EQ(1, network_delegate_.created_requests());
754 scoped_ptr<FetchResult> result = callback1.WaitForResult();
755 result->VerifySuccess("-cert.crt-\n");
757 // request2 was cancelled.
758 EXPECT_FALSE(callback2.HasResult());
761 } // namespace net