Repository: romange/helio
Pull request #391: fix: ipv6 support updated
Author: login: vyavdoshenko
Created: 2025-03-12T16:06:13Z | Merged: 2025-03-14T18:23:59Z

fix: ipv6 support updated
docs: The original issue is here: https://github.com/dragonflydb/dragonfly/issues/4622


Diff:
diff --git a/examples/echo_server.cc b/examples/echo_server.cc
index dc668920..dff0f80b 100644
--- a/examples/echo_server.cc
+++ b/examples/echo_server.cc
@@ -61,6 +61,7 @@ ABSL_FLAG(bool, multishot, false, "If true, iouring sockets use multishot receiv
 ABSL_FLAG(uint16_t, bufring_size, 256, "Size of the buffer ring for iouring sockets");
 ABSL_FLAG(bool, use_incoming_cpu, false,
           "If true uses SO_INCOMING_CPU in order to distribute incoming connections");
+ABSL_FLAG(bool, ipv6, false, "If true, use ipv6 for server");
 
 VarzQps ping_qps("ping-qps");
 VarzCount connections("connections");
@@ -322,10 +323,17 @@ void RunServer(ProactorPool* pp) {
   AcceptServer acceptor(pp);
   acceptor.set_back_log(GetFlag(FLAGS_backlog));
 
-  acceptor.AddListener(GetFlag(FLAGS_port), new EchoListener);
+  char bind_addr[INET6_ADDRSTRLEN];
+  if (GetFlag(FLAGS_ipv6)) {
+    strcpy(bind_addr, "::");
+  } else {
+    strcpy(bind_addr, "0.0.0.0");
+  }
+  acceptor.AddListener(bind_addr, GetFlag(FLAGS_port), new EchoListener);
   if (GetFlag(FLAGS_http_port) >= 0) {
-    uint16_t port = acceptor.AddListener(GetFlag(FLAGS_http_port), new HttpListener<>);
-    LOG(INFO) << "Started http server on port " << port;
+    error_code ec = acceptor.AddListener(bind_addr, GetFlag(FLAGS_http_port), new HttpListener<>);
+    CHECK(!ec) << "Could not open port " << GetFlag(FLAGS_http_port) << " " << ec;
+    LOG(INFO) << "Started http server on port " << GetFlag(FLAGS_http_port);
   }
 
   acceptor.Run();
diff --git a/examples/pingserver/ping_iouring_server.cc b/examples/pingserver/ping_iouring_server.cc
index a2a6eff3..3335700e 100644
--- a/examples/pingserver/ping_iouring_server.cc
+++ b/examples/pingserver/ping_iouring_server.cc
@@ -35,6 +35,7 @@ ABSL_FLAG(bool, use_incoming_cpu, false,
 ABSL_FLAG(string, tls_cert, "", "");
 ABSL_FLAG(string, tls_key, "", "");
 ABSL_FLAG(string, unixsocket, "", "");
+ABSL_FLAG(bool, ipv6, false, "If true, use ipv6 for server");
 
 VarzQps ping_qps("ping-qps");
 
@@ -246,18 +247,27 @@ int main(int argc, char* argv[]) {
   AcceptServer acceptor(pp.get());
   PingListener* listener = new PingListener(ctx);
 
+  char bind_addr[INET6_ADDRSTRLEN];
+  if (GetFlag(FLAGS_ipv6)) {
+    strcpy(bind_addr, "::");
+  } else {
+    strcpy(bind_addr, "0.0.0.0");
+  }
+
   if (uds.empty()) {
-    acceptor.AddListener(port, listener);
+    acceptor.AddListener(bind_addr, port, listener);
   } else {
     unlink(uds.c_str());
     error_code ec = acceptor.AddUDSListener(uds.c_str(), 0700, listener);
     CHECK(!ec) << ec;
   }
 
+
   int http_port = GetFlag(FLAGS_http_port);
   if (http_port >= 0) {
-    uint16_t port = acceptor.AddListener(http_port, new HttpListener<>);
-    LOG(INFO) << "Started http server on port " << port;
+    error_code ec = acceptor.AddListener(bind_addr, http_port, new HttpListener<>);
+    CHECK(!ec) << "Could not open port " << http_port << " " << ec;
+    LOG(INFO) << "Started http server on port " << http_port;
   }
 
   acceptor.Run();
diff --git a/examples/raw_echo_server.cc b/examples/raw_echo_server.cc
index f60f01e6..c44fa15f 100644
--- a/examples/raw_echo_server.cc
+++ b/examples/raw_echo_server.cc
@@ -26,6 +26,7 @@ ABSL_FLAG(uint16_t, threads, 0,
           "Number of connection threads. 0 to run as single threaded. "
           "Otherwise, the main thread accepts connections and worker threads are handling TCP "
           "connections");
+ABSL_FLAG(bool, ipv6, false, "Use IPv6");
 
 namespace ctx = boost::context;
 using namespace std;
@@ -377,20 +378,30 @@ void SetupIORing(io_uring* ring) {
   next_rp_id = 0;
 }
 
-int SetupListener(uint16_t port) {
-  int listen_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
+int SetupListener(uint16_t port, bool ipv6) {
+  int listen_fd = socket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
   CHECK_GE(listen_fd, 0);
 
   const int val = 1;
   CHECK_EQ(0, setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)));
 
-  sockaddr_in server_addr;
-  memset(&server_addr, 0, sizeof(server_addr));
-  server_addr.sin_family = AF_INET;
-  server_addr.sin_port = htons(port);
-  server_addr.sin_addr.s_addr = INADDR_ANY;
+  int res = 0;
+  if (ipv6) {
+    sockaddr_in6 server_addr;
+    memset(&server_addr, 0, sizeof(server_addr));
+    server_addr.sin6_family = AF_INET6;
+    server_addr.sin6_port = htons(port);
+    server_addr.sin6_addr = in6addr_any;
+    res = bind(listen_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
+  } else {
+    sockaddr_in server_addr;
+    memset(&server_addr, 0, sizeof(server_addr));
+    server_addr.sin_family = AF_INET;
+    server_addr.sin_port = htons(port);
+    server_addr.sin_addr.s_addr = INADDR_ANY;
+    res = bind(listen_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
+  }
 
-  int res = bind(listen_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
   CHECK_EQ(0, res);
   res = listen(listen_fd, 32);
   CHECK_EQ(0, res);
@@ -431,7 +442,8 @@ void WorkerThread(unsigned index) {
   fb2::SetCustomDispatcher(policy);
   if (index == 0) {
     uint16_t port = absl::GetFlag(FLAGS_port);
-    listen_fd = SetupListener(port);
+    bool ipv6 = absl::GetFlag(FLAGS_ipv6);
+    listen_fd = SetupListener(port, ipv6);
     Fiber accept_fb("accept", [&] { AcceptFiber(policy->ring(), listen_fd); });
     accept_fb.Join();
   } else {
@@ -484,7 +496,8 @@ int main(int argc, char* argv[]) {
 
   if (num_workers == 0) {
     uint16_t port = absl::GetFlag(FLAGS_port);
-    listen_fd = SetupListener(port);
+    bool ipv6 = absl::GetFlag(FLAGS_ipv6);
+    listen_fd = SetupListener(port, ipv6);
     Fiber accept_fb("accept", [&] { AcceptFiber(policy->ring(), listen_fd); });
     accept_fb.Join();
   }
diff --git a/util/accept_server_test.cc b/util/accept_server_test.cc
index c4117728..bb26dfe2 100644
--- a/util/accept_server_test.cc
+++ b/util/accept_server_test.cc
@@ -86,7 +86,7 @@ class TestListener : public ListenerInterface {
   }
 };
 
-class AcceptServerTest : public testing::Test {
+class AcceptServerTest : public testing::TestWithParam<bool> {
  protected:
   void SetUp() override;
 
@@ -101,6 +101,8 @@ class AcceptServerTest : public testing::Test {
   static void SetUpTestCase() {
   }
 
+  bool UseIPv6() const { return GetParam(); }
+
   FiberSocketBase::endpoint_type ep_;
 
   std::unique_ptr<ProactorPool> pp_;
@@ -111,6 +113,10 @@ class AcceptServerTest : public testing::Test {
   fb2::Done watchdog_done_;
 };
 
+// Add parameterization to run tests with both IPv4 and IPv6
+INSTANTIATE_TEST_SUITE_P(IPVersions, AcceptServerTest, testing::Values(false, true),
+    [](const auto& info) { return info.param ? "IPv6" : "IPv4"; });
+
 void AcceptServerTest::SetUp() {
   const uint16_t kPort = 1234;
 #if USE_URING
@@ -123,13 +129,24 @@ void AcceptServerTest::SetUp() {
 
   as_.reset(new AcceptServer{up});
   listener_ = new TestListener();
-  auto ec = as_->AddListener("localhost", kPort, listener_);
+  
+  // Add appropriate listener based on the parameter
+  auto ec = UseIPv6() 
+      ? as_->AddListener("::1", kPort, listener_)  // IPv6
+      : as_->AddListener("localhost", kPort, listener_);  // IPv4
   CHECK(!ec) << ec;
   as_->Run();
 
   ProactorBase* pb = pp_->GetNextProactor();
   client_sock_.reset(pb->CreateSocket());
-  auto address = boost::asio::ip::make_address("127.0.0.1");
+  
+  // Use IPv4 or IPv6 address based on the parameter
+  boost::asio::ip::address address;
+  if (UseIPv6()) {
+    address = boost::asio::ip::make_address("::1");  // IPv6 loopback
+  } else {
+    address = boost::asio::ip::make_address("127.0.0.1");  // IPv4 loopback
+  }
   ep_ = FiberSocketBase::endpoint_type{address, kPort};
 
   pb->Await([&] {
@@ -171,15 +188,16 @@ void RunClient(FiberSocketBase* fs) {
   LOG(INFO) << ": echo-client stopped";
 }
 
-TEST_F(AcceptServerTest, Basic) {
+TEST_P(AcceptServerTest, Basic) {
+  LOG(INFO) << "Running Basic test for " << (UseIPv6() ? "IPv6" : "IPv4");
   client_sock_->proactor()->Await([&] { RunClient(client_sock_.get()); });
 }
 
-TEST_F(AcceptServerTest, Break) {
+TEST_P(AcceptServerTest, Break) {
   usleep(1000);
 }
 
-TEST_F(AcceptServerTest, ConnectionsLimit) {
+TEST_P(AcceptServerTest, ConnectionsLimit) {
   listener_->SetMaxClients((1 << 16) - 1);
   ASSERT_EQ(listener_->GetMaxClients(), (1 << 16) - 1);
   listener_->SetMaxClients(1);
@@ -221,7 +239,7 @@ TEST_F(AcceptServerTest, ConnectionsLimit) {
   ASSERT_EQ(listener_->GetMaxClients(), (1 << 16) - 1);
 }
 
-TEST_F(AcceptServerTest, UDS) {
+TEST_P(AcceptServerTest, UDS) {
 #ifdef __APPLE__
   GTEST_SKIP() << "Skipped AcceptServerTest.UDS test on MacOS";
   return;
@@ -235,7 +253,7 @@ TEST_F(AcceptServerTest, UDS) {
   as.Stop(true);
 }
 
-TEST_F(AcceptServerTest, Migrate) {
+TEST_P(AcceptServerTest, Migrate) {
   // Make sure the connection is active.
   client_sock_->proactor()->Await([&] { RunClient(client_sock_.get()); });
 
@@ -264,7 +282,7 @@ TEST_F(AcceptServerTest, Migrate) {
   ASSERT_EQ(200, conn->migrations);
 }
 
-TEST_F(AcceptServerTest, Shutdown) {
+TEST_P(AcceptServerTest, Shutdown) {
   listener_->SetMaxClients(1 << 16);
   auto* proactor = client_sock_->proactor();
 
diff --git a/util/fibers/epoll_socket.cc b/util/fibers/epoll_socket.cc
index db3f91bb..04a5c241 100644
--- a/util/fibers/epoll_socket.cc
+++ b/util/fibers/epoll_socket.cc
@@ -50,14 +50,13 @@ nonstd::unexpected<error_code> MakeUnexpected(std::errc code) {
 constexpr int kEventMask = EPOLLIN | EPOLLOUT | EPOLLET | EPOLLRDHUP;
 
 int AcceptSock(int fd) {
-  sockaddr_in client_addr;
+  sockaddr_storage client_addr;
   socklen_t addr_len = sizeof(client_addr);
-  int res = accept4(fd, (struct sockaddr*)&client_addr, &addr_len, SOCK_NONBLOCK | SOCK_CLOEXEC);
-  return res;
+  return accept4(fd, (struct sockaddr*)&client_addr, &addr_len, SOCK_NONBLOCK | SOCK_CLOEXEC);
 }
 
-int CreateSockFd() {
-  return socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);
+int CreateSockFd(int family) {
+  return socket(family, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);
 }
 
 /*void RegisterEvents(int poll_fd, int sock_fd, uint32_t user_data) {
@@ -73,7 +72,7 @@ int CreateSockFd() {
 constexpr int kEventMask = POLLIN | POLLOUT;
 
 int AcceptSock(int fd) {
-  sockaddr_in client_addr;
+  sockaddr_storage client_addr;
   socklen_t addr_len = sizeof(client_addr);
   int res = accept(fd, (struct sockaddr*)&client_addr, &addr_len);
   if (res >= 0) {
@@ -84,8 +83,8 @@ int AcceptSock(int fd) {
   return res;
 }
 
-int CreateSockFd() {
-  int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+int CreateSockFd(int family) {
+  int fd = socket(family, SOCK_STREAM, IPPROTO_TCP);
   if (fd >= 0) {
     SetNonBlocking(fd);
     SetCloexec(fd);
@@ -268,7 +267,7 @@ error_code EpollSocket::Connect(const endpoint_type& ep, std::function<void(int)
 
   error_code ec;
 
-  int fd = CreateSockFd();
+  int fd = CreateSockFd(ep.address().is_v4() ? AF_INET : AF_INET6);
   if (posix_err_wrap(fd, &ec) < 0)
     return ec;
 
diff --git a/util/fibers/fiber_socket_base.cc b/util/fibers/fiber_socket_base.cc
index b5a2bad6..d941d80a 100644
--- a/util/fibers/fiber_socket_base.cc
+++ b/util/fibers/fiber_socket_base.cc
@@ -95,12 +95,22 @@ error_code LinuxSocketBase::Create(unsigned short pfamily) {
 }
 
 error_code LinuxSocketBase::Listen(uint16_t port, unsigned backlog) {
-  sockaddr_in server_addr;
-  memset(&server_addr, 0, sizeof(server_addr));
-  server_addr.sin_family = AF_INET;
-  server_addr.sin_port = htons(port);
-  server_addr.sin_addr.s_addr = INADDR_ANY;
-  error_code ec = Bind((struct sockaddr*)&server_addr, sizeof(server_addr));
+  error_code ec;
+  if (LocalEndpoint().address().is_v4()) {
+    sockaddr_in server_addr;
+    memset(&server_addr, 0, sizeof(server_addr));
+    server_addr.sin_family = AF_INET;
+    server_addr.sin_port = htons(port);
+    server_addr.sin_addr.s_addr = INADDR_ANY;
+    ec = Bind((struct sockaddr*)&server_addr, sizeof(server_addr));
+ } else {
+    sockaddr_in6 server_addr;
+    memset(&server_addr, 0, sizeof(server_addr));
+    server_addr.sin6_family = AF_INET6;
+    server_addr.sin6_port = htons(port);
+    server_addr.sin6_addr = in6addr_any;
+    ec = Bind((struct sockaddr*)&server_addr, sizeof(server_addr));
+  }
   if (ec)
     return ec;
   return Listen(backlog);
diff --git a/util/fibers/fiber_socket_test.cc b/util/fibers/fiber_socket_test.cc
index 74ca90a8..df283266 100644
--- a/util/fibers/fiber_socket_test.cc
+++ b/util/fibers/fiber_socket_test.cc
@@ -39,7 +39,20 @@ void InitProactor(ProactorBase* p) {
 
 using namespace std;
 
-class FiberSocketTest : public testing::TestWithParam<string_view> {
+// Struct to combine proactor type and IP version parameters
+struct TestParams {
+  string_view proactor_type;
+  bool use_ipv6;
+  
+  TestParams(string_view type, bool ipv6) : proactor_type(type), use_ipv6(ipv6) {}
+  
+  string ToString() const {
+    string ip_ver = use_ipv6 ? "IPv6" : "IPv4";
+    return string(proactor_type) + "_" + ip_ver;
+  }
+};
+
+class FiberSocketTest : public testing::TestWithParam<TestParams> {
  protected:
   void SetUp() final;
 
@@ -49,6 +62,12 @@ class FiberSocketTest : public testing::TestWithParam<string_view> {
     testing::FLAGS_gtest_death_test_style = "threadsafe";
   }
 
+  // Return the proactor type parameter
+  string_view GetProactorType() const { return GetParam().proactor_type; }
+  
+  // Return whether to use IPv6
+  bool UseIPv6() const { return GetParam().use_ipv6; }
+
   using IoResult = int;
 
   unique_ptr<ProactorBase> proactor_;
@@ -63,18 +82,22 @@ class FiberSocketTest : public testing::TestWithParam<string_view> {
   uint32_t conn_sock_err_mask_ = 0;
 };
 
-INSTANTIATE_TEST_SUITE_P(Engines, FiberSocketTest,
-                         testing::Values("epoll"
+INSTANTIATE_TEST_SUITE_P(
+    Engines, 
+    FiberSocketTest,
+    testing::Values(
+          TestParams("epoll", false)  // epoll with IPv4
+        , TestParams("epoll", true)   // epoll with IPv6
 #ifdef __linux__
-                                         ,
-                                         "uring"
+        , TestParams("uring", false)  // uring with IPv4
+        , TestParams("uring", true)    // uring with IPv6
 #endif
-                                         ),
-                         [](const auto& info) { return string(info.param); });
+    ),
+    [](const auto& info) { return info.param.ToString(); });
 
 void FiberSocketTest::SetUp() {
 #if __linux__
-  bool use_uring = GetParam() == "uring";
+  bool use_uring = GetProactorType() == "uring";
   ProactorBase* proactor = nullptr;
   if (use_uring)
     proactor = new UringProactor;
@@ -96,6 +119,16 @@ void FiberSocketTest::SetUp() {
 
   error_code ec = proactor_->AwaitBrief([&] {
     listen_socket_.reset(proactor_->CreateSocket());
+    
+    // For IPv6, we need to explicitly create an IPv6 socket
+    if (UseIPv6()) {
+      auto create_ec = listen_socket_->Create(AF_INET6);
+      if (create_ec) {
+        LOG(ERROR) << "Failed to create IPv6 socket: " << create_ec.message();
+        return create_ec;
+      }
+    }
+    
     return listen_socket_->Listen(0, 0);
   });
 
@@ -103,7 +136,13 @@ void FiberSocketTest::SetUp() {
   listen_port_ = listen_socket_->LocalEndpoint().port();
   DCHECK_GT(listen_port_, 0);
 
-  auto address = boost::asio::ip::make_address("127.0.0.1");
+  // Use IPv4 or IPv6 address based on the parameter
+  boost::asio::ip::address address;
+  if (UseIPv6()) {
+    address = boost::asio::ip::make_address("::1");  // IPv6 loopback
+  } else {
+    address = boost::asio::ip::make_address("127.0.0.1");  // IPv4 loopback
+  }
   listen_ep_ = FiberSocketBase::endpoint_type{address, listen_port_};
 
   accept_fb_ = proactor_->LaunchFiber("AcceptFb", [this] {
@@ -148,7 +187,8 @@ void FiberSocketTest::TearDown() {
 TEST_P(FiberSocketTest, Basic) {
   unique_ptr<FiberSocketBase> sock(proactor_->CreateSocket());
 
-  LOG(INFO) << "before wait ";
+  LOG(INFO) << "Running Basic test for " << (UseIPv6() ? "IPv6" : "IPv4") << " with " 
+            << GetProactorType() << " proactor";
   proactor_->Await([&] {
     ThisFiber::SetName("ConnectFb");
 
@@ -373,7 +413,7 @@ TEST_P(FiberSocketTest, UDS) {
 }
 
 TEST_P(FiberSocketTest, RecvProvided) {
-  bool use_uring = GetParam() == "uring";
+  bool use_uring = GetProactorType() == "uring";
   if (!use_uring) {
     GTEST_SKIP() << "RecvProvided is supported only on uring";
     return;
@@ -438,7 +478,7 @@ TEST_P(FiberSocketTest, RecvProvided) {
 
 #ifdef __linux__
 TEST_P(FiberSocketTest, RecvMultiShot) {
-  bool use_uring = GetParam() == "uring";
+  bool use_uring = GetProactorType() == "uring";
   if (!use_uring) {
     GTEST_SKIP() << "RecvMultiShot is supported only on uring";
     return;
@@ -509,7 +549,7 @@ TEST_P(FiberSocketTest, RecvMultiShot) {
 }
 
 TEST_P(FiberSocketTest, MultiShotNobuf) {
-  bool use_uring = GetParam() == "uring";
+  bool use_uring = GetProactorType() == "uring";
   if (!use_uring) {
     GTEST_SKIP() << "RecvMultiShot is supported only on uring";
     return;
@@ -547,7 +587,7 @@ TEST_P(FiberSocketTest, MultiShotNobuf) {
 }
 
 TEST_P(FiberSocketTest, NotEmpty) {
-  bool use_uring = GetParam() == "uring";
+  bool use_uring = GetProactorType() == "uring";
   bool has_poll_first = false;
 
   if (use_uring) {
@@ -584,7 +624,7 @@ TEST_P(FiberSocketTest, NotEmpty) {
 }
 
 TEST_P(FiberSocketTest, OpenMany) {
-  bool use_uring = GetParam() == "uring";
+  bool use_uring = GetProactorType() == "uring";
   if (!use_uring) {
     GTEST_SKIP() << "OpenMany requires iouring";
     return;
@@ -604,7 +644,7 @@ TEST_P(FiberSocketTest, OpenMany) {
 }
 
 TEST_P(FiberSocketTest, SendProvided) {
-  bool use_uring = GetParam() == "uring";
+  bool use_uring = GetProactorType() == "uring";
   if (!use_uring) {
     GTEST_SKIP() << "SendProvided requires iouring";
     return;
diff --git a/util/fibers/fibers_test.cc b/util/fibers/fibers_test.cc
index 3eb8b300..f77474bc 100644
--- a/util/fibers/fibers_test.cc
+++ b/util/fibers/fibers_test.cc
@@ -112,10 +112,23 @@ ProactorThread::ProactorThread(unsigned index, ProactorBase::Kind kind) {
   }};
 }
 
-class ProactorTest : public testing::TestWithParam<string_view> {
+// Struct to combine proactor type and IP version parameters
+struct TestParams {
+  string_view proactor_type;
+  bool use_ipv6;
+  
+  TestParams(string_view type, bool ipv6) : proactor_type(type), use_ipv6(ipv6) {}
+  
+  string ToString() const {
+    string ip_ver = use_ipv6 ? "IPv6" : "IPv4";
+    return string(proactor_type) + "_" + ip_ver;
+  }
+};
+
+class ProactorTest : public testing::TestWithParam<TestParams> {
  protected:
   static unique_ptr<ProactorThread> CreateProactorThread() {
-    string_view param = GetParam();
+    string_view param = GetParam().proactor_type;
     if (param == "epoll") {
       return make_unique<ProactorThread>(0, ProactorBase::EPOLL);
     }
@@ -130,7 +143,8 @@ class ProactorTest : public testing::TestWithParam<string_view> {
   void SetUp() final {
     proactor_th_ = CreateProactorThread();
     const TestInfo* const test_info = UnitTest::GetInstance()->current_test_info();
-    LOG(INFO) << "Starting " << test_info->name();
+    LOG(INFO) << "Starting " << test_info->name() << " with " 
+              << (UseIPv6() ? "IPv6" : "IPv4");
   }
 
   void TearDown() final {
@@ -144,19 +158,30 @@ class ProactorTest : public testing::TestWithParam<string_view> {
   ProactorBase* proactor() {
     return proactor_th_->get();
   }
+  
+  // Return the proactor type parameter
+  string_view GetProactorType() const { return GetParam().proactor_type; }
+  
+  // Return whether to use IPv6
+  bool UseIPv6() const { return GetParam().use_ipv6; }
 
   using IoResult = int;
 
   std::unique_ptr<ProactorThread> proactor_th_;
 };
 
-INSTANTIATE_TEST_SUITE_P(Engines, ProactorTest,
-                         testing::Values("epoll"
+INSTANTIATE_TEST_SUITE_P(
+    Engines, 
+    ProactorTest,
+    testing::Values(
+          TestParams("epoll", false)  // epoll with IPv4
+        , TestParams("epoll", true)   // epoll with IPv6
 #ifdef __linux__
-                                         ,
-                                         "uring"
+        , TestParams("uring", false)  // uring with IPv4
+        , TestParams("uring", true)    // uring with IPv6
 #endif
-                                         ));
+    ),
+    [](const auto& info) { return info.param.ToString(); });
 
 struct SlistMember {
   boost::intrusive::slist_member_hook<boost::intrusive::link_mode<boost::intrusive::safe_link>>
@@ -830,6 +855,13 @@ TEST_P(ProactorTest, DragonflyBug1591) {
   Mutex m1, m2;
   auto fb_server = proactor()->LaunchFiber("server", [&] {
     start_step(0);
+    
+    // Create the correct socket type based on IP version
+    if (UseIPv6()) {
+      auto create_ec = sock->Create(AF_INET6);
+      ASSERT_FALSE(create_ec) << create_ec.message();
+    }
+    
     auto ec = sock->Listen(0, /*backlog=*/10);
     ASSERT_FALSE(ec) << ec.message();
     end_step();
@@ -858,7 +890,14 @@ TEST_P(ProactorTest, DragonflyBug1591) {
 
   auto fb_client = proactor()->LaunchFiber("client", [&] {
     start_step(1);
-    auto localhost = boost::asio::ip::make_address("127.0.0.1");
+
+    // Use IPv4 or IPv6 address based on the parameter
+    boost::asio::ip::address localhost;
+    if (UseIPv6()) {
+      localhost = boost::asio::ip::make_address("::1");  // IPv6 loopback
+    } else {
+      localhost = boost::asio::ip::make_address("127.0.0.1");  // IPv4 loopback
+    }
     auto ep = FiberSocketBase::endpoint_type{localhost, sock->LocalEndpoint().port()};
     auto ec = sock2->Connect(ep);
     end_step();


Reviews:
user: login: romange
state: COMMENTED
author_association: OWNER
submitted_at: 2025-03-13T10:29:31Z
user: login: vyavdoshenko
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-13T10:33:48Z
user: login: romange
state: COMMENTED
author_association: OWNER
submitted_at: 2025-03-13T10:34:06Z
user: login: romange
body: lgtm, besides the test issue
state: COMMENTED
author_association: OWNER
submitted_at: 2025-03-13T10:34:22Z
user: login: vyavdoshenko
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-13T10:35:45Z
user: login: romange
body: It's good that echo_server supports ipv6 now but why not add the test to unit tests?
state: COMMENTED
author_association: OWNER
submitted_at: 2025-03-14T11:46:23Z
user: login: vyavdoshenko
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-14T13:42:17Z
user: login: romange
state: DISMISSED
author_association: OWNER
submitted_at: 2025-03-14T16:26:09Z
user: login: romange
state: APPROVED
author_association: OWNER
submitted_at: 2025-03-14T18:23:57Z

Review comments:
diff_hunk: @@ -50,14 +50,13 @@ nonstd::unexpected<error_code> MakeUnexpected(std::errc code) {
 constexpr int kEventMask = EPOLLIN | EPOLLOUT | EPOLLET | EPOLLRDHUP;
 
 int AcceptSock(int fd) {
-  sockaddr_in client_addr;
+  sockaddr_storage client_addr;
path: util/fibers/epoll_socket.cc
user: login: romange
body: nice, I did not know about this type
created_at: 2025-03-13T10:29:31Z
author_association: OWNER
diff_hunk: @@ -50,14 +50,13 @@ nonstd::unexpected<error_code> MakeUnexpected(std::errc code) {
 constexpr int kEventMask = EPOLLIN | EPOLLOUT | EPOLLET | EPOLLRDHUP;
 
 int AcceptSock(int fd) {
-  sockaddr_in client_addr;
+  sockaddr_storage client_addr;
path: util/fibers/epoll_socket.cc
user: login: vyavdoshenko
body: This type doesn't fit all the cases. But here it suits well. 
created_at: 2025-03-13T10:33:48Z
author_association: COLLABORATOR
diff_hunk: @@ -95,15 +95,27 @@ error_code LinuxSocketBase::Create(unsigned short pfamily) {
 }
 
 error_code LinuxSocketBase::Listen(uint16_t port, unsigned backlog) {
-  sockaddr_in server_addr;
-  memset(&server_addr, 0, sizeof(server_addr));
-  server_addr.sin_family = AF_INET;
-  server_addr.sin_port = htons(port);
-  server_addr.sin_addr.s_addr = INADDR_ANY;
-  error_code ec = Bind((struct sockaddr*)&server_addr, sizeof(server_addr));
-  if (ec)
-    return ec;
-  return Listen(backlog);
+  if (LocalEndpoint().address().is_v4()) {
path: util/fibers/fiber_socket_base.cc
user: login: romange
body: have you tested it?
can you please create a unit test inside fiber_socket_test that tests these changes?
can also be inside accept_server_test - whatever is more convenient for you.
please note that our CI probably does not support ipv6 by default, but maybe it can be fixed - we did something in dragonfly repo to support. 

Minimal requirement: the unit test should cover ipv6 on a machine that has ipv6 enabled and skip the test on the machine that does not.

created_at: 2025-03-13T10:34:06Z
author_association: OWNER
diff_hunk: @@ -95,15 +95,27 @@ error_code LinuxSocketBase::Create(unsigned short pfamily) {
 }
 
 error_code LinuxSocketBase::Listen(uint16_t port, unsigned backlog) {
-  sockaddr_in server_addr;
-  memset(&server_addr, 0, sizeof(server_addr));
-  server_addr.sin_family = AF_INET;
-  server_addr.sin_port = htons(port);
-  server_addr.sin_addr.s_addr = INADDR_ANY;
-  error_code ec = Bind((struct sockaddr*)&server_addr, sizeof(server_addr));
-  if (ec)
-    return ec;
-  return Listen(backlog);
+  if (LocalEndpoint().address().is_v4()) {
path: util/fibers/fiber_socket_base.cc
user: login: vyavdoshenko
body: I tested this code manually. 
I will add tests.
created_at: 2025-03-13T10:35:45Z
author_association: COLLABORATOR
diff_hunk: @@ -95,15 +95,27 @@ error_code LinuxSocketBase::Create(unsigned short pfamily) {
 }
 
 error_code LinuxSocketBase::Listen(uint16_t port, unsigned backlog) {
-  sockaddr_in server_addr;
-  memset(&server_addr, 0, sizeof(server_addr));
-  server_addr.sin_family = AF_INET;
-  server_addr.sin_port = htons(port);
-  server_addr.sin_addr.s_addr = INADDR_ANY;
-  error_code ec = Bind((struct sockaddr*)&server_addr, sizeof(server_addr));
-  if (ec)
-    return ec;
-  return Listen(backlog);
+  if (LocalEndpoint().address().is_v4()) {
path: util/fibers/fiber_socket_base.cc
user: login: vyavdoshenko
body: @romange 
I have updated the unit tests and tried adding IPv6 support everywhere.
Please take a look.
created_at: 2025-03-14T13:42:17Z
author_association: COLLABORATOR

PR comments:
user: login: codecov-commenter
created_at: 2025-03-13T10:42:26Z
body: :warning: Please install the !['codecov app svg image'](https://github.com/codecov/engineering-team/assets/152432831/e90313f4-9d3a-4b63-8b54-cfe14e7ec20d) to ensure uploads and comments are reliably processed by Codecov.

## [Codecov](https://app.codecov.io/gh/romange/helio/pull/391?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Roman+Gershman) Report
Attention: Patch coverage is `97.26027%` with `2 lines` in your changes missing coverage. Please review.
> Project coverage is 77.79%. Comparing base [(`d30de86`)](https://app.codecov.io/gh/romange/helio/commit/d30de86ee14567c76c3df7dcd537e1f106472fd7?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Roman+Gershman) to head [(`91fe0cb`)](https://app.codecov.io/gh/romange/helio/commit/91fe0cb1d9f43efca99c9fbf70b8285df0014c55?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Roman+Gershman).
> Report is 160 commits behind head on master.

| [Files with missing lines](https://app.codecov.io/gh/romange/helio/pull/391?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Roman+Gershman) | Patch % | Lines |
|---|---|---|
| [util/fibers/fiber\_socket\_test.cc](https://app.codecov.io/gh/romange/helio/pull/391?src=pr&el=tree&filepath=util%2Ffibers%2Ffiber_socket_test.cc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Roman+Gershman#diff-dXRpbC9maWJlcnMvZmliZXJfc29ja2V0X3Rlc3QuY2M=) | 92.00% | [2 Missing :warning: ](https://app.codecov.io/gh/romange/helio/pull/391?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Roman+Gershman) |

:exclamation: Your organization needs to install the [Codecov GitHub app](https://github.com/apps/codecov/installations/select_target) to enable full functionality.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master     #391      +/-   ##
==========================================
+ Coverage   77.60%   77.79%   +0.18%     
==========================================
  Files         103      109       +6     
  Lines        7824     9088    +1264     
==========================================
+ Hits         6072     7070     +998     
- Misses       1752     2018     +266     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/romange/helio/pull/391?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Roman+Gershman).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Roman+Gershman).

<details><summary>🚀 New features to boost your workflow: </summary>

- ❄ [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: vyavdoshenko
created_at: 2025-03-14T11:49:00Z
body: > It's good that echo_server supports ipv6 now but why not add the test to unit tests?

I am in progress. I try to find all possible entities to test and add them.
I will notify you as soon as I finish adding everything.
author_association: COLLABORATOR
user: login: romange
created_at: 2025-03-14T16:27:12Z
body: lgtm but please fix compile error on macos:
https://github.com/romange/helio/actions/runs/13857796262/job/38788394190?pr=391#step:5:1649
author_association: OWNER
user: login: vyavdoshenko
created_at: 2025-03-14T17:08:13Z
body: > lgtm but please fix compile error on macos: https://github.com/romange/helio/actions/runs/13857796262/job/38788394190?pr=391#step:5:1649

Build has been fixed. I verified for macOS and Linux as well.
author_association: COLLABORATOR
user: login: romange
created_at: 2025-03-14T19:56:42Z
body: Thanks, good work @vyavdoshenko .
author_association: OWNER