diff --git a/folly/io/async/AsyncServerSocket.cpp b/folly/io/async/AsyncServerSocket.cpp
index 67c425e2117..c06b0e73b21 100644
--- a/folly/io/async/AsyncServerSocket.cpp
+++ b/folly/io/async/AsyncServerSocket.cpp
@@ -568,6 +568,26 @@ void AsyncServerSocket::bind(uint16_t port) {
}
}
+void AsyncServerSocket::setEnableReuseAddr(bool enable) {
+ enableReuseAddr_ = enable;
+ for (auto& handler : sockets_) {
+ if (handler.socket_ == NetworkSocket()) {
+ continue;
+ }
+
+ int val = (enable) ? 1 : 0;
+ if (netops::setsockopt(
+ handler.socket_, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) !=
+ 0) {
+ auto errnoCopy = errno;
+ LOG(ERROR) << "failed to set SO_REUSEADDR on async server socket "
+ << errnoCopy;
+ folly::throwSystemErrorExplicit(
+ errnoCopy, "failed to set SO_REUSEADDR on async server socket");
+ }
+ }
+}
+
void AsyncServerSocket::listen(int backlog) {
if (eventBase_) {
eventBase_->dcheckIsInEventBaseThread();
@@ -830,7 +850,7 @@ void AsyncServerSocket::setupSocket(NetworkSocket fd, int family) {
// Set reuseaddr to avoid 2MSL delay on server restart
int one = 1;
// AF_UNIX does not support SO_REUSEADDR, setting this would confuse Windows
- if (family != AF_UNIX &&
+ if (family != AF_UNIX && enableReuseAddr_ &&
netops::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) !=
0) {
auto errnoCopy = errno;
diff --git a/folly/io/async/AsyncServerSocket.h b/folly/io/async/AsyncServerSocket.h
index 4b845cd31ab..b8d22a236e5 100644
--- a/folly/io/async/AsyncServerSocket.h
+++ b/folly/io/async/AsyncServerSocket.h
@@ -765,6 +765,13 @@ class AsyncServerSocket : public DelayedDestruction, public AsyncSocketBase {
}
}
+ /**
+ * Set whether or not SO_REUSEADDR should be enabled on the server socket,
+ * allowing multiple sockets binds to the same
:
+ * It's enabled by default.
+ */
+ void setEnableReuseAddr(bool enable);
+
/**
* Get whether or not SO_REUSEPORT is enabled on the server socket.
*/
@@ -1004,6 +1011,8 @@ class AsyncServerSocket : public DelayedDestruction, public AsyncSocketBase {
CallbackAssignFunction callbackAssignFunc_;
bool keepAliveEnabled_;
bool reusePortEnabled_{false};
+ // SO_REUSEADDR is enabled by default
+ bool enableReuseAddr_{true};
bool closeOnExec_;
bool tfo_{false};
bool noTransparentTls_{false};
diff --git a/folly/io/async/test/AsyncSocketTest.cpp b/folly/io/async/test/AsyncSocketTest.cpp
index f038eefc05d..79d2455a16a 100644
--- a/folly/io/async/test/AsyncSocketTest.cpp
+++ b/folly/io/async/test/AsyncSocketTest.cpp
@@ -69,6 +69,49 @@ TEST(AsyncSocketTest, REUSEPORT) {
serverSocket2->startAccepting();
}
+TEST(AsyncSocketTest, DisableReuseAddr) {
+ EventBase base;
+ auto serverSocket = AsyncServerSocket::newSocket(&base);
+ serverSocket->setEnableReuseAddr(false /* enable */);
+ // idempotent
+ serverSocket->setEnableReuseAddr(false /* enable */);
+ serverSocket->setEnableReuseAddr(false /* enable */);
+ serverSocket->bind(0);
+
+ SocketAddress address;
+ serverSocket->getAddress(&address);
+ int port = address.getPort();
+
+ auto serverSocket2 = AsyncServerSocket::newSocket(&base);
+ serverSocket2->setEnableReuseAddr(false /* enable */);
+ // idempotent
+ serverSocket2->setEnableReuseAddr(false /* enable */);
+ serverSocket2->setEnableReuseAddr(false /* enable */);
+ EXPECT_THROW(serverSocket2->bind(port), std::system_error);
+ // it's ok to bind to a different port
+ serverSocket2->bind(0);
+}
+
+TEST(AsyncSocketTest, EnableThenDisableReuseAddr) {
+ EventBase base;
+ auto serverSocket = AsyncServerSocket::newSocket(&base);
+ serverSocket->bind(0);
+
+ SocketAddress address;
+ serverSocket->getAddress(&address);
+ int port = address.getPort();
+
+ auto serverSocket2 = AsyncServerSocket::newSocket(&base);
+ // defaulty SO_REUSEADDR enabled so can bind to same port
+ serverSocket2->bind(port);
+ serverSocket2->setEnableReuseAddr(false /* enable */);
+ serverSocket->setEnableReuseAddr(false /* enable */);
+
+ EXPECT_THROW(serverSocket2->bind(port), std::system_error);
+ // it's ok to bind to a different port
+ serverSocket2->bind(0);
+}
+
TEST(AsyncSocketTest, v4v6samePort) {
EventBase base;
auto serverSocket = AsyncServerSocket::newSocket(&base);