Commit 106a2a56 authored by Kevin Lewi's avatar Kevin Lewi Committed by Facebook Github Bot 6

Adding the ability to check for whether TCP fast open succeeded on a socket

Summary: Modifies AsyncSocket to check if TFO succeeded. Also added checks to AsyncSocketTest2 to check if TFO succeeded when it should.

Reviewed By: siyengar

Differential Revision: D3540330

fbshipit-source-id: 8a4b64fdb040dea73ba264b8e3dfff4d717fd96f
parent a4657e89
...@@ -46,6 +46,18 @@ int tfo_enable(int sockfd, size_t max_queue_size) { ...@@ -46,6 +46,18 @@ int tfo_enable(int sockfd, size_t max_queue_size) {
sockfd, SOL_TCP, TCP_FASTOPEN, &max_queue_size, sizeof(max_queue_size)); sockfd, SOL_TCP, TCP_FASTOPEN, &max_queue_size, sizeof(max_queue_size));
} }
bool tfo_succeeded(int sockfd) {
// Call getsockopt to check if TFO was used.
struct tcp_info info;
socklen_t info_len = sizeof(info);
errno = 0;
if (getsockopt(sockfd, IPPROTO_TCP, TCP_INFO, &info, &info_len) != 0) {
// errno is set from getsockopt
return false;
}
return info.tcpi_options & TCPI_OPT_SYN_DATA;
}
#else #else
ssize_t tfo_sendmsg(int sockfd, const struct msghdr* msg, int flags) { ssize_t tfo_sendmsg(int sockfd, const struct msghdr* msg, int flags) {
...@@ -58,6 +70,11 @@ int tfo_enable(int sockfd, size_t max_queue_size) { ...@@ -58,6 +70,11 @@ int tfo_enable(int sockfd, size_t max_queue_size) {
return -1; return -1;
} }
bool tfo_succeeded(int sockfd) {
errno = EOPNOTSUPP;
return false;
}
#endif #endif
} }
} }
...@@ -37,5 +37,10 @@ ssize_t tfo_sendmsg(int sockfd, const struct msghdr* msg, int flags); ...@@ -37,5 +37,10 @@ ssize_t tfo_sendmsg(int sockfd, const struct msghdr* msg, int flags);
* Enable TFO on a listening socket. * Enable TFO on a listening socket.
*/ */
int tfo_enable(int sockfd, size_t max_queue_size); int tfo_enable(int sockfd, size_t max_queue_size);
/**
* Check if TFO succeeded in being used.
*/
bool tfo_succeeded(int sockfd);
} }
} }
...@@ -1762,6 +1762,18 @@ AsyncSocket::sendSocketMessage(int fd, struct msghdr* msg, int msg_flags) { ...@@ -1762,6 +1762,18 @@ AsyncSocket::sendSocketMessage(int fd, struct msghdr* msg, int msg_flags) {
msg->msg_namelen = len; msg->msg_namelen = len;
totalWritten = tfoSendMsg(fd_, msg, msg_flags); totalWritten = tfoSendMsg(fd_, msg, msg_flags);
if (totalWritten >= 0) { if (totalWritten >= 0) {
// Call tfo_succeeded to check if TFO was used.
tfoSucceeded_ = detail::tfo_succeeded(fd_);
if (errno != 0) {
auto errnoCopy = errno;
AsyncSocketException ex(
AsyncSocketException::INTERNAL_ERROR,
withAddr("error calling tfo_succeeded"),
errnoCopy);
return WriteResult(
WRITE_ERROR, folly::make_unique<AsyncSocketException>(ex));
}
tfoFinished_ = true; tfoFinished_ = true;
state_ = StateEnum::ESTABLISHED; state_ = StateEnum::ESTABLISHED;
handleInitialReadWrite(); handleInitialReadWrite();
......
...@@ -431,6 +431,14 @@ class AsyncSocket : virtual public AsyncTransportWrapper { ...@@ -431,6 +431,14 @@ class AsyncSocket : virtual public AsyncTransportWrapper {
return tfoFinished_; return tfoFinished_;
} }
/**
* Returns whether or not TFO "worked", or, succeeded
* in actually being used.
*/
bool getTFOSucceeded() const {
return tfoSucceeded_;
}
// Methods controlling socket options // Methods controlling socket options
/** /**
...@@ -899,6 +907,7 @@ class AsyncSocket : virtual public AsyncTransportWrapper { ...@@ -899,6 +907,7 @@ class AsyncSocket : virtual public AsyncTransportWrapper {
bool tfoEnabled_{false}; bool tfoEnabled_{false};
bool tfoAttempted_{false}; bool tfoAttempted_{false};
bool tfoFinished_{false}; bool tfoFinished_{false};
bool tfoSucceeded_{false};
}; };
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma vtordisp(pop) #pragma vtordisp(pop)
......
...@@ -2406,6 +2406,7 @@ TEST(AsyncSocketTest, ConnectTFO) { ...@@ -2406,6 +2406,7 @@ TEST(AsyncSocketTest, ConnectTFO) {
ASSERT_EQ(1, rcb.buffers.size()); ASSERT_EQ(1, rcb.buffers.size());
ASSERT_EQ(sizeof(buf), rcb.buffers[0].length); ASSERT_EQ(sizeof(buf), rcb.buffers[0].length);
EXPECT_EQ(0, memcmp(rcb.buffers[0].buffer, buf.data(), buf.size())); EXPECT_EQ(0, memcmp(rcb.buffers[0].buffer, buf.data(), buf.size()));
EXPECT_EQ(socket->getTFOSucceeded(), socket->getTFOFinished());
} }
/** /**
...@@ -2473,6 +2474,7 @@ TEST(AsyncSocketTest, ConnectWriteAndCloseNowTFO) { ...@@ -2473,6 +2474,7 @@ TEST(AsyncSocketTest, ConnectWriteAndCloseNowTFO) {
// Loop, although there shouldn't be anything to do. // Loop, although there shouldn't be anything to do.
evb.loop(); evb.loop();
EXPECT_EQ(socket->getTFOSucceeded(), socket->getTFOFinished());
CHECK_EQ(ccb.state, STATE_SUCCEEDED); CHECK_EQ(ccb.state, STATE_SUCCEEDED);
ASSERT_TRUE(socket->isClosedBySelf()); ASSERT_TRUE(socket->isClosedBySelf());
...@@ -2500,6 +2502,7 @@ TEST(AsyncSocketTest, ConnectAndCloseTFO) { ...@@ -2500,6 +2502,7 @@ TEST(AsyncSocketTest, ConnectAndCloseTFO) {
// Make sure the connection was aborted // Make sure the connection was aborted
CHECK_EQ(ccb.state, STATE_SUCCEEDED); CHECK_EQ(ccb.state, STATE_SUCCEEDED);
EXPECT_EQ(socket->getTFOSucceeded(), socket->getTFOFinished());
ASSERT_TRUE(socket->isClosedBySelf()); ASSERT_TRUE(socket->isClosedBySelf());
ASSERT_FALSE(socket->isClosedByPeer()); ASSERT_FALSE(socket->isClosedByPeer());
...@@ -2760,6 +2763,7 @@ TEST(AsyncSocketTest, ConnectTFOWithBigData) { ...@@ -2760,6 +2763,7 @@ TEST(AsyncSocketTest, ConnectTFOWithBigData) {
t.join(); t.join();
EXPECT_EQ(socket->getTFOSucceeded(), socket->getTFOFinished());
EXPECT_EQ(STATE_SUCCEEDED, write.state); EXPECT_EQ(STATE_SUCCEEDED, write.state);
EXPECT_EQ(0, memcmp(readBuf.data(), sendBuf->data(), readBuf.size())); EXPECT_EQ(0, memcmp(readBuf.data(), sendBuf->data(), readBuf.size()));
EXPECT_EQ(STATE_SUCCEEDED, rcb.state); EXPECT_EQ(STATE_SUCCEEDED, rcb.state);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment