LCOV - code coverage report
Current view: top level - asio/sockets - socket.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 85.6 % 97 83
Test Date: 2026-11-10 10:38:07 Functions: 73.3 % 30 22

            Line data    Source code
       1              : #include <array>
       2              : #include <iostream>
       3              : #include "socket.hpp"
       4              : #if !defined(BOOST_ASIO_WINDOWS)
       5              : #include <poll.h>
       6              : #include <sys/socket.h>
       7              : #include <sys/uio.h>
       8              : #endif
       9              : #if defined(__linux__)
      10              : #include <linux/sockios.h>
      11              : #include <sys/ioctl.h>
      12              : #endif
      13              : 
      14              : namespace thinger::asio {
      15              : 
      16              :     std::atomic<unsigned long> socket::connections(0);
      17              : 
      18        57796 :     socket::socket(const std::string&, boost::asio::io_context& io_context)
      19        57796 :         : io_context_(io_context) {
      20        57796 :         ++connections;
      21        57796 :     }
      22              : 
      23        57796 :     socket::~socket() {
      24        57796 :         --connections;
      25        57796 :     }
      26              : 
      27       262992 :     boost::asio::io_context& socket::get_io_context() const {
      28       262992 :         return io_context_;
      29              :     }
      30              : 
      31        15894 :     bool socket::requires_handshake() const {
      32        15894 :         return false;
      33              :     }
      34              : 
      35            0 :     awaitable<boost::system::error_code> socket::handshake(const std::string& host) {
      36              :         co_return boost::system::error_code{};
      37            0 :     }
      38              : 
      39         6632 :     size_t socket::write_now(const std::vector<boost::asio::const_buffer>&, boost::system::error_code& ec) {
      40         6632 :         ec = boost::asio::error::operation_not_supported;
      41         6632 :         return 0;
      42              :     }
      43              : 
      44        32485 :     size_t socket::send_now(native_handle_type descriptor, const std::vector<boost::asio::const_buffer>& buffers,
      45              :                             boost::system::error_code& ec) {
      46              : #if defined(MSG_DONTWAIT) && !defined(BOOST_ASIO_WINDOWS)
      47              :         std::array<iovec, 64> iov;
      48        32485 :         size_t count = std::min(buffers.size(), iov.size());
      49       374544 :         for (size_t i = 0; i < count; ++i) {
      50       342059 :             iov[i].iov_base = const_cast<void*>(buffers[i].data());
      51       342059 :             iov[i].iov_len = buffers[i].size();
      52              :         }
      53        32485 :         msghdr message{};
      54        32485 :         message.msg_iov = iov.data();
      55        32485 :         message.msg_iovlen = count;
      56        32485 :         int flags = MSG_DONTWAIT;
      57              : #if defined(MSG_NOSIGNAL)
      58        32485 :         flags |= MSG_NOSIGNAL;
      59              : #endif
      60              :         ssize_t bytes;
      61              :         do {
      62        32485 :             bytes = ::sendmsg(descriptor, &message, flags);
      63        32485 :         } while (bytes < 0 && errno == EINTR);
      64        32485 :         if (bytes < 0) {
      65          344 :             ec = boost::system::error_code(errno, boost::asio::error::get_system_category());
      66          344 :             if (ec == boost::asio::error::try_again) ec = boost::asio::error::would_block;
      67          344 :             return 0;
      68              :         }
      69        32141 :         ec = {};
      70        32141 :         return static_cast<size_t>(bytes);
      71              : #else
      72              :         ec = boost::asio::error::operation_not_supported;
      73              :         return 0;
      74              : #endif
      75              :     }
      76              : 
      77            0 :     awaitable<boost::system::error_code> socket::peer_closed() {
      78              :         co_return boost::asio::error::operation_not_supported;
      79            0 :     }
      80              : 
      81            0 :     awaitable<boost::system::error_code> socket::connect(const std::string&, const std::string&, std::chrono::seconds) {
      82              :         co_return boost::asio::error::operation_not_supported;
      83            0 :     }
      84              : 
      85            3 :     awaitable<boost::system::error_code> socket::wait(boost::asio::socket_base::wait_type) {
      86              :         co_return boost::asio::error::operation_not_supported;
      87            6 :     }
      88              : 
      89           15 :     bool socket::is_secure() const {
      90           15 :         return false;
      91              :     }
      92              : 
      93            6 :     size_t socket::available() const {
      94            6 :         return 0;
      95              :     }
      96              : 
      97            0 :     bool socket::readable_now() const {
      98            0 :         return false;
      99              :     }
     100              : 
     101            0 :     size_t socket::send_backlog() const {
     102            0 :         return 0;
     103              :     }
     104              : 
     105          470 :     size_t socket::output_queue(native_handle_type descriptor) {
     106          470 :         int bytes = 0;
     107              : #if defined(__linux__)
     108              :         // TCP: not acknowledged, sent or not; Unix: not read
     109          470 :         if (::ioctl(descriptor, SIOCOUTQ, &bytes) != 0) return 0;
     110              : #elif defined(SO_NWRITE)
     111              :         socklen_t size = sizeof(bytes);
     112              :         if (::getsockopt(descriptor, SOL_SOCKET, SO_NWRITE, &bytes, &size) != 0) return 0;
     113              : #else
     114              :         (void) descriptor;
     115              : #endif
     116          470 :         return bytes > 0 ? size_t(bytes) : 0;
     117              :     }
     118              : 
     119         3459 :     bool socket::poll_readable(native_handle_type descriptor) {
     120              : #if !defined(BOOST_ASIO_WINDOWS)
     121         3459 :         pollfd fd{};
     122         3459 :         fd.fd = descriptor;
     123         3459 :         fd.events = POLLIN;
     124              :         int ready;
     125              :         do {
     126         3459 :             ready = ::poll(&fd, 1, 0);
     127         3459 :         } while (ready < 0 && errno == EINTR);
     128              :         // POLLIN, POLLHUP, POLLERR or POLLNVAL, or poll itself failing
     129         3459 :         return ready != 0;
     130              : #else
     131              :         (void) descriptor;
     132              :         return false;
     133              : #endif
     134              :     }
     135              : 
     136          258 :     boost::system::error_code socket::peek_closed(native_handle_type descriptor) {
     137              : #if defined(MSG_DONTWAIT) && !defined(BOOST_ASIO_WINDOWS)
     138              :         char byte;
     139              :         ssize_t bytes;
     140              :         do {
     141          258 :             bytes = ::recv(descriptor, &byte, 1, MSG_PEEK | MSG_DONTWAIT);
     142          258 :         } while (bytes < 0 && errno == EINTR);
     143          258 :         if (bytes > 0) return {};
     144          226 :         if (bytes == 0) return boost::asio::error::eof;
     145           40 :         boost::system::error_code ec(errno, boost::asio::error::get_system_category());
     146              :         // Nothing to read yet: no conclusion, as with data
     147           40 :         if (ec == boost::asio::error::would_block || ec == boost::asio::error::try_again) return {};
     148           34 :         return ec;
     149              : #else
     150              :         (void) descriptor;
     151              :         return boost::asio::error::operation_not_supported;
     152              : #endif
     153              :     }
     154              : 
     155            6 :     std::string socket::get_remote_ip() const {
     156            6 :         return {};
     157              :     }
     158              : 
     159            0 :     std::string socket::get_local_port() const {
     160            0 :         return {};
     161              :     }
     162              : 
     163            0 :     std::string socket::get_remote_port() const {
     164            0 :         return {};
     165              :     }
     166              : 
     167           15 :     std::optional<tls_session> socket::tls() const {
     168           15 :         return std::nullopt;
     169              :     }
     170              : 
     171            3 :     awaitable<io_result> socket::read_some_cancellable(uint8_t buffer[], size_t max_size,
     172              :                                                        boost::asio::cancellation_slot slot) {
     173              :         co_return co_await boost::asio::co_spawn(io_context_, read_some(buffer, max_size),
     174              :                                                  boost::asio::bind_cancellation_slot(slot, boost::asio::use_awaitable));
     175            6 :     }
     176              : 
     177          261 :     awaitable<io_result> socket::read(uint8_t buffer[], size_t size) {
     178              :         size_t total = 0;
     179              :         while (total < size) {
     180              :             auto [ec, bytes] = co_await read_some(buffer + total, size - total);
     181              :             total += bytes;
     182              :             if (ec) co_return io_result{ec, total};
     183              :             // a socket that keeps the guarantees never returns 0 bytes here: do not spin
     184              :             if (bytes == 0) co_return io_result{boost::asio::error::eof, total};
     185              :         }
     186              :         co_return io_result{boost::system::error_code{}, total};
     187          522 :     }
     188              : 
     189           15 :     awaitable<io_result> socket::read(boost::asio::streambuf& buffer, size_t size) {
     190              :         size_t total = 0;
     191              :         while (total < size) {
     192              :             boost::asio::streambuf::mutable_buffers_type space;
     193              :             try {
     194              :                 space = buffer.prepare(size - total);
     195              :             } catch (const std::length_error&) {
     196              :                 co_return io_result{boost::asio::error::no_buffer_space, total};
     197              :             }
     198              :             auto [ec, bytes] = co_await read_some(static_cast<uint8_t*>(space.data()), space.size());
     199              :             buffer.commit(bytes);
     200              :             total += bytes;
     201              :             if (ec) co_return io_result{ec, total};
     202              :             if (bytes == 0) co_return io_result{boost::asio::error::eof, total};
     203              :         }
     204              :         co_return io_result{boost::system::error_code{}, total};
     205           30 :     }
     206              : 
     207           42 :     awaitable<io_result> socket::read_until(boost::asio::streambuf& buffer, std::string_view delim) {
     208              :         // Look for the delimiter in what the buffer holds, then in what is read, as
     209              :         // boost::asio::async_read_until does
     210              :         size_t search_from = 0;
     211              :         for (;;) {
     212              :             auto data = buffer.data();
     213              :             std::string_view held(static_cast<const char*>(data.data()), data.size());
     214              :             auto position = held.find(delim, search_from);
     215              :             if (position != std::string_view::npos) {
     216              :                 co_return io_result{boost::system::error_code{}, position + delim.size()};
     217              :             }
     218              :             // the delimiter may start in the last bytes held
     219              :             search_from = held.size() >= delim.size() ? held.size() - delim.size() + 1 : 0;
     220              :             if (buffer.size() >= buffer.max_size()) {
     221              :                 co_return io_result{boost::asio::error::not_found, 0};
     222              :             }
     223              :             auto read_size = std::min<size_t>(std::max<size_t>(512, buffer.capacity() - buffer.size()),
     224              :                                               std::min<size_t>(65536, buffer.max_size() - buffer.size()));
     225              :             auto space = buffer.prepare(read_size);
     226              :             auto [ec, bytes] = co_await read_some(static_cast<uint8_t*>(space.data()), space.size());
     227              :             buffer.commit(bytes);
     228              :             if (ec) co_return io_result{ec, 0};
     229              :             if (bytes == 0) co_return io_result{boost::asio::error::eof, 0};
     230              :         }
     231           84 :     }
     232              : 
     233            6 :     awaitable<io_result> socket::write(std::string_view str) {
     234              :         co_return co_await write(reinterpret_cast<const uint8_t*>(str.data()), str.size());
     235           12 :     }
     236              : 
     237           45 :     awaitable<io_result> socket::write(const std::vector<boost::asio::const_buffer>& buffers) {
     238              :         size_t total = 0;
     239              :         for (const auto& buffer : buffers) {
     240              :             if (buffer.size() == 0) continue;
     241              :             auto [ec, bytes] = co_await write(static_cast<const uint8_t*>(buffer.data()), buffer.size());
     242              :             total += bytes;
     243              :             if (ec) co_return io_result{ec, total};
     244              :         }
     245              :         co_return io_result{boost::system::error_code{}, total};
     246           90 :     }
     247              : 
     248         7531 :     awaitable<io_result> socket::write_slices(std::vector<boost::asio::const_buffer>& buffers,
     249              :                                               const sync_callback<void()>& progress) {
     250              :         std::vector<boost::asio::const_buffer> slice;
     251              :         size_t total = 0;
     252              :         consume(buffers, 0);
     253              :         while (!buffers.empty()) {
     254              :             slice.clear();
     255              :             for (size_t size = 0, i = 0; i < buffers.size() && size < WRITE_SLICE_SIZE; ++i) {
     256              :                 slice.push_back(boost::asio::buffer(buffers[i], WRITE_SLICE_SIZE - size));
     257              :                 size += slice.back().size();
     258              :             }
     259              :             auto [ec, bytes] = co_await write(slice);
     260              :             total += bytes;
     261              :             consume(buffers, bytes);
     262              :             if (ec) co_return io_result{ec, total};
     263              :             progress();
     264              :         }
     265              :         co_return io_result{boost::system::error_code{}, total};
     266        15062 :     }
     267              : 
     268        54326 :     void socket::consume(std::vector<boost::asio::const_buffer>& buffers, size_t size) {
     269        54326 :         auto remaining = buffers.begin();
     270        79376 :         while (remaining != buffers.end() && size >= remaining->size()) {
     271        25050 :             size -= remaining->size();
     272        25050 :             ++remaining;
     273              :         }
     274        54326 :         if (remaining != buffers.end()) *remaining += size;
     275        54326 :         buffers.erase(buffers.begin(), remaining);
     276        54326 :     }
     277              : 
     278              : }
        

Generated by: LCOV version 2.0-1