From 705d265128c8dee0ff47445015816301c1934917 Mon Sep 17 00:00:00 2001
From: Daniel M <daniel.q.mueller@stud.h-da.de>
Date: Sat, 22 May 2021 00:17:53 +0200
Subject: [PATCH] Add explicit copying to socket wrappers

- Allow creating copies of socket wrapper objects with the same
  underlying socket and socket file descriptor via a clone function
- Copy constructors and assignments are still deleted to prevent
  accidental copies
---
 inc/tcplistener.hpp | 25 +++++++++++++++++++++++--
 inc/tcpstream.hpp   | 25 +++++++++++++++++++++++--
 inc/udpsocket.hpp   | 25 +++++++++++++++++++++++--
 src/tcplistener.cpp | 14 ++++++++++++++
 src/tcpstream.cpp   | 14 ++++++++++++++
 src/udpsocket.cpp   | 17 +++++++++++++++++
 6 files changed, 114 insertions(+), 6 deletions(-)

diff --git a/inc/tcplistener.hpp b/inc/tcplistener.hpp
index fbbd727..86b1e60 100644
--- a/inc/tcplistener.hpp
+++ b/inc/tcplistener.hpp
@@ -78,12 +78,12 @@ public:
     TcpListener& operator=(TcpListener &&other);
 
     /**
-     * @brief Copying TcpListener is not allowed.
+     * @brief Copying TcpListener is not allowed. See clone() for explicit copies.
      */
     TcpListener(const TcpListener &other) = delete;
 
     /**
-     * @brief Copying TcpListener is not allowed.
+     * @brief Copying TcpListener is not allowed. See clone() for explicit copies.
      */
     TcpListener& operator=(const TcpListener &other) = delete;
 
@@ -120,6 +120,27 @@ public:
      */
     void close();
 
+    /**
+     * @brief Set the behavior for when the TcpListener is destroyed. If autoclose
+     * is enabled, the socket is closed on destruct. If autoclose is disabled, the
+     * socket will not be closed automatically.
+     * 
+     * @param autoclose Enable or disable the autoclose functionality.
+     */
+    void setAutoclose(bool autoclose);
+
+    /**
+     * @brief Create a clone of this socket wrapper object. The clone and the 
+     * original will share the same underlying socket and file descriptor. 
+     * If one of the instances closes the socket, the socket will be closed 
+     * for both. The other instance will not be notified about this, but instead
+     * socket operations will just fail. Due to this, it might be a good idea to  
+     * disabel autoclose and manually close the socket.
+     * 
+     * @return A clone of this TcpListener that shares the same underlying socket.
+     */
+    TcpListener clone();
+
 };
 
 
diff --git a/inc/tcpstream.hpp b/inc/tcpstream.hpp
index 8b0ad76..fcb0801 100644
--- a/inc/tcpstream.hpp
+++ b/inc/tcpstream.hpp
@@ -80,12 +80,12 @@ public:
     TcpStream& operator=(TcpStream &&other);
 
     /**
-     * @brief Copying TcpListener is not allowed.
+     * @brief Copying TcpListener is not allowed. See clone() for explicit copies.
      */
     TcpStream(const TcpStream &other) = delete;
 
     /**
-     * @brief Copying TcpListener is not allowed.
+     * @brief Copying TcpListener is not allowed. See clone() for explicit copies.
      */
     TcpStream& operator=(const TcpStream &other) = delete;
 
@@ -231,6 +231,27 @@ public:
      */
     bool isClosed() const;
 
+    /**
+     * @brief Set the behavior for when the TcpStream is destroyed. If autoclose
+     * is enabled, the socket is closed on destruct. If autoclose is disabled, the
+     * socket will not be closed automatically.
+     * 
+     * @param autoclose Enable or disable the autoclose functionality.
+     */
+    void setAutoclose(bool autoclose);
+
+    /**
+     * @brief Create a clone of this socket wrapper object. The clone and the 
+     * original will share the same underlying socket and file descriptor. 
+     * If one of the instances closes the socket, the socket will be closed 
+     * for both. The other instance will not be notified about this, but instead
+     * socket operations will just fail. Due to this, it might be a good idea to  
+     * disabel autoclose and manually close the socket.
+     * 
+     * @return A clone of this TcpStream that shares the same underlying socket.
+     */
+    TcpStream clone();
+
     friend class TcpListener;
 
 };
