Line data Source code
1 : #include <iostream>
2 : #include "tcp_socket.hpp"
3 :
4 : namespace thinger::asio {
5 :
6 769493 : tcp_socket::tcp_socket(const std::string& context, boost::asio::io_context& io_context)
7 769493 : : socket(context, io_context), socket_(io_context) {
8 769493 : }
9 :
10 0 : tcp_socket::tcp_socket(const std::string& context, const std::shared_ptr<tcp_socket>& tcp_socket)
11 0 : : socket(context, tcp_socket->get_io_context()), socket_(std::move(tcp_socket->get_socket())) {
12 0 : }
13 :
14 15 : tcp_socket::tcp_socket(const std::string& context, boost::asio::ip::tcp::socket&& sock)
15 15 : : socket(context, static_cast<boost::asio::io_context&>(sock.get_executor().context())), socket_(std::move(sock)) {
16 15 : }
17 :
18 769508 : tcp_socket::~tcp_socket() {
19 769508 : LOG_TRACE("releasing tcp connection");
20 769508 : close();
21 769508 : }
22 :
23 770687 : void tcp_socket::close() {
24 770687 : boost::system::error_code ec;
25 770687 : if (socket_.is_open()) {
26 1901 : socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
27 : }
28 770687 : socket_.close(ec);
29 770687 : LOG_TRACE("closing tcp socket result: {}", ec.message());
30 770687 : }
31 :
32 104 : void tcp_socket::cancel() {
33 104 : socket_.cancel();
34 100 : }
35 :
36 927 : awaitable<boost::system::error_code> tcp_socket::connect(
37 : const std::string& host,
38 : const std::string& port,
39 : std::chrono::seconds timeout)
40 : {
41 : close();
42 :
43 : // Resolve host
44 : boost::asio::ip::tcp::resolver resolver(io_context_);
45 : auto [ec_resolve, endpoints] = co_await resolver.async_resolve(
46 : host, port, use_nothrow_awaitable);
47 :
48 : if (ec_resolve) {
49 : co_return ec_resolve;
50 : }
51 :
52 : // Setup timeout timer
53 : boost::asio::steady_timer timer(io_context_);
54 : timer.expires_after(timeout);
55 :
56 : // Race between connect and timeout
57 : bool timed_out = false;
58 : boost::system::error_code connect_ec;
59 :
60 : // Start timeout
61 918 : auto timeout_coro = [&]() -> awaitable<void> {
62 : auto [ec] = co_await timer.async_wait(use_nothrow_awaitable);
63 : if (!ec) {
64 : timed_out = true;
65 : socket_.cancel();
66 : }
67 1836 : };
68 :
69 : // Start connect
70 918 : auto connect_coro = [&]() -> awaitable<void> {
71 : auto [ec, endpoint] = co_await boost::asio::async_connect(
72 : socket_, endpoints, use_nothrow_awaitable);
73 : timer.cancel();
74 : if (ec) {
75 : connect_ec = timed_out ? boost::asio::error::timed_out : ec;
76 : }
77 1836 : };
78 :
79 : // Wait for connect (timeout will cancel if needed)
80 : co_spawn(io_context_, timeout_coro(), detached);
81 : co_await connect_coro();
82 :
83 : if (connect_ec) {
84 : co_return connect_ec;
85 : }
86 :
87 : // Run handshake if required (for SSL sockets)
88 : if (requires_handshake()) {
89 : auto hs_ec = co_await handshake(host);
90 : if (hs_ec) {
91 : close();
92 : co_return hs_ec;
93 : }
94 : }
95 :
96 : co_return boost::system::error_code{};
97 1854 : }
98 :
99 1704 : boost::asio::ip::tcp::socket& tcp_socket::get_socket() {
100 1704 : return socket_;
101 : }
102 :
103 971 : std::string tcp_socket::get_remote_ip() const {
104 971 : boost::system::error_code ec;
105 971 : auto remote_ep = socket_.remote_endpoint(ec);
106 971 : if (!ec) {
107 971 : return remote_ep.address().to_string();
108 : }
109 0 : return "0.0.0.0";
110 : }
111 :
112 974 : std::string tcp_socket::get_local_port() const {
113 974 : boost::system::error_code ec;
114 974 : auto local_ep = socket_.local_endpoint(ec);
115 974 : if (!ec) {
116 974 : return std::to_string(local_ep.port());
117 : }
118 0 : return "0";
119 : }
120 :
121 3 : std::string tcp_socket::get_remote_port() const {
122 3 : boost::system::error_code ec;
123 3 : auto remote_ep = socket_.remote_endpoint(ec);
124 3 : if (!ec) {
125 3 : return std::to_string(remote_ep.port());
126 : }
127 0 : return "0";
128 : }
129 :
130 4178 : awaitable<io_result> tcp_socket::read_some(uint8_t* buffer, size_t max_size) {
131 : co_return co_await socket_.async_read_some(
132 : boost::asio::buffer(buffer, max_size),
133 : use_nothrow_awaitable);
134 8356 : }
135 :
136 559 : awaitable<io_result> tcp_socket::read(uint8_t* buffer, size_t size) {
137 : co_return co_await boost::asio::async_read(
138 : socket_,
139 : boost::asio::buffer(buffer, size),
140 : boost::asio::transfer_exactly(size),
141 : use_nothrow_awaitable);
142 1118 : }
143 :
144 3 : awaitable<io_result> tcp_socket::read(boost::asio::streambuf& buffer, size_t size) {
145 : co_return co_await boost::asio::async_read(
146 : socket_,
147 : buffer,
148 : boost::asio::transfer_exactly(size),
149 : use_nothrow_awaitable);
150 6 : }
151 :
152 3 : awaitable<io_result> tcp_socket::read_until(boost::asio::streambuf& buffer, std::string_view delim) {
153 : co_return co_await boost::asio::async_read_until(
154 : socket_,
155 : buffer,
156 : std::string(delim),
157 : use_nothrow_awaitable);
158 6 : }
159 :
160 846 : awaitable<io_result> tcp_socket::write(const uint8_t* buffer, size_t size) {
161 : co_return co_await boost::asio::async_write(
162 : socket_,
163 : boost::asio::buffer(buffer, size),
164 : use_nothrow_awaitable);
165 1692 : }
166 :
167 12 : awaitable<io_result> tcp_socket::write(std::string_view str) {
168 : co_return co_await boost::asio::async_write(
169 : socket_,
170 : boost::asio::buffer(str.data(), str.size()),
171 : use_nothrow_awaitable);
172 24 : }
173 :
174 1943 : awaitable<io_result> tcp_socket::write(const std::vector<boost::asio::const_buffer>& buffers) {
175 : co_return co_await boost::asio::async_write(
176 : socket_,
177 : buffers,
178 : use_nothrow_awaitable);
179 3886 : }
180 :
181 6 : awaitable<boost::system::error_code> tcp_socket::wait(boost::asio::socket_base::wait_type type) {
182 : auto [ec] = co_await socket_.async_wait(type, use_nothrow_awaitable);
183 : co_return ec;
184 12 : }
185 :
186 973 : void tcp_socket::enable_tcp_no_delay() {
187 973 : socket_.set_option(boost::asio::ip::tcp::no_delay(true));
188 971 : }
189 :
190 5 : void tcp_socket::disable_tcp_no_delay() {
191 5 : socket_.set_option(boost::asio::ip::tcp::no_delay(false));
192 3 : }
193 :
194 5131 : bool tcp_socket::is_open() const {
195 5131 : return socket_.is_open();
196 : }
197 :
198 1860 : bool tcp_socket::is_secure() const {
199 1860 : return false;
200 : }
201 :
202 7 : size_t tcp_socket::available() const {
203 7 : boost::system::error_code ec;
204 7 : auto size = socket_.available(ec);
205 7 : if (ec) {
206 4 : LOG_ERROR("error while getting socket available bytes ({}): {}", size, ec.message());
207 : }
208 7 : return size;
209 : }
210 :
211 : }
|