From adc966e14fadb178cd51a5ed668684221bd92ac4 Mon Sep 17 00:00:00 2001 From: Cameron Pickett Date: Tue, 16 Dec 2014 13:02:26 -0800 Subject: [PATCH] Add specific multi-bind to AsyncServerSocket Summary: Add a bind api similar to binding by port across all interfaces. However, this bind will only attempt to bind sockets to the supplied IP addresses. I'd like to add this to aid in moving TURN to IPv6. TURN will use two specified addresses, one for IPv4, and one for IPv6, to handle incoming connections from clients. In order to avoid duplicating workers, we'd like to take advantage of the multi-socket implementation of AsyncServerSocket. However, we don't want to bind to all interfaces, especially for port 443. Test Plan: Plan to test by adapting TurnTcpListener to use this new API. See that 1. using one address still works as expected, 2. using zero addesses will cause exception 3. using multiple addresses works as expected Will write unit tests if needed Reviewed By: davejwatson@fb.com Subscribers: hannesr, trunkagent, net-systems@, folly-diffs@, naizhi FB internal diff: D1729442 Tasks: 3633642 Signature: t1:1729442:1418752467:22a60da4ec9009ea0b7fe28a8a436a179e0449aa --- folly/io/async/AsyncServerSocket.cpp | 59 ++++++++++++++++++++-------- folly/io/async/AsyncServerSocket.h | 12 ++++++ 2 files changed, 54 insertions(+), 17 deletions(-) diff --git a/folly/io/async/AsyncServerSocket.cpp b/folly/io/async/AsyncServerSocket.cpp index 7482ae40..ea2a46d9 100644 --- a/folly/io/async/AsyncServerSocket.cpp +++ b/folly/io/async/AsyncServerSocket.cpp @@ -273,6 +273,29 @@ void AsyncServerSocket::useExistingSocket(int fd) { useExistingSockets({fd}); } +void AsyncServerSocket::bindSocket( + int fd, + const SocketAddress& address, + bool isExistingSocket) { + sockaddr_storage addrStorage; + address.getAddress(&addrStorage); + sockaddr* saddr = reinterpret_cast(&addrStorage); + if (::bind(fd, saddr, address.getActualSize()) != 0) { + if (!isExistingSocket) { + ::close(fd); + } + folly::throwSystemError(errno, + "failed to bind to async server socket: " + + address.describe()); + } + + // If we just created this socket, update the EventHandler and set socket_ + if (!isExistingSocket) { + sockets_.push_back( + ServerEventHandler(eventBase_, fd, this, address.getFamily())); + } +} + void AsyncServerSocket::bind(const SocketAddress& address) { assert(eventBase_ == nullptr || eventBase_->isInEventBaseThread()); @@ -295,27 +318,29 @@ void AsyncServerSocket::bind(const SocketAddress& address) { "Attempted to bind to multiple fds"); } - // Bind to the socket - sockaddr_storage addrStorage; - address.getAddress(&addrStorage); - sockaddr* saddr = reinterpret_cast(&addrStorage); - if (::bind(fd, saddr, address.getActualSize()) != 0) { - if (sockets_.size() == 0) { - ::close(fd); - } - folly::throwSystemError(errno, - "failed to bind to async server socket: " + - address.describe()); + bindSocket(fd, address, !sockets_.empty()); +} + +void AsyncServerSocket::bind( + const std::vector& ipAddresses, + uint16_t port) { + if (ipAddresses.empty()) { + throw std::invalid_argument("No ip addresses were provided"); + } + if (!sockets_.empty()) { + throw std::invalid_argument("Cannot call bind on a AsyncServerSocket " + "that already has a socket."); } - // Record the address family that we are using, - // so we know how much address space we need to record accepted addresses. + for (const IPAddress& ipAddress : ipAddresses) { + SocketAddress address(ipAddress.toFullyQualified(), port); + int fd = createSocket(address.getFamily()); - // If we just created this socket, update the EventHandler and set socket_ + bindSocket(fd, address, false); + } if (sockets_.size() == 0) { - sockets_.push_back( - ServerEventHandler(eventBase_, fd, this, address.getFamily())); - sockets_[0].changeHandlerFD(fd); + throw std::runtime_error( + "did not bind any async server socket for port and addresses"); } } diff --git a/folly/io/async/AsyncServerSocket.h b/folly/io/async/AsyncServerSocket.h index 72c14a79..a932f4e4 100644 --- a/folly/io/async/AsyncServerSocket.h +++ b/folly/io/async/AsyncServerSocket.h @@ -255,6 +255,17 @@ class AsyncServerSocket : public DelayedDestruction { */ virtual void bind(const SocketAddress& address); + /** + * Bind to the specified port for the specified addresses. + * + * This must be called from the primary EventBase thread. + * + * Throws TTransportException on error. + */ + virtual void bind( + const std::vector& ipAddresses, + uint16_t port); + /** * Bind to the specified port. * @@ -639,6 +650,7 @@ class AsyncServerSocket : public DelayedDestruction { int createSocket(int family); void setupSocket(int fd); + void bindSocket(int fd, const SocketAddress& address, bool isExistingSocket); void dispatchSocket(int socket, SocketAddress&& address); void dispatchError(const char *msg, int errnoValue); void enterBackoff(); -- 2.34.1