+TEST(AsyncSSLSocketTest, ConnResetErrorString) {
+ // Start listening on a local port
+ WriteCallbackBase writeCallback;
+ WriteErrorCallback readCallback(&writeCallback);
+ HandshakeCallback handshakeCallback(&readCallback,
+ HandshakeCallback::EXPECT_ERROR);
+ SSLServerAcceptCallback acceptCallback(&handshakeCallback);
+ TestSSLServer server(&acceptCallback);
+
+ auto socket = std::make_shared<BlockingSocket>(server.getAddress(), nullptr);
+ socket->open();
+ uint8_t buf[3] = {0x16, 0x03, 0x01};
+ socket->write(buf, sizeof(buf));
+ socket->closeWithReset();
+
+ handshakeCallback.waitForHandshake();
+ EXPECT_NE(
+ handshakeCallback.errorString_.find("Network error"), std::string::npos);
+ EXPECT_NE(handshakeCallback.errorString_.find("104"), std::string::npos);
+}
+
+TEST(AsyncSSLSocketTest, ConnEOFErrorString) {
+ // Start listening on a local port
+ WriteCallbackBase writeCallback;
+ WriteErrorCallback readCallback(&writeCallback);
+ HandshakeCallback handshakeCallback(&readCallback,
+ HandshakeCallback::EXPECT_ERROR);
+ SSLServerAcceptCallback acceptCallback(&handshakeCallback);
+ TestSSLServer server(&acceptCallback);
+
+ auto socket = std::make_shared<BlockingSocket>(server.getAddress(), nullptr);
+ socket->open();
+ uint8_t buf[3] = {0x16, 0x03, 0x01};
+ socket->write(buf, sizeof(buf));
+ socket->close();
+
+ handshakeCallback.waitForHandshake();
+ EXPECT_NE(
+ handshakeCallback.errorString_.find("Connection EOF"), std::string::npos);
+ EXPECT_NE(handshakeCallback.errorString_.find("EOF"), std::string::npos);
+}
+
+TEST(AsyncSSLSocketTest, ConnOpenSSLErrorString) {
+ // Start listening on a local port
+ WriteCallbackBase writeCallback;
+ WriteErrorCallback readCallback(&writeCallback);
+ HandshakeCallback handshakeCallback(&readCallback,
+ HandshakeCallback::EXPECT_ERROR);
+ SSLServerAcceptCallback acceptCallback(&handshakeCallback);
+ TestSSLServer server(&acceptCallback);
+
+ auto socket = std::make_shared<BlockingSocket>(server.getAddress(), nullptr);
+ socket->open();
+ uint8_t buf[256] = {0x16, 0x03};
+ memset(buf + 2, 'a', sizeof(buf) - 2);
+ socket->write(buf, sizeof(buf));
+ socket->close();
+
+ handshakeCallback.waitForHandshake();
+ EXPECT_NE(handshakeCallback.errorString_.find("SSL routines"),
+ std::string::npos);
+ EXPECT_NE(handshakeCallback.errorString_.find("unknown protocol"),
+ std::string::npos);
+}
+
+#if FOLLY_ALLOW_TFO
+
+class MockAsyncTFOSSLSocket : public AsyncSSLSocket {
+ public:
+ using UniquePtr = std::unique_ptr<MockAsyncTFOSSLSocket, Destructor>;
+
+ explicit MockAsyncTFOSSLSocket(
+ std::shared_ptr<folly::SSLContext> sslCtx,
+ EventBase* evb)
+ : AsyncSocket(evb), AsyncSSLSocket(sslCtx, evb) {}
+
+ MOCK_METHOD3(tfoSendMsg, ssize_t(int fd, struct msghdr* msg, int msg_flags));
+};
+
+/**
+ * Test connecting to, writing to, reading from, and closing the
+ * connection to the SSL server with TFO.
+ */
+TEST(AsyncSSLSocketTest, ConnectWriteReadCloseTFO) {
+ // Start listening on a local port
+ WriteCallbackBase writeCallback;
+ ReadCallback readCallback(&writeCallback);
+ HandshakeCallback handshakeCallback(&readCallback);
+ SSLServerAcceptCallback acceptCallback(&handshakeCallback);
+ TestSSLServer server(&acceptCallback, true);
+
+ // Set up SSL context.
+ auto sslContext = std::make_shared<SSLContext>();
+
+ // connect
+ auto socket =
+ std::make_shared<BlockingSocket>(server.getAddress(), sslContext);
+ socket->enableTFO();
+ socket->open();
+
+ // write()
+ std::array<uint8_t, 128> buf;
+ memset(buf.data(), 'a', buf.size());
+ socket->write(buf.data(), buf.size());
+
+ // read()
+ std::array<uint8_t, 128> readbuf;
+ uint32_t bytesRead = socket->readAll(readbuf.data(), readbuf.size());
+ EXPECT_EQ(bytesRead, 128);
+ EXPECT_EQ(memcmp(buf.data(), readbuf.data(), bytesRead), 0);
+
+ // close()
+ socket->close();
+}
+
+/**
+ * Test connecting to, writing to, reading from, and closing the
+ * connection to the SSL server with TFO.
+ */
+TEST(AsyncSSLSocketTest, ConnectWriteReadCloseTFOWithTFOServerDisabled) {
+ // Start listening on a local port
+ WriteCallbackBase writeCallback;
+ ReadCallback readCallback(&writeCallback);
+ HandshakeCallback handshakeCallback(&readCallback);
+ SSLServerAcceptCallback acceptCallback(&handshakeCallback);
+ TestSSLServer server(&acceptCallback, false);
+
+ // Set up SSL context.
+ auto sslContext = std::make_shared<SSLContext>();
+
+ // connect
+ auto socket =
+ std::make_shared<BlockingSocket>(server.getAddress(), sslContext);
+ socket->enableTFO();
+ socket->open();
+
+ // write()
+ std::array<uint8_t, 128> buf;
+ memset(buf.data(), 'a', buf.size());
+ socket->write(buf.data(), buf.size());
+
+ // read()
+ std::array<uint8_t, 128> readbuf;
+ uint32_t bytesRead = socket->readAll(readbuf.data(), readbuf.size());
+ EXPECT_EQ(bytesRead, 128);
+ EXPECT_EQ(memcmp(buf.data(), readbuf.data(), bytesRead), 0);
+
+ // close()
+ socket->close();
+}
+
+class ConnCallback : public AsyncSocket::ConnectCallback {
+ public:
+ virtual void connectSuccess() noexcept override {
+ state = State::SUCCESS;
+ }
+
+ virtual void connectErr(const AsyncSocketException&) noexcept override {
+ state = State::ERROR;
+ }
+
+ enum class State { WAITING, SUCCESS, ERROR };
+
+ State state{State::WAITING};
+};
+
+template <class Cardinality>
+MockAsyncTFOSSLSocket::UniquePtr setupSocketWithFallback(
+ EventBase* evb,
+ const SocketAddress& address,
+ Cardinality cardinality) {
+ // Set up SSL context.
+ auto sslContext = std::make_shared<SSLContext>();
+
+ // connect
+ auto socket = MockAsyncTFOSSLSocket::UniquePtr(
+ new MockAsyncTFOSSLSocket(sslContext, evb));
+ socket->enableTFO();
+
+ EXPECT_CALL(*socket, tfoSendMsg(_, _, _))
+ .Times(cardinality)
+ .WillOnce(Invoke([&](int fd, struct msghdr*, int) {
+ sockaddr_storage addr;
+ auto len = address.getAddress(&addr);
+ return connect(fd, (const struct sockaddr*)&addr, len);
+ }));
+ return socket;
+}
+
+TEST(AsyncSSLSocketTest, ConnectWriteReadCloseTFOFallback) {
+ // Start listening on a local port
+ WriteCallbackBase writeCallback;
+ ReadCallback readCallback(&writeCallback);
+ HandshakeCallback handshakeCallback(&readCallback);
+ SSLServerAcceptCallback acceptCallback(&handshakeCallback);
+ TestSSLServer server(&acceptCallback, true);
+
+ EventBase evb;
+
+ auto socket = setupSocketWithFallback(&evb, server.getAddress(), 1);
+ ConnCallback ccb;
+ socket->connect(&ccb, server.getAddress(), 30);
+
+ evb.loop();
+ EXPECT_EQ(ConnCallback::State::SUCCESS, ccb.state);
+
+ evb.runInEventBaseThread([&] { socket->detachEventBase(); });
+ evb.loop();
+
+ BlockingSocket sock(std::move(socket));
+ // write()
+ std::array<uint8_t, 128> buf;
+ memset(buf.data(), 'a', buf.size());
+ sock.write(buf.data(), buf.size());
+
+ // read()
+ std::array<uint8_t, 128> readbuf;
+ uint32_t bytesRead = sock.readAll(readbuf.data(), readbuf.size());
+ EXPECT_EQ(bytesRead, 128);
+ EXPECT_EQ(memcmp(buf.data(), readbuf.data(), bytesRead), 0);
+
+ // close()
+ sock.close();
+}
+
+TEST(AsyncSSLSocketTest, ConnectTFOTimeout) {
+ // Start listening on a local port
+ ConnectTimeoutCallback acceptCallback;
+ TestSSLServer server(&acceptCallback, true);
+
+ // Set up SSL context.
+ auto sslContext = std::make_shared<SSLContext>();
+
+ // connect
+ auto socket =
+ std::make_shared<BlockingSocket>(server.getAddress(), sslContext);
+ socket->enableTFO();
+ EXPECT_THROW(
+ socket->open(std::chrono::milliseconds(1)), AsyncSocketException);
+}
+
+TEST(AsyncSSLSocketTest, ConnectTFOFallbackTimeout) {
+ // Start listening on a local port
+ ConnectTimeoutCallback acceptCallback;
+ TestSSLServer server(&acceptCallback, true);
+
+ EventBase evb;
+
+ auto socket = setupSocketWithFallback(&evb, server.getAddress(), AtMost(1));
+ ConnCallback ccb;
+ // Set a short timeout
+ socket->connect(&ccb, server.getAddress(), 1);
+
+ evb.loop();
+ EXPECT_EQ(ConnCallback::State::ERROR, ccb.state);
+}
+
+#endif
+