AsyncResolve cleanups and error handling

The Async DBus resolver really has nothing to do with crow, which is our
core http library namespace and has some opportunistic cleanups that can
be done.

This commit moves it into the bmcweb namespace (unimportantly) and
breaks out one of the larger functions such that it can be unit tested,
and unit tests it.

Tested: Unit tests pass.

Signed-off-by: Ed Tanous <edtanous@google.com>
Change-Id: Ie3cfbb0ef81a027a1ad42358c04967a517471117
diff --git a/http/http_client.hpp b/http/http_client.hpp
index 1533875..07fa85d 100644
--- a/http/http_client.hpp
+++ b/http/http_client.hpp
@@ -148,7 +148,7 @@
     std::function<void(bool, uint32_t, Response&)> callback;
 
 #ifdef BMCWEB_DBUS_DNS_RESOLVER
-    using Resolver = crow::async_resolve::Resolver;
+    using Resolver = async_resolve::Resolver;
 #else
     using Resolver = boost::asio::ip::tcp::resolver;
 #endif
diff --git a/include/async_resolve.hpp b/include/async_resolve.hpp
index 5e526ca..2bf5112 100644
--- a/include/async_resolve.hpp
+++ b/include/async_resolve.hpp
@@ -12,12 +12,37 @@
 #include <iostream>
 #include <memory>
 
-namespace crow
-{
-
 namespace async_resolve
 {
 
+inline bool endpointFromResolveTuple(const std::vector<uint8_t>& ipAddress,
+                                     boost::asio::ip::tcp::endpoint& endpoint)
+{
+    if (ipAddress.size() == 4) // ipv4 address
+    {
+        BMCWEB_LOG_DEBUG << "ipv4 address";
+        boost::asio::ip::address_v4 ipv4Addr(
+            {ipAddress[0], ipAddress[1], ipAddress[2], ipAddress[3]});
+        endpoint.address(ipv4Addr);
+    }
+    else if (ipAddress.size() == 16) // ipv6 address
+    {
+        BMCWEB_LOG_DEBUG << "ipv6 address";
+        boost::asio::ip::address_v6 ipv6Addr(
+            {ipAddress[0], ipAddress[1], ipAddress[2], ipAddress[3],
+             ipAddress[4], ipAddress[5], ipAddress[6], ipAddress[7],
+             ipAddress[8], ipAddress[9], ipAddress[10], ipAddress[11],
+             ipAddress[12], ipAddress[13], ipAddress[14], ipAddress[15]});
+        endpoint.address(ipv6Addr);
+    }
+    else
+    {
+        BMCWEB_LOG_ERROR << "Resolve failed to fetch the IP address";
+        return false;
+    }
+    return true;
+}
+
 class Resolver
 {
   public:
@@ -75,36 +100,15 @@
             for (const std::tuple<int32_t, int32_t, std::vector<uint8_t>>&
                      resolveList : resp)
             {
-                const std::vector<uint8_t>& ipAddress =
-                    std::get<2>(resolveList);
                 boost::asio::ip::tcp::endpoint endpoint;
-                if (ipAddress.size() == 4) // ipv4 address
-                {
-                    BMCWEB_LOG_DEBUG << "ipv4 address";
-                    boost::asio::ip::address_v4 ipv4Addr(
-                        {ipAddress[0], ipAddress[1], ipAddress[2],
-                         ipAddress[3]});
-                    endpoint.address(ipv4Addr);
-                }
-                else if (ipAddress.size() == 16) // ipv6 address
-                {
-                    BMCWEB_LOG_DEBUG << "ipv6 address";
-                    boost::asio::ip::address_v6 ipv6Addr(
-                        {ipAddress[0], ipAddress[1], ipAddress[2], ipAddress[3],
-                         ipAddress[4], ipAddress[5], ipAddress[6], ipAddress[7],
-                         ipAddress[8], ipAddress[9], ipAddress[10],
-                         ipAddress[11], ipAddress[12], ipAddress[13],
-                         ipAddress[14], ipAddress[15]});
-                    endpoint.address(ipv6Addr);
-                }
-                else
-                {
-                    BMCWEB_LOG_ERROR
-                        << "Resolve failed to fetch the IP address";
-                    handler(ec, endpointList);
-                    return;
-                }
                 endpoint.port(portNum);
+                if (!endpointFromResolveTuple(std::get<2>(resolveList),
+                                              endpoint))
+                {
+                    boost::system::error_code ecErr = make_error_code(
+                        boost::system::errc::address_not_available);
+                    handler(ecErr, endpointList);
+                }
                 BMCWEB_LOG_DEBUG << "resolved endpoint is : " << endpoint;
                 endpointList.push_back(endpoint);
             }
@@ -118,5 +122,4 @@
 };
 
 } // namespace async_resolve
-} // namespace crow
 #endif
diff --git a/meson.build b/meson.build
index e9716be..fcc0d7c 100644
--- a/meson.build
+++ b/meson.build
@@ -387,6 +387,7 @@
   'test/include/json_html_serializer.cpp',
   'test/include/http_utility_test.cpp',
   'test/include/human_sort_test.cpp',
+  'test/include/async_resolve_test.cpp',
   'test/include/ibm/configfile_test.cpp',
   'test/include/ibm/lock_test.cpp',
   'test/include/multipart_test.cpp',
diff --git a/test/include/async_resolve_test.cpp b/test/include/async_resolve_test.cpp
new file mode 100644
index 0000000..be1e8a3
--- /dev/null
+++ b/test/include/async_resolve_test.cpp
@@ -0,0 +1,25 @@
+#include "async_resolve.hpp"
+
+#include <boost/asio/ip/tcp.hpp>
+
+#include <gmock/gmock.h>
+
+TEST(AsyncResolve, ipv4Positive)
+{
+    boost::asio::ip::tcp::endpoint ep;
+    ASSERT_TRUE(async_resolve::endpointFromResolveTuple({1, 2, 3, 4}, ep));
+    EXPECT_TRUE(ep.address().is_v4());
+    EXPECT_FALSE(ep.address().is_v6());
+
+    EXPECT_EQ(ep.address().to_string(), "1.2.3.4");
+}
+
+TEST(AsyncResolve, ipv6Positive)
+{
+    boost::asio::ip::tcp::endpoint ep;
+    ASSERT_TRUE(async_resolve::endpointFromResolveTuple(
+        {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, ep));
+    EXPECT_FALSE(ep.address().is_v4());
+    EXPECT_TRUE(ep.address().is_v6());
+    EXPECT_EQ(ep.address().to_string(), "102:304:506:708:90a:b0c:d0e:f10");
+}