fd: Add TCP stream functions
This makes it possible to listen on stream sockets and accept new
connections.
Change-Id: I6a79035b4b7f308a62eab6c73075fef98df7a866
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/src/stdplus/fd/gmock.hpp b/src/stdplus/fd/gmock.hpp
index bd57b51..37dc3eb 100644
--- a/src/stdplus/fd/gmock.hpp
+++ b/src/stdplus/fd/gmock.hpp
@@ -20,6 +20,9 @@
MOCK_METHOD(size_t, lseek, (off_t offset, Whence whence), (override));
MOCK_METHOD(void, truncate, (off_t size), (override));
MOCK_METHOD(void, bind, (span<const std::byte> sockaddr), (override));
+ MOCK_METHOD(void, listen, (int backlog), (override));
+ MOCK_METHOD((std::tuple<std::optional<int>, span<std::byte>>), accept,
+ (span<std::byte> sockaddr), (override));
MOCK_METHOD(void, setsockopt,
(SockLevel level, SockOpt optname, span<const std::byte> opt),
(override));
diff --git a/src/stdplus/fd/impl.cpp b/src/stdplus/fd/impl.cpp
index 511bda4..4335cf5 100644
--- a/src/stdplus/fd/impl.cpp
+++ b/src/stdplus/fd/impl.cpp
@@ -115,6 +115,28 @@
"bind");
}
+void FdImpl::listen(int backlog)
+{
+ CHECK_ERRNO(::listen(get(), backlog), "listen");
+}
+
+std::tuple<std::optional<int>, span<std::byte>>
+ FdImpl::accept(span<std::byte> sockaddr)
+{
+ socklen_t len = sockaddr.size();
+ auto fd = ::accept(
+ get(), reinterpret_cast<struct sockaddr*>(sockaddr.data()), &len);
+ if (fd == -1)
+ {
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ {
+ return {};
+ }
+ throw util::makeSystemError(errno, "accept");
+ }
+ return std::make_tuple(fd, sockaddr.subspan(0, len));
+}
+
void FdImpl::setsockopt(SockLevel level, SockOpt optname,
span<const std::byte> opt)
{
diff --git a/src/stdplus/fd/impl.hpp b/src/stdplus/fd/impl.hpp
index b321dfb..0487aba 100644
--- a/src/stdplus/fd/impl.hpp
+++ b/src/stdplus/fd/impl.hpp
@@ -19,6 +19,9 @@
size_t lseek(off_t offset, Whence whence) override;
void truncate(off_t size) override;
void bind(span<const std::byte> sockaddr) override;
+ void listen(int backlog) override;
+ std::tuple<std::optional<int>, span<std::byte>>
+ accept(span<std::byte> sockaddr) override;
void setsockopt(SockLevel level, SockOpt optname,
span<const std::byte> opt) override;
int ioctl(unsigned long id, void* data) override;
diff --git a/src/stdplus/fd/intf.hpp b/src/stdplus/fd/intf.hpp
index aac85a2..bd8de13 100644
--- a/src/stdplus/fd/intf.hpp
+++ b/src/stdplus/fd/intf.hpp
@@ -1,9 +1,11 @@
#pragma once
#include <cstddef>
#include <fcntl.h>
+#include <optional>
#include <stdplus/flags.hpp>
#include <stdplus/types.hpp>
#include <sys/socket.h>
+#include <tuple>
namespace stdplus
{
@@ -91,6 +93,9 @@
virtual size_t lseek(off_t offset, Whence whence) = 0;
virtual void truncate(off_t size) = 0;
virtual void bind(span<const std::byte> sockaddr) = 0;
+ virtual void listen(int backlog) = 0;
+ virtual std::tuple<std::optional<int>, span<std::byte>>
+ accept(span<std::byte> sockaddr) = 0;
virtual void setsockopt(SockLevel level, SockOpt optname,
span<const std::byte> opt) = 0;
virtual int ioctl(unsigned long id, void* data) = 0;
diff --git a/src/stdplus/fd/ops.hpp b/src/stdplus/fd/ops.hpp
index 317eb30..4bfc5ba 100644
--- a/src/stdplus/fd/ops.hpp
+++ b/src/stdplus/fd/ops.hpp
@@ -1,4 +1,6 @@
#pragma once
+#include <stdexcept>
+#include <stdplus/fd/dupable.hpp>
#include <stdplus/fd/intf.hpp>
#include <stdplus/raw.hpp>
#include <stdplus/types.hpp>
@@ -103,6 +105,36 @@
return fd.bind(raw::asSpan<std::byte>(sockaddr));
}
+inline void listen(Fd& fd, int backlog)
+{
+ return fd.listen(backlog);
+}
+
+template <typename SockAddr>
+inline std::optional<stdplus::DupableFd> accept(Fd& fd, SockAddr&& sockaddr)
+{
+ auto ret = fd.accept(raw::asSpan<std::byte>(sockaddr));
+ if (!std::get<0>(ret))
+ {
+ return std::nullopt;
+ }
+ if (std::get<1>(ret).size() != sizeof(sockaddr))
+ {
+ throw std::runtime_error("Invalid sockaddr type for accept");
+ }
+ return stdplus::DupableFd(std::move(*std::get<0>(ret)));
+}
+
+inline std::optional<stdplus::DupableFd> accept(Fd& fd)
+{
+ auto ret = std::get<0>(fd.accept(span<std::byte>{}));
+ if (!ret)
+ {
+ return std::nullopt;
+ }
+ return stdplus::DupableFd(std::move(*ret));
+}
+
template <typename Opt>
inline void setsockopt(Fd& fd, SockLevel level, SockOpt optname, Opt&& opt)
{