1 // Copyright 2014 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/ssl/channel_id_service.h"
10 #include "base/bind.h"
11 #include "base/location.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/task_runner.h"
17 #include "base/thread_task_runner_handle.h"
18 #include "crypto/ec_private_key.h"
19 #include "net/base/net_errors.h"
20 #include "net/base/test_completion_callback.h"
21 #include "net/cert/asn1_util.h"
22 #include "net/cert/x509_certificate.h"
23 #include "net/ssl/default_channel_id_store.h"
24 #include "net/test/channel_id_test_util.h"
25 #include "testing/gtest/include/gtest/gtest.h"
31 void FailTest(int /* result */) {
35 // Simple task runner that refuses to actually post any tasks. This simulates
36 // a TaskRunner that has been shutdown, by returning false for any attempt to
38 class FailingTaskRunner
: public base::TaskRunner
{
40 FailingTaskRunner() {}
42 bool PostDelayedTask(const tracked_objects::Location
& from_here
,
43 const base::Closure
& task
,
44 base::TimeDelta delay
) override
{
48 bool RunsTasksOnCurrentThread() const override
{ return true; }
51 ~FailingTaskRunner() override
{}
54 DISALLOW_COPY_AND_ASSIGN(FailingTaskRunner
);
57 class MockChannelIDStoreWithAsyncGet
58 : public DefaultChannelIDStore
{
60 MockChannelIDStoreWithAsyncGet()
61 : DefaultChannelIDStore(NULL
), channel_id_count_(0) {}
63 int GetChannelID(const std::string
& server_identifier
,
64 scoped_ptr
<crypto::ECPrivateKey
>* key_result
,
65 const GetChannelIDCallback
& callback
) override
;
67 void SetChannelID(scoped_ptr
<ChannelID
> channel_id
) override
{
68 channel_id_count_
= 1;
71 int GetChannelIDCount() override
{ return channel_id_count_
; }
73 void CallGetChannelIDCallbackWithResult(int err
, crypto::ECPrivateKey
* key
);
76 GetChannelIDCallback callback_
;
77 std::string server_identifier_
;
78 int channel_id_count_
;
81 int MockChannelIDStoreWithAsyncGet::GetChannelID(
82 const std::string
& server_identifier
,
83 scoped_ptr
<crypto::ECPrivateKey
>* key_result
,
84 const GetChannelIDCallback
& callback
) {
85 server_identifier_
= server_identifier
;
87 // Reset the cert count, it'll get incremented in either SetChannelID or
88 // CallGetChannelIDCallbackWithResult.
89 channel_id_count_
= 0;
90 // Do nothing else: the results to be provided will be specified through
91 // CallGetChannelIDCallbackWithResult.
92 return ERR_IO_PENDING
;
95 void MockChannelIDStoreWithAsyncGet::CallGetChannelIDCallbackWithResult(
97 crypto::ECPrivateKey
* key
) {
99 channel_id_count_
= 1;
100 base::ThreadTaskRunnerHandle::Get()->PostTask(
102 base::Bind(callback_
, err
, server_identifier_
,
103 base::Passed(make_scoped_ptr(key
? key
->Copy() : nullptr))));
106 class ChannelIDServiceTest
: public testing::Test
{
108 ChannelIDServiceTest()
109 : service_(new ChannelIDService(new DefaultChannelIDStore(NULL
),
110 base::ThreadTaskRunnerHandle::Get())) {}
113 scoped_ptr
<ChannelIDService
> service_
;
116 TEST_F(ChannelIDServiceTest
, GetDomainForHost
) {
117 EXPECT_EQ("google.com",
118 ChannelIDService::GetDomainForHost("google.com"));
119 EXPECT_EQ("google.com",
120 ChannelIDService::GetDomainForHost("www.google.com"));
121 EXPECT_EQ("foo.appspot.com",
122 ChannelIDService::GetDomainForHost("foo.appspot.com"));
123 EXPECT_EQ("bar.appspot.com",
124 ChannelIDService::GetDomainForHost("foo.bar.appspot.com"));
125 EXPECT_EQ("appspot.com",
126 ChannelIDService::GetDomainForHost("appspot.com"));
127 EXPECT_EQ("google.com",
128 ChannelIDService::GetDomainForHost("www.mail.google.com"));
130 ChannelIDService::GetDomainForHost("goto"));
131 EXPECT_EQ("127.0.0.1",
132 ChannelIDService::GetDomainForHost("127.0.0.1"));
135 TEST_F(ChannelIDServiceTest
, GetCacheMiss
) {
136 std::string
host("encrypted.google.com");
139 TestCompletionCallback callback
;
140 ChannelIDService::Request request
;
142 // Synchronous completion, because the store is initialized.
143 scoped_ptr
<crypto::ECPrivateKey
> key
;
144 EXPECT_EQ(0, service_
->channel_id_count());
145 error
= service_
->GetChannelID(host
, &key
, callback
.callback(), &request
);
146 EXPECT_EQ(ERR_FILE_NOT_FOUND
, error
);
147 EXPECT_FALSE(request
.is_active());
148 EXPECT_EQ(0, service_
->channel_id_count());
152 TEST_F(ChannelIDServiceTest
, CacheHit
) {
153 std::string
host("encrypted.google.com");
156 TestCompletionCallback callback
;
157 ChannelIDService::Request request
;
159 // Asynchronous completion.
160 scoped_ptr
<crypto::ECPrivateKey
> key1
;
161 EXPECT_EQ(0, service_
->channel_id_count());
162 error
= service_
->GetOrCreateChannelID(host
, &key1
, callback
.callback(),
164 EXPECT_EQ(ERR_IO_PENDING
, error
);
165 EXPECT_TRUE(request
.is_active());
166 error
= callback
.WaitForResult();
167 EXPECT_EQ(OK
, error
);
168 EXPECT_EQ(1, service_
->channel_id_count());
170 EXPECT_FALSE(request
.is_active());
172 // Synchronous completion.
173 scoped_ptr
<crypto::ECPrivateKey
> key2
;
174 error
= service_
->GetOrCreateChannelID(host
, &key2
, callback
.callback(),
176 EXPECT_FALSE(request
.is_active());
177 EXPECT_EQ(OK
, error
);
178 EXPECT_EQ(1, service_
->channel_id_count());
179 EXPECT_TRUE(KeysEqual(key1
.get(), key2
.get()));
182 scoped_ptr
<crypto::ECPrivateKey
> key3
;
183 error
= service_
->GetChannelID(host
, &key3
, callback
.callback(), &request
);
184 EXPECT_FALSE(request
.is_active());
185 EXPECT_EQ(OK
, error
);
186 EXPECT_EQ(1, service_
->channel_id_count());
187 EXPECT_TRUE(KeysEqual(key1
.get(), key3
.get()));
189 EXPECT_EQ(3u, service_
->requests());
190 EXPECT_EQ(2u, service_
->key_store_hits());
191 EXPECT_EQ(0u, service_
->inflight_joins());
194 TEST_F(ChannelIDServiceTest
, StoreChannelIDs
) {
196 TestCompletionCallback callback
;
197 ChannelIDService::Request request
;
199 std::string
host1("encrypted.google.com");
200 scoped_ptr
<crypto::ECPrivateKey
> key1
;
201 EXPECT_EQ(0, service_
->channel_id_count());
202 error
= service_
->GetOrCreateChannelID(host1
, &key1
, callback
.callback(),
204 EXPECT_EQ(ERR_IO_PENDING
, error
);
205 EXPECT_TRUE(request
.is_active());
206 error
= callback
.WaitForResult();
207 EXPECT_EQ(OK
, error
);
208 EXPECT_EQ(1, service_
->channel_id_count());
210 std::string
host2("www.verisign.com");
211 scoped_ptr
<crypto::ECPrivateKey
> key2
;
212 error
= service_
->GetOrCreateChannelID(host2
, &key2
, callback
.callback(),
214 EXPECT_EQ(ERR_IO_PENDING
, error
);
215 EXPECT_TRUE(request
.is_active());
216 error
= callback
.WaitForResult();
217 EXPECT_EQ(OK
, error
);
218 EXPECT_EQ(2, service_
->channel_id_count());
220 std::string
host3("www.twitter.com");
221 scoped_ptr
<crypto::ECPrivateKey
> key3
;
222 error
= service_
->GetOrCreateChannelID(host3
, &key3
, callback
.callback(),
224 EXPECT_EQ(ERR_IO_PENDING
, error
);
225 EXPECT_TRUE(request
.is_active());
226 error
= callback
.WaitForResult();
227 EXPECT_EQ(OK
, error
);
228 EXPECT_EQ(3, service_
->channel_id_count());
230 EXPECT_FALSE(KeysEqual(key1
.get(), key2
.get()));
231 EXPECT_FALSE(KeysEqual(key1
.get(), key3
.get()));
232 EXPECT_FALSE(KeysEqual(key2
.get(), key3
.get()));
235 // Tests an inflight join.
236 TEST_F(ChannelIDServiceTest
, InflightJoin
) {
237 std::string
host("encrypted.google.com");
240 scoped_ptr
<crypto::ECPrivateKey
> key1
;
241 TestCompletionCallback callback1
;
242 ChannelIDService::Request request1
;
244 scoped_ptr
<crypto::ECPrivateKey
> key2
;
245 TestCompletionCallback callback2
;
246 ChannelIDService::Request request2
;
248 error
= service_
->GetOrCreateChannelID(host
, &key1
, callback1
.callback(),
250 EXPECT_EQ(ERR_IO_PENDING
, error
);
251 EXPECT_TRUE(request1
.is_active());
252 // Should join with the original request.
253 error
= service_
->GetOrCreateChannelID(host
, &key2
, callback2
.callback(),
255 EXPECT_EQ(ERR_IO_PENDING
, error
);
256 EXPECT_TRUE(request2
.is_active());
258 error
= callback1
.WaitForResult();
259 EXPECT_EQ(OK
, error
);
260 error
= callback2
.WaitForResult();
261 EXPECT_EQ(OK
, error
);
263 EXPECT_EQ(2u, service_
->requests());
264 EXPECT_EQ(0u, service_
->key_store_hits());
265 EXPECT_EQ(1u, service_
->inflight_joins());
266 EXPECT_EQ(1u, service_
->workers_created());
269 // Tests an inflight join of a Get request to a GetOrCreate request.
270 TEST_F(ChannelIDServiceTest
, InflightJoinGetOrCreateAndGet
) {
271 std::string
host("encrypted.google.com");
274 scoped_ptr
<crypto::ECPrivateKey
> key1
;
275 TestCompletionCallback callback1
;
276 ChannelIDService::Request request1
;
278 scoped_ptr
<crypto::ECPrivateKey
> key2
;
279 TestCompletionCallback callback2
;
280 ChannelIDService::Request request2
;
282 error
= service_
->GetOrCreateChannelID(host
, &key1
, callback1
.callback(),
284 EXPECT_EQ(ERR_IO_PENDING
, error
);
285 EXPECT_TRUE(request1
.is_active());
286 // Should join with the original request.
287 error
= service_
->GetChannelID(host
, &key2
, callback2
.callback(), &request2
);
288 EXPECT_EQ(ERR_IO_PENDING
, error
);
289 EXPECT_TRUE(request2
.is_active());
291 error
= callback1
.WaitForResult();
292 EXPECT_EQ(OK
, error
);
293 error
= callback2
.WaitForResult();
294 EXPECT_EQ(OK
, error
);
295 EXPECT_TRUE(KeysEqual(key1
.get(), key2
.get()));
297 EXPECT_EQ(2u, service_
->requests());
298 EXPECT_EQ(0u, service_
->key_store_hits());
299 EXPECT_EQ(1u, service_
->inflight_joins());
300 EXPECT_EQ(1u, service_
->workers_created());
303 // Tests that the callback of a canceled request is never made.
304 TEST_F(ChannelIDServiceTest
, CancelRequest
) {
305 std::string
host("encrypted.google.com");
306 scoped_ptr
<crypto::ECPrivateKey
> key
;
308 ChannelIDService::Request request
;
310 error
= service_
->GetOrCreateChannelID(host
, &key
, base::Bind(&FailTest
),
312 EXPECT_EQ(ERR_IO_PENDING
, error
);
313 EXPECT_TRUE(request
.is_active());
315 EXPECT_FALSE(request
.is_active());
317 // Wait for reply from ChannelIDServiceWorker to be posted back to the
319 base::MessageLoop::current()->RunUntilIdle();
321 // Even though the original request was cancelled, the service will still
322 // store the result, it just doesn't call the callback.
323 EXPECT_EQ(1, service_
->channel_id_count());
326 // Tests that destructing the Request cancels the request.
327 TEST_F(ChannelIDServiceTest
, CancelRequestByHandleDestruction
) {
328 std::string
host("encrypted.google.com");
329 scoped_ptr
<crypto::ECPrivateKey
> key
;
331 scoped_ptr
<ChannelIDService::Request
> request(
332 new ChannelIDService::Request());
334 error
= service_
->GetOrCreateChannelID(host
, &key
, base::Bind(&FailTest
),
336 EXPECT_EQ(ERR_IO_PENDING
, error
);
337 EXPECT_TRUE(request
->is_active());
339 // Delete the Request object.
342 // Wait for reply from ChannelIDServiceWorker to be posted back to the
344 base::MessageLoop::current()->RunUntilIdle();
346 // Even though the original request was cancelled, the service will still
347 // store the result, it just doesn't call the callback.
348 EXPECT_EQ(1, service_
->channel_id_count());
351 TEST_F(ChannelIDServiceTest
, DestructionWithPendingRequest
) {
352 std::string
host("encrypted.google.com");
353 scoped_ptr
<crypto::ECPrivateKey
> key
;
355 ChannelIDService::Request request
;
357 error
= service_
->GetOrCreateChannelID(host
, &key
, base::Bind(&FailTest
),
359 EXPECT_EQ(ERR_IO_PENDING
, error
);
360 EXPECT_TRUE(request
.is_active());
362 // Cancel request and destroy the ChannelIDService.
366 // ChannelIDServiceWorker should not post anything back to the
367 // non-existent ChannelIDService, but run the loop just to be sure it
369 base::MessageLoop::current()->RunUntilIdle();
371 // If we got here without crashing or a valgrind error, it worked.
374 // Tests that shutting down the sequenced worker pool and then making new
375 // requests gracefully fails.
376 // This is a regression test for http://crbug.com/236387
377 TEST_F(ChannelIDServiceTest
, RequestAfterPoolShutdown
) {
378 scoped_refptr
<FailingTaskRunner
> task_runner(new FailingTaskRunner
);
379 service_
.reset(new ChannelIDService(
380 new DefaultChannelIDStore(NULL
), task_runner
));
382 // Make a request that will force synchronous completion.
383 std::string
host("encrypted.google.com");
384 scoped_ptr
<crypto::ECPrivateKey
> key
;
386 ChannelIDService::Request request
;
388 error
= service_
->GetOrCreateChannelID(host
, &key
, base::Bind(&FailTest
),
390 // If we got here without crashing or a valgrind error, it worked.
391 ASSERT_EQ(ERR_INSUFFICIENT_RESOURCES
, error
);
392 EXPECT_FALSE(request
.is_active());
395 // Tests that simultaneous creation of different certs works.
396 TEST_F(ChannelIDServiceTest
, SimultaneousCreation
) {
399 std::string
host1("encrypted.google.com");
400 scoped_ptr
<crypto::ECPrivateKey
> key1
;
401 TestCompletionCallback callback1
;
402 ChannelIDService::Request request1
;
404 std::string
host2("foo.com");
405 scoped_ptr
<crypto::ECPrivateKey
> key2
;
406 TestCompletionCallback callback2
;
407 ChannelIDService::Request request2
;
409 std::string
host3("bar.com");
410 scoped_ptr
<crypto::ECPrivateKey
> key3
;
411 TestCompletionCallback callback3
;
412 ChannelIDService::Request request3
;
414 error
= service_
->GetOrCreateChannelID(host1
, &key1
, callback1
.callback(),
416 EXPECT_EQ(ERR_IO_PENDING
, error
);
417 EXPECT_TRUE(request1
.is_active());
419 error
= service_
->GetOrCreateChannelID(host2
, &key2
, callback2
.callback(),
421 EXPECT_EQ(ERR_IO_PENDING
, error
);
422 EXPECT_TRUE(request2
.is_active());
424 error
= service_
->GetOrCreateChannelID(host3
, &key3
, callback3
.callback(),
426 EXPECT_EQ(ERR_IO_PENDING
, error
);
427 EXPECT_TRUE(request3
.is_active());
429 error
= callback1
.WaitForResult();
430 EXPECT_EQ(OK
, error
);
433 error
= callback2
.WaitForResult();
434 EXPECT_EQ(OK
, error
);
437 error
= callback3
.WaitForResult();
438 EXPECT_EQ(OK
, error
);
441 EXPECT_FALSE(KeysEqual(key1
.get(), key2
.get()));
442 EXPECT_FALSE(KeysEqual(key1
.get(), key3
.get()));
443 EXPECT_FALSE(KeysEqual(key2
.get(), key3
.get()));
445 EXPECT_EQ(3, service_
->channel_id_count());
448 TEST_F(ChannelIDServiceTest
, AsyncStoreGetOrCreateNoChannelIDsInStore
) {
449 MockChannelIDStoreWithAsyncGet
* mock_store
=
450 new MockChannelIDStoreWithAsyncGet();
451 service_
= scoped_ptr
<ChannelIDService
>(
452 new ChannelIDService(mock_store
, base::ThreadTaskRunnerHandle::Get()));
454 std::string
host("encrypted.google.com");
457 TestCompletionCallback callback
;
458 ChannelIDService::Request request
;
460 // Asynchronous completion with no certs in the store.
461 scoped_ptr
<crypto::ECPrivateKey
> key
;
462 EXPECT_EQ(0, service_
->channel_id_count());
464 service_
->GetOrCreateChannelID(host
, &key
, callback
.callback(), &request
);
465 EXPECT_EQ(ERR_IO_PENDING
, error
);
466 EXPECT_TRUE(request
.is_active());
468 mock_store
->CallGetChannelIDCallbackWithResult(ERR_FILE_NOT_FOUND
, nullptr);
470 error
= callback
.WaitForResult();
471 EXPECT_EQ(OK
, error
);
472 EXPECT_EQ(1, service_
->channel_id_count());
474 EXPECT_FALSE(request
.is_active());
477 TEST_F(ChannelIDServiceTest
, AsyncStoreGetNoChannelIDsInStore
) {
478 MockChannelIDStoreWithAsyncGet
* mock_store
=
479 new MockChannelIDStoreWithAsyncGet();
480 service_
= scoped_ptr
<ChannelIDService
>(
481 new ChannelIDService(mock_store
, base::ThreadTaskRunnerHandle::Get()));
483 std::string
host("encrypted.google.com");
486 TestCompletionCallback callback
;
487 ChannelIDService::Request request
;
489 // Asynchronous completion with no certs in the store.
490 scoped_ptr
<crypto::ECPrivateKey
> key
;
491 EXPECT_EQ(0, service_
->channel_id_count());
492 error
= service_
->GetChannelID(host
, &key
, callback
.callback(), &request
);
493 EXPECT_EQ(ERR_IO_PENDING
, error
);
494 EXPECT_TRUE(request
.is_active());
496 mock_store
->CallGetChannelIDCallbackWithResult(ERR_FILE_NOT_FOUND
, nullptr);
498 error
= callback
.WaitForResult();
499 EXPECT_EQ(ERR_FILE_NOT_FOUND
, error
);
500 EXPECT_EQ(0, service_
->channel_id_count());
501 EXPECT_EQ(0u, service_
->workers_created());
503 EXPECT_FALSE(request
.is_active());
506 TEST_F(ChannelIDServiceTest
, AsyncStoreGetOrCreateOneCertInStore
) {
507 MockChannelIDStoreWithAsyncGet
* mock_store
=
508 new MockChannelIDStoreWithAsyncGet();
509 service_
= scoped_ptr
<ChannelIDService
>(
510 new ChannelIDService(mock_store
, base::ThreadTaskRunnerHandle::Get()));
512 std::string
host("encrypted.google.com");
515 TestCompletionCallback callback
;
516 ChannelIDService::Request request
;
518 // Asynchronous completion with a cert in the store.
519 scoped_ptr
<crypto::ECPrivateKey
> key
;
520 EXPECT_EQ(0, service_
->channel_id_count());
522 service_
->GetOrCreateChannelID(host
, &key
, callback
.callback(), &request
);
523 EXPECT_EQ(ERR_IO_PENDING
, error
);
524 EXPECT_TRUE(request
.is_active());
526 scoped_ptr
<crypto::ECPrivateKey
> expected_key(crypto::ECPrivateKey::Create());
527 mock_store
->CallGetChannelIDCallbackWithResult(OK
, expected_key
.get());
529 error
= callback
.WaitForResult();
530 EXPECT_EQ(OK
, error
);
531 EXPECT_EQ(1, service_
->channel_id_count());
532 EXPECT_EQ(1u, service_
->requests());
533 EXPECT_EQ(1u, service_
->key_store_hits());
534 // Because the cert was found in the store, no new workers should have been
536 EXPECT_EQ(0u, service_
->workers_created());
538 EXPECT_TRUE(KeysEqual(expected_key
.get(), key
.get()));
539 EXPECT_FALSE(request
.is_active());
542 TEST_F(ChannelIDServiceTest
, AsyncStoreGetOneCertInStore
) {
543 MockChannelIDStoreWithAsyncGet
* mock_store
=
544 new MockChannelIDStoreWithAsyncGet();
545 service_
= scoped_ptr
<ChannelIDService
>(
546 new ChannelIDService(mock_store
, base::ThreadTaskRunnerHandle::Get()));
548 std::string
host("encrypted.google.com");
551 TestCompletionCallback callback
;
552 ChannelIDService::Request request
;
554 // Asynchronous completion with a cert in the store.
555 scoped_ptr
<crypto::ECPrivateKey
> key
;
556 std::string private_key
, spki
;
557 EXPECT_EQ(0, service_
->channel_id_count());
558 error
= service_
->GetChannelID(host
, &key
, callback
.callback(), &request
);
559 EXPECT_EQ(ERR_IO_PENDING
, error
);
560 EXPECT_TRUE(request
.is_active());
562 scoped_ptr
<crypto::ECPrivateKey
> expected_key(crypto::ECPrivateKey::Create());
563 mock_store
->CallGetChannelIDCallbackWithResult(OK
, expected_key
.get());
565 error
= callback
.WaitForResult();
566 EXPECT_EQ(OK
, error
);
567 EXPECT_EQ(1, service_
->channel_id_count());
568 EXPECT_EQ(1u, service_
->requests());
569 EXPECT_EQ(1u, service_
->key_store_hits());
570 // Because the cert was found in the store, no new workers should have been
572 EXPECT_EQ(0u, service_
->workers_created());
573 EXPECT_TRUE(KeysEqual(expected_key
.get(), key
.get()));
574 EXPECT_FALSE(request
.is_active());
577 TEST_F(ChannelIDServiceTest
, AsyncStoreGetThenCreateNoCertsInStore
) {
578 MockChannelIDStoreWithAsyncGet
* mock_store
=
579 new MockChannelIDStoreWithAsyncGet();
580 service_
= scoped_ptr
<ChannelIDService
>(
581 new ChannelIDService(mock_store
, base::ThreadTaskRunnerHandle::Get()));
583 std::string
host("encrypted.google.com");
587 // Asynchronous get with no certs in the store.
588 TestCompletionCallback callback1
;
589 ChannelIDService::Request request1
;
590 scoped_ptr
<crypto::ECPrivateKey
> key1
;
591 EXPECT_EQ(0, service_
->channel_id_count());
592 error
= service_
->GetChannelID(host
, &key1
, callback1
.callback(), &request1
);
593 EXPECT_EQ(ERR_IO_PENDING
, error
);
594 EXPECT_TRUE(request1
.is_active());
596 // Asynchronous get/create with no certs in the store.
597 TestCompletionCallback callback2
;
598 ChannelIDService::Request request2
;
599 scoped_ptr
<crypto::ECPrivateKey
> key2
;
600 EXPECT_EQ(0, service_
->channel_id_count());
601 error
= service_
->GetOrCreateChannelID(host
, &key2
, callback2
.callback(),
603 EXPECT_EQ(ERR_IO_PENDING
, error
);
604 EXPECT_TRUE(request2
.is_active());
606 mock_store
->CallGetChannelIDCallbackWithResult(ERR_FILE_NOT_FOUND
, nullptr);
608 // Even though the first request didn't ask to create a cert, it gets joined
609 // by the second, which does, so both succeed.
610 error
= callback1
.WaitForResult();
611 EXPECT_EQ(OK
, error
);
612 error
= callback2
.WaitForResult();
613 EXPECT_EQ(OK
, error
);
615 // One cert is created, one request is joined.
616 EXPECT_EQ(2U, service_
->requests());
617 EXPECT_EQ(1, service_
->channel_id_count());
618 EXPECT_EQ(1u, service_
->workers_created());
619 EXPECT_EQ(1u, service_
->inflight_joins());
621 EXPECT_TRUE(KeysEqual(key1
.get(), key2
.get()));
622 EXPECT_FALSE(request1
.is_active());
623 EXPECT_FALSE(request2
.is_active());