LCOV - code coverage report
Current view: top level - core - tcp.h (source / functions) Coverage Total Hit
Test: unnamed Lines: 87.1 % 31 27
Test Date: 2025-01-09 16:21:55 Functions: 100.0 % 8 8

            Line data    Source code
       1              : // Copyright 2025 powersagitar
       2              : //
       3              : // Use of this source code is governed by an MIT-style
       4              : // license that can be found in the LICENSE file or at
       5              : // https://opensource.org/licenses/MIT.
       6              : 
       7              : #ifndef TCP_H
       8              : #define TCP_H
       9              : 
      10              : #include <arpa/inet.h>
      11              : #include <netinet/in.h>
      12              : #include <sys/errno.h>
      13              : #include <sys/socket.h>
      14              : #include <unistd.h>
      15              : 
      16              : #include "spdlog/spdlog.h"
      17              : 
      18              : namespace http {
      19              : 
      20              : namespace internals {
      21              : 
      22              : constexpr int kNullSock = -1;
      23              : constexpr int kHTTPPort = 80;
      24              : 
      25              : }  // namespace internals
      26              : 
      27              : class TcpClient {
      28              :  public:
      29              :   TcpClient(const TcpClient &) = delete;
      30              :   TcpClient &operator=(const TcpClient &) = delete;
      31              : 
      32              :   TcpClient(TcpClient &&obj) noexcept { *this = std::move(obj); }
      33              : 
      34              :   TcpClient &operator=(TcpClient &&rhs) noexcept {
      35              :     if (this == &rhs) {
      36              :       return *this;
      37              :     }
      38              : 
      39              :     this->sockfd_ = rhs.sockfd_;
      40              :     rhs.sockfd_ = internals::kNullSock;
      41              : 
      42              :     return *this;
      43              :   }
      44              : 
      45            1 :   TcpClient(const int sockfd, const sockaddr_in addr,
      46              :             const socklen_t addr_len) noexcept
      47            1 :       : sockfd_(sockfd), addr_(addr), addr_len_(addr_len) {}
      48              : 
      49            2 :   ~TcpClient() noexcept {
      50            1 :     if (sockfd_ == internals::kNullSock) {
      51            0 :       return;
      52              :     }
      53              : 
      54            1 :     if (close(sockfd_) != 0) {
      55              :       // TODO: error handling / logging
      56              :       // throw std::system_error(
      57              :       //     errno, std::system_category(),
      58              :       //     "Failed to close client socket " + std::to_string(sockfd_));
      59              :     }
      60            1 :   }
      61              : 
      62            1 :   ssize_t Recv(void *const buffer, const size_t length,
      63              :                const int flags = 0) const noexcept {
      64            1 :     return recv(sockfd_, buffer, length, flags);
      65              :   }
      66              : 
      67              :   ssize_t Send(const void *const buffer, const size_t length,
      68              :                const int flags = 0) const noexcept {
      69              :     return send(sockfd_, buffer, length, flags);
      70              :   }
      71              : 
      72              :   [[nodiscard]] int sockfd() const noexcept { return sockfd_; }
      73              : 
      74              :  private:
      75              :   int sockfd_ = internals::kNullSock;
      76              :   sockaddr_in addr_{};
      77              :   socklen_t addr_len_{};
      78              : };
      79              : 
      80              : class TcpServer {
      81              :  public:
      82              :   /**
      83              :    * See man protocols(5)
      84              :    */
      85              :   enum class Protocol : int {
      86              :     kIP = 0,
      87              :   };
      88              : 
      89              :   /**
      90              :    * SocketServer is uniquely owned. No two instances may share the same socket.
      91              :    */
      92              :   TcpServer(const TcpServer &) = delete;
      93              :   TcpServer &operator=(const TcpServer &) = delete;
      94              : 
      95              :   TcpServer(TcpServer &&obj) noexcept { *this = std::move(obj); }
      96              : 
      97              :   TcpServer &operator=(TcpServer &&rhs) noexcept {
      98              :     if (this == &rhs) {
      99              :       return *this;
     100              :     }
     101              : 
     102              :     this->sockfd_ = rhs.sockfd_;
     103              :     rhs.sockfd_ = internals::kNullSock;
     104              : 
     105              :     return *this;
     106              :   }
     107              : 
     108              :   /**
     109              :    * See man socket(2)
     110              :    */
     111            1 :   TcpServer() noexcept
     112            1 :       : sockfd_(socket(PF_INET, SOCK_STREAM, static_cast<int>(Protocol::kIP))) {
     113            1 :   }
     114              : 
     115              :   /**
     116              :    * See man close(2)
     117              :    */
     118            2 :   ~TcpServer() noexcept {
     119            1 :     if (sockfd_ == internals::kNullSock) {
     120            0 :       return;
     121              :     }
     122              : 
     123            1 :     if (close(sockfd_) != 0) {
     124              :       // TODO: error handling / logging
     125              :       // throw std::system_error(
     126              :       //     errno, std::system_category(),
     127              :       //     "Failed to close server socket " + std::to_string(sockfd_));
     128              :     }
     129            1 :   }
     130              : 
     131              :   /**
     132              :    * See man bind(2)
     133              :    */
     134            1 :   [[nodiscard]] int Bind(const sa_family_t sin_family = AF_INET,
     135              :                          const in_port_t sin_port = internals::kHTTPPort,
     136              :                          const in_addr sin_addr = {INADDR_ANY}) const noexcept {
     137            1 :     sockaddr_in addr{.sin_family = sin_family,
     138              :                      .sin_port = sin_port,
     139              :                      .sin_addr = sin_addr,
     140            1 :                      .sin_zero{0}};
     141              : 
     142              :     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
     143            1 :     return bind(sockfd_, reinterpret_cast<sockaddr *>(&addr), sizeof(addr));
     144              :   }
     145              : 
     146              :   /**
     147              :    * See man listen(2)
     148              :    */
     149            1 :   [[nodiscard]] int Listen(const int backlog = 512) const noexcept {
     150            1 :     return listen(sockfd_, backlog);
     151              :   }
     152              : 
     153              :   /**
     154              :    * See man accept(2)
     155              :    */
     156            1 :   [[nodiscard]] TcpClient Accept() const {
     157            1 :     sockaddr_in addr{};
     158            1 :     socklen_t addr_len = sizeof addr;
     159              : 
     160              :     const int client_sockfd =
     161              :         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
     162            1 :         accept(sockfd_, reinterpret_cast<sockaddr *>(&addr), &addr_len);
     163              : 
     164            1 :     if (client_sockfd == internals::kNullSock) {
     165            0 :       throw std::system_error(errno, std::system_category(),
     166            0 :                               "Failed to accept client socket");
     167              :     }
     168              : 
     169            2 :     return {client_sockfd, addr, addr_len};
     170              :   }
     171              : 
     172              :  private:
     173              :   int sockfd_ = internals::kNullSock;
     174              : };
     175              : 
     176              : }  // namespace http
     177              : 
     178              : #endif  // TCP_H
        

Generated by: LCOV version 2.0-1