};
AsyncSocket::AsyncSocket()
- : eventBase_(nullptr)
- , writeTimeout_(this, nullptr)
- , ioHandler_(this, nullptr)
- , immediateReadHandler_(this) {
+ : eventBase_(nullptr),
+ writeTimeout_(this, nullptr),
+ ioHandler_(this, nullptr),
+ immediateReadHandler_(this) {
VLOG(5) << "new AsyncSocket()";
init();
}
AsyncSocket::AsyncSocket(EventBase* evb)
- : eventBase_(evb)
- , writeTimeout_(this, evb)
- , ioHandler_(this, evb)
- , immediateReadHandler_(this) {
+ : eventBase_(evb),
+ writeTimeout_(this, evb),
+ ioHandler_(this, evb),
+ immediateReadHandler_(this) {
VLOG(5) << "new AsyncSocket(" << this << ", evb=" << evb << ")";
init();
}
}
AsyncSocket::AsyncSocket(EventBase* evb, int fd)
- : eventBase_(evb)
- , writeTimeout_(this, evb)
- , ioHandler_(this, evb, fd)
- , immediateReadHandler_(this) {
+ : eventBase_(evb),
+ writeTimeout_(this, evb),
+ ioHandler_(this, evb, fd),
+ immediateReadHandler_(this) {
VLOG(5) << "new AsyncSocket(" << this << ", evb=" << evb << ", fd="
<< fd << ")";
init();
// one here just to make sure, in case one of our calling code paths ever
// changes.
DestructorGuard dg(this);
-
// If we have a readCallback_, make sure we enable read events. We
// may already be registered for reads if connectSuccess() set
// the read calback.
if (totalWritten >= 0) {
tfoFinished_ = true;
state_ = StateEnum::ESTABLISHED;
- handleInitialReadWrite();
+ // We schedule this asynchrously so that we don't end up
+ // invoking initial read or write while a write is in progress.
+ scheduleInitialReadWrite();
} else if (errno == EINPROGRESS) {
VLOG(4) << "TFO falling back to connecting";
// A normal sendmsg doesn't return EINPROGRESS, however
// connect succeeded immediately
// Treat this like no data was written.
state_ = StateEnum::ESTABLISHED;
- handleInitialReadWrite();
+ scheduleInitialReadWrite();
}
// If there was no exception during connections,
// we would return that no bytes were written.
EXPECT_EQ(0, memcmp(rcb.buffers[0].buffer, buf.data(), buf.size()));
}
+TEST(AsyncSocketTest, ConnectTFOSupplyEarlyReadCB) {
+ // Start listening on a local port
+ TestServer server(true);
+
+ // Connect using a AsyncSocket
+ EventBase evb;
+ std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);
+ socket->enableTFO();
+ ConnCallback cb;
+ socket->connect(&cb, server.getAddress(), 30);
+ ReadCallback rcb;
+ socket->setReadCB(&rcb);
+
+ std::array<uint8_t, 128> buf;
+ memset(buf.data(), 'a', buf.size());
+
+ std::array<uint8_t, 3> readBuf;
+ auto sendBuf = IOBuf::copyBuffer("hey");
+
+ std::thread t([&] {
+ auto acceptedSocket = server.accept();
+ acceptedSocket->write(buf.data(), buf.size());
+ acceptedSocket->flush();
+ acceptedSocket->readAll(readBuf.data(), readBuf.size());
+ acceptedSocket->close();
+ });
+
+ evb.loop();
+
+ CHECK_EQ(cb.state, STATE_SUCCEEDED);
+ EXPECT_LE(0, socket->getConnectTime().count());
+ EXPECT_EQ(socket->getConnectTimeout(), std::chrono::milliseconds(30));
+ EXPECT_TRUE(socket->getTFOAttempted());
+
+ // Should trigger the connect
+ WriteCallback write;
+ socket->writeChain(&write, sendBuf->clone());
+ evb.loop();
+
+ t.join();
+
+ EXPECT_EQ(STATE_SUCCEEDED, write.state);
+ EXPECT_EQ(0, memcmp(readBuf.data(), sendBuf->data(), readBuf.size()));
+ EXPECT_EQ(STATE_SUCCEEDED, rcb.state);
+ ASSERT_EQ(1, rcb.buffers.size());
+ ASSERT_EQ(sizeof(buf), rcb.buffers[0].length);
+ EXPECT_EQ(0, memcmp(rcb.buffers[0].buffer, buf.data(), buf.size()));
+}
+
/**
* Test connecting to a server that isn't listening
*/