diff --git a/inc/udpsocket.hpp b/inc/udpsocket.hpp
index 99771eb..112e329 100644
--- a/inc/udpsocket.hpp
+++ b/inc/udpsocket.hpp
@@ -98,12 +98,12 @@ public:
     UdpSocket& operator=(UdpSocket &&other);
 
     /**
-     * @brief Copying UdpSocket is not allowed.
+     * @brief Copying UdpSocket is not allowed. See clone() for explicit copies.
      */
     UdpSocket(const UdpSocket &other) = delete;
 
     /**
-     * @brief Copying UdpSocket is not allowed.
+     * @brief Copying UdpSocket is not allowed. See clone() for explicit copies.
      */
     UdpSocket& operator=(const UdpSocket &other) = delete;
 
@@ -217,6 +217,27 @@ public:
      */
     void close();
 
+    /**
+     * @brief Set the behavior for when the UdpSocket is destroyed. If autoclose
+     * is enabled, the socket is closed on destruct. If autoclose is disabled, the
+     * socket will not be closed automatically.
+     * 
+     * @param autoclose Enable or disable the autoclose functionality.
+     */
+    void setAutoclose(bool autoclose);
+
+    /**
+     * @brief Create a clone of this socket wrapper object. The clone and the 
+     * original will share the same underlying socket and file descriptor. 
+     * If one of the instances closes the socket, the socket will be closed 
+     * for both. The other instance will not be notified about this, but instead
+     * socket operations will just fail. Due to this, it might be a good idea to  
+     * disabel autoclose and manually close the socket.
+     * 
+     * @return A clone of this UdpSocket that shares the same underlying socket.
+     */
+    UdpSocket clone();
+
 };
 
 
diff --git a/src/tcplistener.cpp b/src/tcplistener.cpp
index bbab406..dc5262f 100644
--- a/src/tcplistener.cpp
+++ b/src/tcplistener.cpp
@@ -129,4 +129,18 @@ void TcpListener::close()
     }
 
     sockfd = 0;
+}
+
+void TcpListener::setAutoclose(bool _autoclose)
+{
+    autoclose = _autoclose;
+}
+
+TcpListener TcpListener::clone()
+{
+    TcpListener other{local};
+    other.sockfd = sockfd;
+    other.autoclose = autoclose;
+
+    return other;
 }
\ No newline at end of file
diff --git a/src/tcpstream.cpp b/src/tcpstream.cpp
index 85efb7e..8f6e5fd 100644
--- a/src/tcpstream.cpp
+++ b/src/tcpstream.cpp
@@ -259,4 +259,18 @@ const SockAddr & TcpStream::getRemoteAddr() const
 bool TcpStream::isClosed() const
 {
     return sockfd == 0;
+}
+
+void TcpStream::setAutoclose(bool _autoclose)
+{
+    autoclose = _autoclose;
+}
+
+TcpStream TcpStream::clone()
+{
+    TcpStream other{remote};
+    other.sockfd = sockfd;
+    other.autoclose = autoclose;
+
+    return other;
 }
\ No newline at end of file
diff --git a/src/udpsocket.cpp b/src/udpsocket.cpp
index 3f5cd11..9ef39b4 100644
--- a/src/udpsocket.cpp
+++ b/src/udpsocket.cpp
@@ -190,4 +190,21 @@ void UdpSocket::close()
         ::close(sockfd);
     }
     sockfd = 0;
+}
+
+void UdpSocket::setAutoclose(bool _autoclose)
+{
+    autoclose = _autoclose;
+}
+
+UdpSocket UdpSocket::clone()
+{
+    UdpSocket other;
+    other.local = local;
+    other.sockfd = sockfd;
+    other.raw_socklen = raw_socklen;
+    other.address_family = address_family;
+    other.autoclose = autoclose;
+
+    return other;
 }
\ No newline at end of file
-- 
GitLab