Test non-blocking connect in connect BIOs

I'm a little surprised this test reliably works on loopback, but it
seems to be OK. Let's see what the CQ thinks.

Change-Id: I71ede5bd64d8fb82e346b44f27d53475f41677ec
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/94808
Commit-Queue: Adam Langley <agl@google.com>
Auto-Submit: David Benjamin <davidben@google.com>
Presubmit-BoringSSL-Verified: boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com <boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/bio/bio_test.cc b/crypto/bio/bio_test.cc
index afe35dd..5c93174 100644
--- a/crypto/bio/bio_test.cc
+++ b/crypto/bio/bio_test.cc
@@ -232,26 +232,113 @@
              ntohs(addr.ToIPv4().sin_port));
   }
 
-  // Connect to it with a connect BIO.
-  UniquePtr<BIO> bio(BIO_new_connect(hostname));
-  ASSERT_TRUE(bio);
+  // Using a connect BIO implicitly connects to it.
+  {
+    // Connect to it with a connect BIO.
+    UniquePtr<BIO> bio(BIO_new_connect(hostname));
+    ASSERT_TRUE(bio);
 
-  // Write a test message to the BIO. This is assumed to be smaller than the
-  // transport buffer.
-  ASSERT_EQ(static_cast<int>(sizeof(kTestMessage)),
-            BIO_write(bio.get(), kTestMessage, sizeof(kTestMessage)))
-      << LastSocketError();
+    // Write a test message to the BIO. This is assumed to be smaller than the
+    // transport buffer.
+    ASSERT_EQ(static_cast<int>(sizeof(kTestMessage)),
+              BIO_write(bio.get(), kTestMessage, sizeof(kTestMessage)))
+        << LastSocketError();
 
-  // Accept the socket.
-  OwnedSocket sock(accept(listening_sock.get(), addr.addr_mut(), &addr.len));
-  ASSERT_TRUE(sock.is_valid()) << LastSocketError();
+    // Accept the socket.
+    OwnedSocket sock(accept(listening_sock.get(), addr.addr_mut(), &addr.len));
+    ASSERT_TRUE(sock.is_valid()) << LastSocketError();
 
-  // Check the same message is read back out.
-  char buf[sizeof(kTestMessage)];
-  ASSERT_EQ(static_cast<int>(sizeof(kTestMessage)),
-            recv(sock.get(), buf, sizeof(buf), 0))
-      << LastSocketError();
-  EXPECT_EQ(Bytes(kTestMessage, sizeof(kTestMessage)), Bytes(buf, sizeof(buf)));
+    // Check the same message is read back out.
+    char buf[sizeof(kTestMessage)];
+    ASSERT_EQ(static_cast<int>(sizeof(kTestMessage)),
+              recv(sock.get(), buf, sizeof(buf), 0))
+        << LastSocketError();
+    EXPECT_EQ(Bytes(kTestMessage, sizeof(kTestMessage)),
+              Bytes(buf, sizeof(buf)));
+  }
+
+  // Explicitly connect to the BIO first.
+  {
+    UniquePtr<BIO> bio(BIO_new_connect(hostname));
+    ASSERT_TRUE(bio);
+
+    ASSERT_EQ(1, BIO_do_connect(bio.get())) << LastSocketError();
+    ASSERT_EQ(static_cast<int>(sizeof(kTestMessage)),
+              BIO_write(bio.get(), kTestMessage, sizeof(kTestMessage)))
+        << LastSocketError();
+
+    // Accept and read.
+    OwnedSocket sock(accept(listening_sock.get(), addr.addr_mut(), &addr.len));
+    ASSERT_TRUE(sock.is_valid()) << LastSocketError();
+    char buf[sizeof(kTestMessage)];
+    ASSERT_EQ(static_cast<int>(sizeof(kTestMessage)),
+              recv(sock.get(), buf, sizeof(buf), 0))
+        << LastSocketError();
+    EXPECT_EQ(Bytes(kTestMessage, sizeof(kTestMessage)),
+              Bytes(buf, sizeof(buf)));
+  }
+
+  // Connect in non-blocking mode.
+  {
+    UniquePtr<BIO> bio(BIO_new_connect(hostname));
+    ASSERT_TRUE(bio);
+    ASSERT_EQ(1, BIO_set_nbio(bio.get(), 1));
+
+    ASSERT_EQ(-1, BIO_do_connect(bio.get()));
+    EXPECT_TRUE(BIO_should_retry(bio.get()));
+    EXPECT_TRUE(BIO_should_io_special(bio.get()));
+    EXPECT_EQ(BIO_RR_CONNECT, BIO_get_retry_reason(bio.get()));
+
+    // Wait for the underlying socket to become writable and try again.
+    int fd = BIO_get_fd(bio.get(), nullptr);
+    ASSERT_GT(fd, -1);
+    ASSERT_TRUE(WaitForSocket(static_cast<Socket>(fd), WaitType::kWrite));
+    ASSERT_EQ(1, BIO_do_connect(bio.get()));
+
+    ASSERT_EQ(static_cast<int>(sizeof(kTestMessage)),
+              BIO_write(bio.get(), kTestMessage, sizeof(kTestMessage)))
+        << LastSocketError();
+
+    // Accept and read.
+    OwnedSocket sock(accept(listening_sock.get(), addr.addr_mut(), &addr.len));
+    ASSERT_TRUE(sock.is_valid()) << LastSocketError();
+    char buf[sizeof(kTestMessage)];
+    ASSERT_EQ(static_cast<int>(sizeof(kTestMessage)),
+              recv(sock.get(), buf, sizeof(buf), 0))
+        << LastSocketError();
+    EXPECT_EQ(Bytes(kTestMessage, sizeof(kTestMessage)),
+              Bytes(buf, sizeof(buf)));
+  }
+
+  // Implicitly connect in non-blocking mode.
+  {
+    UniquePtr<BIO> bio(BIO_new_connect(hostname));
+    ASSERT_TRUE(bio);
+    ASSERT_EQ(1, BIO_set_nbio(bio.get(), 1));
+
+    ASSERT_EQ(-1, BIO_write(bio.get(), kTestMessage, sizeof(kTestMessage)));
+    EXPECT_TRUE(BIO_should_retry(bio.get()));
+    EXPECT_TRUE(BIO_should_io_special(bio.get()));
+    EXPECT_EQ(BIO_RR_CONNECT, BIO_get_retry_reason(bio.get()));
+
+    // Wait for the underlying socket to become writable and try again.
+    int fd = BIO_get_fd(bio.get(), nullptr);
+    ASSERT_GT(fd, -1);
+    ASSERT_TRUE(WaitForSocket(static_cast<Socket>(fd), WaitType::kWrite));
+    ASSERT_EQ(static_cast<int>(sizeof(kTestMessage)),
+              BIO_write(bio.get(), kTestMessage, sizeof(kTestMessage)))
+        << LastSocketError();
+
+    // Accept and read.
+    OwnedSocket sock(accept(listening_sock.get(), addr.addr_mut(), &addr.len));
+    ASSERT_TRUE(sock.is_valid()) << LastSocketError();
+    char buf[sizeof(kTestMessage)];
+    ASSERT_EQ(static_cast<int>(sizeof(kTestMessage)),
+              recv(sock.get(), buf, sizeof(buf), 0))
+        << LastSocketError();
+    EXPECT_EQ(Bytes(kTestMessage, sizeof(kTestMessage)),
+              Bytes(buf, sizeof(buf)));
+  }
 }
 
 TEST(BIOTest, SocketNonBlocking) {