Add specific multi-bind to AsyncServerSocket
authorCameron Pickett <pickett@fb.com>
Tue, 16 Dec 2014 21:02:26 +0000 (13:02 -0800)
committerJoelMarcey <joelm@fb.com>
Thu, 18 Dec 2014 20:29:40 +0000 (12:29 -0800)
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
folly/io/async/AsyncServerSocket.h

index 7482ae4060f646dfa7361856fbfde7d009713b21..ea2a46d9025a282e723b74137c8f584d66711467 100644 (file)
@@ -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<sockaddr*>(&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<sockaddr*>(&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<IPAddress>& 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");
   }
 }
 
index 72c14a79cecee8dc32c6bdbcc3dbf5c7fb821964..a932f4e4d1ad43f9a98a6685cf814ac42c969721 100644 (file)
@@ -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<IPAddress>& 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();