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);