More useful logging upon failed AsyncServerSocket::bind()
authorMarcelo Juchem <marcelo.juchem@fb.com>
Tue, 3 May 2016 22:35:30 +0000 (15:35 -0700)
committerFacebook Github Bot 8 <facebook-github-bot-8-bot@fb.com>
Tue, 3 May 2016 22:50:22 +0000 (15:50 -0700)
Summary: `AsyncServerSocket::bind()` would not give information like port or family name when failing to bind a socket. This diff addresses that by including this information in the exception. Two additional helper methods were added to `SocketAddress` to retrieve both the port and the family name from a `sockaddr` structure.

Reviewed By: ckwalsh, yfeldblum

Differential Revision: D3249778

fb-gh-sync-id: 4edb28af5c211b7bf8d525b40844a5b0b6261e07
fbshipit-source-id: 4edb28af5c211b7bf8d525b40844a5b0b6261e07

folly/SocketAddress.cpp
folly/SocketAddress.h
folly/io/async/AsyncServerSocket.cpp
folly/io/async/test/AsyncSocketTest.cpp

index 5f63d8e0420f83ad512bcd33dee95f7a0746301c..dbeff61bf958dbc32666457d17b5bb4566764460 100644 (file)
@@ -179,6 +179,39 @@ void SocketAddress::setFromHostPort(const char* hostAndPort) {
   setFromAddrInfo(results.info);
 }
 
+int SocketAddress::getPortFrom(const struct sockaddr* address) {
+  switch (address->sa_family) {
+    case AF_INET:
+      return ntohs(((sockaddr_in*)address)->sin_port);
+
+    case AF_INET6:
+      return ntohs(((sockaddr_in6*)address)->sin6_port);
+
+    default:
+      return -1;
+  }
+}
+
+const char* SocketAddress::getFamilyNameFrom(
+    const struct sockaddr* address,
+    const char* defaultResult) {
+#define GETFAMILYNAMEFROM_IMPL(Family) \
+  case Family:                         \
+    return #Family
+
+  switch (address->sa_family) {
+    GETFAMILYNAMEFROM_IMPL(AF_INET);
+    GETFAMILYNAMEFROM_IMPL(AF_INET6);
+    GETFAMILYNAMEFROM_IMPL(AF_UNIX);
+    GETFAMILYNAMEFROM_IMPL(AF_UNSPEC);
+
+    default:
+      return defaultResult;
+  }
+
+#undef GETFAMILYNAMEFROM_IMPL
+}
+
 void SocketAddress::setFromPath(StringPiece path) {
   // Before we touch storage_, check to see if the length is too big.
   // Note that "storage_.un.addr->sun_path" may not be safe to evaluate here,
index 12d8cae7ffb7be85232cec0758042b29a4dbc5fb..1953e2135574ee1aecd0d6796394d3dd9230e09c 100644 (file)
@@ -274,6 +274,25 @@ class SocketAddress {
     return setFromHostPort(hostAndPort.c_str());
   }
 
+  /**
+   * Returns the port number from the given socketaddr structure.
+   *
+   * Currently only IPv4 and IPv6 are supported.
+   *
+   * Returns -1 for unsupported socket families.
+   */
+  static int getPortFrom(const struct sockaddr* address);
+
+  /**
+   * Returns the family name from the given socketaddr structure (e.g.: AF_INET6
+   * for IPv6).
+   *
+   * Returns `defaultResult` for unsupported socket families.
+   */
+  static const char* getFamilyNameFrom(
+      const struct sockaddr* address,
+      const char* defaultResult = nullptr);
+
   /**
    * Initialize this SocketAddress from a local unix path.
    *
index 49ece5ef0f6e7f3a5fa77a23686753f9328e507c..fccf662d745c4c52d462788de1faa6a7a1aa7f43 100644 (file)
@@ -397,8 +397,11 @@ void AsyncServerSocket::bind(uint16_t port) {
     // Bind to the socket
     if (::bind(s, res->ai_addr, res->ai_addrlen) != 0) {
       folly::throwSystemError(
-        errno,
-        "failed to bind to async server socket for port");
+          errno,
+          "failed to bind to async server socket for port ",
+          SocketAddress::getPortFrom(res->ai_addr),
+          " family ",
+          SocketAddress::getFamilyNameFrom(res->ai_addr, "<unknown>"));
     }
   };
 
index c339feff4dad3d66dbeced2e1e75deba09785ad0..bd348bd9c446a28b7f6829d7da0b0e74b862c662 100644 (file)
@@ -76,4 +76,17 @@ TEST(AsyncSocketTest, v4v6samePort) {
   }
 }
 
+TEST(AsyncSocketTest, duplicateBind) {
+  EventBase base;
+  auto server1 = AsyncServerSocket::newSocket(&base);
+  server1->bind(0);
+  server1->listen(10);
+
+  SocketAddress address;
+  server1->getAddress(std::addressof(address));
+
+  auto server2 = AsyncServerSocket::newSocket(&base);
+  EXPECT_THROW(server2->bind(address.getPort()), std::exception);
+}
+
 } // namespace