LCOV - code coverage report
Current view: top level - asio/sockets - tcp_socket.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 98.0 % 102 100
Test Date: 2026-11-10 10:38:07 Functions: 97.0 % 33 32

            Line data    Source code
       1              : #include <algorithm>
       2              : #include <charconv>
       3              : #include <iostream>
       4              : #include <fcntl.h>
       5              : #include "tcp_socket.hpp"
       6              : #include "../with_timeout.hpp"
       7              : 
       8              : namespace thinger::asio {
       9              : 
      10        24883 : tcp_socket::tcp_socket(const std::string& context, boost::asio::io_context& io_context)
      11        24883 :     : socket(context, io_context), socket_(io_context) {
      12        24883 : }
      13              : 
      14          144 : tcp_socket::tcp_socket(const std::string& context, const std::shared_ptr<tcp_socket>& tcp_socket)
      15          144 :     : socket(context, tcp_socket->get_io_context()), socket_(std::move(tcp_socket->get_socket())) {
      16          144 : }
      17              : 
      18        10805 : tcp_socket::tcp_socket(const std::string& context, boost::asio::ip::tcp::socket&& sock)
      19        10805 :     : socket(context, static_cast<boost::asio::io_context&>(sock.get_executor().context())), socket_(std::move(sock)) {
      20        10805 : }
      21              : 
      22        35832 : tcp_socket::~tcp_socket() {
      23        35832 :     LOG_TRACE("releasing tcp connection");
      24        35832 :     close();
      25        35832 : }
      26              : 
      27        63417 : void tcp_socket::close() {
      28        63417 :     boost::system::error_code ec;
      29        63417 :     if (socket_.is_open()) {
      30        24983 :         socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
      31              :     }
      32        63417 :     socket_.close(ec);
      33        63417 :     LOG_TRACE("closing tcp socket result: {}", ec.message());
      34        63417 : }
      35              : 
      36          656 : bool tcp_socket::shutdown_send() {
      37          656 :     boost::system::error_code ec;
      38          656 :     socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec);
      39         1312 :     return !ec;
      40              : }
      41              : 
      42         5729 : void tcp_socket::cancel() {
      43         5729 :     socket_.cancel();
      44         5722 : }
      45              : 
      46         5897 : awaitable<boost::system::error_code> tcp_socket::connect(
      47              :     const std::string& host,
      48              :     const std::string& port,
      49              :     std::chrono::seconds timeout)
      50              : {
      51              :     // One deadline for the whole call: each phase (resolve, connect, handshake) gets the time
      52              :     // left until it, so a peer cannot stretch the call by answering just in time to each
      53              :     const auto deadline = std::chrono::steady_clock::now() + timeout;
      54         7913 :     auto time_left = [&deadline] {
      55         7913 :         return std::max(deadline - std::chrono::steady_clock::now(), std::chrono::steady_clock::duration::zero());
      56              :     };
      57              :     // A phase did not end in time: the socket is closed, and the error is timed_out, or
      58              :     // operation_aborted when the caller cancelled the call (the timer may then win the race)
      59           21 :     auto expired = [this](const boost::asio::cancellation_state& state) {
      60           21 :         close();
      61           21 :         return state.cancelled() != boost::asio::cancellation_type::none
      62           21 :             ? boost::system::error_code{boost::asio::error::operation_aborted}
      63           21 :             : boost::system::error_code{boost::asio::error::timed_out};
      64              :     };
      65              : 
      66              :     close();
      67              : 
      68              :     // Resolve the host, unless it is already an address with a numeric port. asio runs
      69              :     // getaddrinfo on a thread of its own and cannot interrupt it: a resolve that hangs ends
      70              :     // the call only once getaddrinfo returns (its own timeouts), with timed_out
      71              :     using results_type = boost::asio::ip::tcp::resolver::results_type;
      72              :     results_type endpoints;
      73              :     boost::system::error_code address_ec;
      74              :     auto address = boost::asio::ip::make_address(host, address_ec);
      75              :     uint16_t port_number = 0;
      76              :     auto [port_end, port_ec] = std::from_chars(port.data(), port.data() + port.size(), port_number);
      77              :     if (!address_ec && port_ec == std::errc{} && port_end == port.data() + port.size()) {
      78              :         endpoints = results_type::create(boost::asio::ip::tcp::endpoint(address, port_number), host, port);
      79              :     } else {
      80              :         boost::asio::ip::tcp::resolver resolver(io_context_);
      81              :         auto resolved = co_await with_timeout(resolver.async_resolve(host, port, use_nothrow_awaitable), time_left());
      82              :         if (!resolved) co_return expired(co_await boost::asio::this_coro::cancellation_state);
      83              :         auto [resolve_ec, results] = std::move(*resolved);
      84              :         if (resolve_ec) co_return resolve_ec;
      85              :         endpoints = std::move(results);
      86              :     }
      87              : 
      88              :     // Connect: a connect not done in time is cancelled
      89              :     auto connected = co_await with_timeout(
      90              :         boost::asio::async_connect(socket_, endpoints, use_nothrow_awaitable), time_left());
      91              :     if (!connected) co_return expired(co_await boost::asio::this_coro::cancellation_state);
      92              :     auto connect_ec = std::get<0>(*connected);
      93              :     if (connect_ec) {
      94              :         co_return connect_ec;
      95              :     }
      96              :     // Not inherited by the programs the process runs (fork and exec): asio opens sockets
      97              :     // without SOCK_CLOEXEC
      98              :     ::fcntl(socket_.native_handle(), F_SETFD, FD_CLOEXEC);
      99              : 
     100              :     // Run the client handshake if required (for SSL sockets), with the time left: a peer that
     101              :     // accepts and stays silent, or trickles its answer, must not hold the call
     102              :     if (requires_handshake()) {
     103              :         auto handshaken = co_await with_timeout(handshake(host), time_left());
     104              :         if (!handshaken) co_return expired(co_await boost::asio::this_coro::cancellation_state);
     105              :         if (*handshaken) {
     106              :             close();
     107              :             co_return *handshaken;
     108              :         }
     109              :     }
     110              : 
     111              :     co_return boost::system::error_code{};
     112        11794 : }
     113              : 
     114       460126 : boost::asio::ip::tcp::socket& tcp_socket::get_socket() {
     115       460126 :     return socket_;
     116              : }
     117              : 
     118        23386 : std::string tcp_socket::get_remote_ip() const {
     119        23386 :     boost::system::error_code ec;
     120        23386 :     auto remote_ep = socket_.remote_endpoint(ec);
     121        23386 :     if (!ec) {
     122        20326 :         return remote_ep.address().to_string();
     123              :     }
     124         6120 :     return "0.0.0.0";
     125              : }
     126              : 
     127        20138 : std::string tcp_socket::get_local_port() const {
     128        20138 :     boost::system::error_code ec;
     129        20138 :     auto local_ep = socket_.local_endpoint(ec);
     130        20138 :     if (!ec) {
     131        20138 :         return std::to_string(local_ep.port());
     132              :     }
     133            0 :     return "0";
     134              : }
     135              : 
     136         1143 : std::string tcp_socket::get_remote_port() const {
     137         1143 :     boost::system::error_code ec;
     138         1143 :     auto remote_ep = socket_.remote_endpoint(ec);
     139         1143 :     if (!ec) {
     140         1143 :         return std::to_string(remote_ep.port());
     141              :     }
     142            0 :     return "0";
     143              : }
     144              : 
     145       169319 : awaitable<io_result> tcp_socket::read_some(uint8_t* buffer, size_t max_size) {
     146              :     co_return co_await socket_.async_read_some(
     147              :         boost::asio::buffer(buffer, max_size),
     148              :         use_nothrow_awaitable);
     149       338638 : }
     150              : 
     151        31432 : awaitable<io_result> tcp_socket::read_some_cancellable(uint8_t* buffer, size_t max_size,
     152              :                                                        boost::asio::cancellation_slot slot) {
     153              :     co_return co_await detail::read_some_on_slot(socket_, buffer, max_size, slot);
     154        62864 : }
     155              : 
     156          606 : awaitable<io_result> tcp_socket::read(uint8_t* buffer, size_t size) {
     157              :     co_return co_await boost::asio::async_read(
     158              :         socket_,
     159              :         boost::asio::buffer(buffer, size),
     160              :         boost::asio::transfer_exactly(size),
     161              :         use_nothrow_awaitable);
     162         1212 : }
     163              : 
     164            3 : awaitable<io_result> tcp_socket::read(boost::asio::streambuf& buffer, size_t size) {
     165              :     co_return co_await boost::asio::async_read(
     166              :         socket_,
     167              :         buffer,
     168              :         boost::asio::transfer_exactly(size),
     169              :         use_nothrow_awaitable);
     170            6 : }
     171              : 
     172            3 : awaitable<io_result> tcp_socket::read_until(boost::asio::streambuf& buffer, std::string_view delim) {
     173              :     co_return co_await boost::asio::async_read_until(
     174              :         socket_,
     175              :         buffer,
     176              :         std::string(delim),
     177              :         use_nothrow_awaitable);
     178            6 : }
     179              : 
     180        54001 : awaitable<io_result> tcp_socket::write(const uint8_t* buffer, size_t size) {
     181              :     co_return co_await boost::asio::async_write(
     182              :         socket_,
     183              :         boost::asio::buffer(buffer, size),
     184              :         use_nothrow_awaitable);
     185       108002 : }
     186              : 
     187        19702 : awaitable<io_result> tcp_socket::write(std::string_view str) {
     188              :     co_return co_await boost::asio::async_write(
     189              :         socket_,
     190              :         boost::asio::buffer(str.data(), str.size()),
     191              :         use_nothrow_awaitable);
     192        39404 : }
     193              : 
     194        68231 : awaitable<io_result> tcp_socket::write(const std::vector<boost::asio::const_buffer>& buffers) {
     195              :     co_return co_await boost::asio::async_write(
     196              :         socket_,
     197              :         buffers,
     198              :         use_nothrow_awaitable);
     199       136462 : }
     200              : 
     201        20309 : size_t tcp_socket::write_now(const std::vector<boost::asio::const_buffer>& buffers, boost::system::error_code& ec) {
     202        20309 :     return send_now(socket_.native_handle(), buffers, ec);
     203              : }
     204              : 
     205         1365 : awaitable<boost::system::error_code> tcp_socket::wait(boost::asio::socket_base::wait_type type) {
     206              :     auto [ec] = co_await socket_.async_wait(type, use_nothrow_awaitable);
     207              :     co_return ec;
     208         2730 : }
     209              : 
     210          219 : awaitable<boost::system::error_code> tcp_socket::peer_closed() {
     211              :     co_return peek_closed(socket_.native_handle());
     212          438 : }
     213              : 
     214        24038 : bool tcp_socket::enable_tcp_no_delay() {
     215        24038 :     boost::system::error_code ec;
     216        24038 :     socket_.set_option(boost::asio::ip::tcp::no_delay(true), ec);
     217        24038 :     if (ec) LOG_DEBUG("cannot enable tcp_no_delay: {}", ec.message());
     218        48076 :     return !ec;
     219              : }
     220              : 
     221            5 : bool tcp_socket::disable_tcp_no_delay() {
     222            5 :     boost::system::error_code ec;
     223            5 :     socket_.set_option(boost::asio::ip::tcp::no_delay(false), ec);
     224            5 :     if (ec) LOG_DEBUG("cannot disable tcp_no_delay: {}", ec.message());
     225           10 :     return !ec;
     226              : }
     227              : 
     228      2600394 : bool tcp_socket::is_open() const {
     229      2600394 :     return socket_.is_open();
     230              : }
     231              : 
     232        21556 : bool tcp_socket::is_secure() const {
     233        21556 :     return false;
     234              : }
     235              : 
     236         3261 : bool tcp_socket::readable_now() const {
     237         3261 :     return socket_.is_open() && poll_readable(const_cast<boost::asio::ip::tcp::socket&>(socket_).native_handle());
     238              : }
     239              : 
     240          440 : size_t tcp_socket::send_backlog() const {
     241          440 :     return socket_.is_open() ? output_queue(const_cast<boost::asio::ip::tcp::socket&>(socket_).native_handle()) : 0;
     242              : }
     243              : 
     244         1973 : size_t tcp_socket::available() const {
     245         1973 :     boost::system::error_code ec;
     246         1973 :     auto size = socket_.available(ec);
     247         1973 :     if (ec) {
     248            4 :         LOG_ERROR("error while getting socket available bytes ({}): {}", size, ec.message());
     249              :     }
     250         1973 :     return size;
     251              : }
     252              : 
     253              : }
        

Generated by: LCOV version 2.0-1