Insert a state before cert_cb.

If cert_cb runs asynchronously, we end up repeating a large part of very
stateful ClientHello processing. This seems to be mostly fine and there
are few users of server-side cert_cb (it's a new API in 1.0.2), but it's
a little scary.

This is also visible to external consumers because some callbacks get
called multiple times. We especially should try to avoid that as there
is no guarantee that these callbacks are idempotent and give the same
answer each time.

Change-Id: I212b2325eae2cfca0fb423dace101e466c5e5d4e
Reviewed-on: https://boringssl-review.googlesource.com/10224
Commit-Queue: David Benjamin <davidben@google.com>
Commit-Queue: Adam Langley <agl@google.com>
Reviewed-by: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 2a76580..09a6fcc 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -4338,6 +4338,25 @@
 			resumeSession:     resumeSession,
 		})
 
+		// Test ALPN in async mode as well to ensure that extensions callbacks are only
+		// called once.
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "ALPNServer-Async-" + ver.name,
+			config: Config{
+				MaxVersion: ver.version,
+				NextProtos: []string{"foo", "bar", "baz"},
+			},
+			flags: []string{
+				"-expect-advertised-alpn", "\x03foo\x03bar\x03baz",
+				"-select-alpn", "foo",
+				"-async",
+			},
+			expectedNextProto:     "foo",
+			expectedNextProtoType: alpn,
+			resumeSession:         resumeSession,
+		})
+
 		var emptyString string
 		testCases = append(testCases, testCase{
 			testType: clientTest,
@@ -4502,6 +4521,26 @@
 				resumeSession: true,
 			})
 
+			// Test that the ticket callback is only called once when everything before
+			// it in the ClientHello is asynchronous. This corrupts the ticket so
+			// certificate selection callbacks run.
+			testCases = append(testCases, testCase{
+				testType: serverTest,
+				name:     "TicketCallback-SingleCall-" + ver.name,
+				config: Config{
+					MaxVersion: ver.version,
+					Bugs: ProtocolBugs{
+						CorruptTicket: true,
+					},
+				},
+				resumeSession:        true,
+				expectResumeRejected: true,
+				flags: []string{
+					"-use-ticket-callback",
+					"-async",
+				},
+			})
+
 			// Resume with an oversized session id.
 			testCases = append(testCases, testCase{
 				testType: serverTest,