• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

libbitcoin / libbitcoin-network / 42421988994

07 Nov 2026 10:44PM UTC coverage: 93.582% (-0.3%) from 93.863%
42421988994

Pull #1011

github

web-flow
Merge 62b70bb20 into ddc7eabb3
Pull Request #1011: Read rpc channel post bodies as json-rpc regardless of content type.

70 of 70 new or added lines in 5 files covered. (100.0%)

58 existing lines in 7 files now uncovered.

14567 of 15566 relevant lines covered (93.58%)

2260.09 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.33
/src/io/socket.cpp
1
/**
2
 * Copyright (c) 2011-2026 libbitcoin developers
3
 *
4
 * This file is part of libbitcoin.
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU Affero General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU Affero General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Affero General Public License
17
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
 */
19
#include <bitcoin/network/io/socket.hpp>
20

21
#include <atomic>
22
#include <bitcoin/network/define.hpp>
23
#include "backend.hpp"
24
#include "native.hpp"
25

26
namespace libbitcoin {
27
namespace network {
28
namespace io {
29

30
using namespace system;
31
using namespace detail;
32

33
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
34
BC_PUSH_WARNING(NO_REINTERPRET_CAST)
35

36
#if defined(HAVE_WINDOWS)
37
    constexpr int shutdown_receive = SD_RECEIVE;
38
    constexpr int shutdown_send = SD_SEND;
39
    constexpr int shutdown_both = SD_BOTH;
40
    constexpr int send_flags = 0;
41
#else
42
    constexpr int shutdown_receive = SHUT_RD;
43
    constexpr int shutdown_send = SHUT_WR;
44
    constexpr int shutdown_both = SHUT_RDWR;
45
#if defined(MSG_NOSIGNAL)
46
    constexpr int send_flags = MSG_NOSIGNAL;
47
#else
48
    constexpr int send_flags = 0;
49
#endif
50
#endif
51

52
static descriptor_ptr make_descriptor(const executor& executor) NOEXCEPT
1,936 ✔
53
{
54
    return std::make_shared<descriptor>(executor.context().scheduler());
1,936 ✔
55
}
56

57
static detail::backend& backend_of(const executor& executor) NOEXCEPT
81,274 ✔
58
{
59
    return executor.context().scheduler().backend();
81,274 ✔
60
}
61

62
static code native_code() NOEXCEPT
169 ✔
63
{
64
    return to_code(last_error());
169 ✔
65
}
66

67
template <typename Option>
68
static code set_socket_option(native_socket socket, int level, int name,
619 ✔
69
    Option value) NOEXCEPT
70
{
71
    if (::setsockopt(socket, level, name, pointer_cast<const char>(&value),
619 ✔
72
        sizeof(value)) != 0)
73
        return native_code();
×
74

75
    return error::success;
619 ✔
76
}
77

78
// Deliver the result on the executor (never within the initiating call).
79
static void post_result(const executor& executor,
22 ✔
80
    const socket::io_handler& handler, const code& ec) NOEXCEPT
81
{
82
    executor.post([handler, ec]() NOEXCEPT
22 ✔
83
    {
84
        handler(ec, zero);
22 ✔
85
    });
22 ✔
86
}
22 ✔
87

88
template <typename Buffers>
89
static size_t total_size(const Buffers& buffers) NOEXCEPT
83,805 ✔
90
{
91
    size_t total{};
92
    for (const auto& buffer: buffers)
167,798 ✔
93
        total = ceilinged_add(total, buffer.size());
83,993 ✔
94

95
    return total;
83,805 ✔
96
}
97

98
#if defined(HAVE_WINDOWS)
99
template <typename Buffers>
100
static std::vector<WSABUF> to_native(const Buffers& buffers) NOEXCEPT
101
{
102
    std::vector<WSABUF> out{};
103
    out.reserve(buffers.size());
104
    for (const auto& buffer: buffers)
105
        out.push_back(
106
        {
107
            possible_narrow_cast<ULONG>(buffer.size()),
108
            pointer_cast<CHAR>(const_cast<void*>(
109
                static_cast<const void*>(buffer.data())))
110
        });
111

112
    return out;
113
}
114
#else
115
template <typename Buffers>
116
static std::vector<iovec> to_native(const Buffers& buffers) NOEXCEPT
5,871 ✔
117
{
118
    std::vector<iovec> out{};
5,871 ✔
119
    out.reserve(buffers.size());
5,871 ✔
120
    for (const auto& buffer: buffers)
11,742 ✔
121
        out.push_back(
5,871 ✔
122
        {
123
            const_cast<void*>(static_cast<const void*>(buffer.data())),
5,871 ✔
124
            buffer.size()
5,871 ✔
125
        });
126

127
    return out;
5,871 ✔
128
}
×
129
#endif
130

131
// tcp
132
// ----------------------------------------------------------------------------
133

134
tcp::tcp(bool v6) NOEXCEPT
1,053 ✔
135
  : v6_(v6)
1,053 ✔
136
{
137
}
1,053 ✔
138

139
tcp tcp::v4() NOEXCEPT
109 ✔
140
{
141
    return tcp{ false };
109 ✔
142
}
143

144
tcp tcp::v6() NOEXCEPT
×
145
{
146
    return tcp{ true };
×
147
}
148

149
tcp tcp::of(const endpoint& point) NOEXCEPT
944 ✔
150
{
151
    return tcp{ point.address().is_v6() };
944 ✔
152
}
153

154
bool tcp::is_v6() const NOEXCEPT
1,053 ✔
155
{
156
    return v6_;
1,053 ✔
157
}
158

159
// socket
160
// ----------------------------------------------------------------------------
161

162
socket::socket(const executor& executor) NOEXCEPT
1,179 ✔
163
  : executor_(executor), descriptor_(make_descriptor(executor))
1,179 ✔
164
{
165
}
1,179 ✔
166

167
socket::socket(socket&& other) NOEXCEPT
175 ✔
168
  : executor_(other.executor_), descriptor_(std::move(other.descriptor_))
175 ✔
169
{
170
    other.descriptor_ = make_descriptor(other.executor_);
175 ✔
171
}
175 ✔
172

173
socket& socket::operator=(socket&& other) NOEXCEPT
×
174
{
175
    if (this != &other)
×
176
    {
177
        code ignore{};
×
178
        close(ignore);
×
179
        executor_ = other.executor_;
×
180
        descriptor_ = std::move(other.descriptor_);
×
181
        other.descriptor_ = make_descriptor(other.executor_);
×
182
    }
183

184
    return *this;
×
185
}
186

187
socket::~socket() NOEXCEPT
1,354 ✔
188
{
189
    code ignore{};
1,354 ✔
190
    close(ignore);
1,354 ✔
191
}
1,354 ✔
192

193
socket::executor_type socket::get_executor() const NOEXCEPT
57 ✔
194
{
195
    return executor_;
57 ✔
196
}
197

198
bool socket::is_open() const NOEXCEPT
82,336 ✔
199
{
200
    return descriptor_->handle != invalid_socket;
82,336 ✔
201
}
202

203
socket::native_handle_type socket::native_handle() const NOEXCEPT
×
204
{
205
    return static_cast<native_handle_type>(descriptor_->handle);
×
206
}
207

208
void socket::open(const tcp& protocol, code& ec) NOEXCEPT
491 ✔
209
{
210
    if (is_open())
491 ✔
211
    {
212
        ec = error::connect_failed;
×
213
        return;
×
214
    }
215

216
    int native{};
491 ✔
217
    backend_of(executor_).open(descriptor_, protocol.is_v6(), native);
491 ✔
218
    ec = to_code(native);
491 ✔
219
}
220

221
void socket::bind(const endpoint& point, code& ec) NOEXCEPT
32 ✔
222
{
223
    const auto address = to_socket_address(point);
32 ✔
224
    if (::bind(descriptor_->handle, address.data(), address.size) != 0)
32 ✔
225
    {
226
        ec = native_code();
1 ✔
227
        return;
1 ✔
228
    }
229

230
    descriptor_->bound = true;
31 ✔
231
    ec = error::success;
31 ✔
232
}
233

234
void socket::close(code& ec) NOEXCEPT
2,519 ✔
235
{
236
    if (is_open())
2,519 ✔
237
        backend_of(executor_).close(*descriptor_);
931 ✔
238

239
    ec = error::success;
2,519 ✔
240
}
2,519 ✔
241

242
void socket::close() NOEXCEPT
271 ✔
243
{
244
    code ignore{};
271 ✔
245
    close(ignore);
271 ✔
246
}
271 ✔
247

248
void socket::cancel(code& ec) NOEXCEPT
4 ✔
249
{
250
    if (!is_open())
4 ✔
251
    {
UNCOV
252
        ec = error::bad_stream;
×
UNCOV
253
        return;
×
254
    }
255

256
    backend_of(executor_).cancel(*descriptor_);
4 ✔
257
    ec = error::success;
4 ✔
258
}
259

260
void socket::shutdown(shutdown_type what, code& ec) NOEXCEPT
547 ✔
261
{
262
    const auto how = what == shutdown_type::receive ? shutdown_receive :
547 ✔
263
        (what == shutdown_type::send ? shutdown_send : shutdown_both);
264

265
    ec = ::shutdown(descriptor_->handle, how) != 0 ? native_code() :
547 ✔
266
        code{ error::success };
267
}
547 ✔
268

269
void socket::set_option(const no_delay& option, code& ec) NOEXCEPT
348 ✔
270
{
271
    ec = set_socket_option(descriptor_->handle, IPPROTO_TCP, TCP_NODELAY,
348 ✔
272
        option.value ? 1 : 0);
348 ✔
273
}
348 ✔
274

275
void socket::set_option(const keep_alive& option, code& ec) NOEXCEPT
×
276
{
277
    ec = set_socket_option(descriptor_->handle, SOL_SOCKET, SO_KEEPALIVE,
×
278
        option.value ? 1 : 0);
×
279
}
×
280

281
void socket::non_blocking(bool value, code& ec) NOEXCEPT
3 ✔
282
{
283
    // Posix sockets are always nonblocking (blocking calls wait on poll).
284
#if defined(HAVE_WINDOWS)
285
    int native{};
286
    if (!set_blocking(descriptor_->handle, !value, native))
287
    {
288
        ec = to_code(native);
289
        return;
290
    }
291
#endif
292

293
    descriptor_->non_blocking = value;
3 ✔
294
    ec = error::success;
3 ✔
295
}
3 ✔
296

297
bool socket::non_blocking() const NOEXCEPT
1 ✔
298
{
299
    return descriptor_->non_blocking;
1 ✔
300
}
301

302
size_t socket::available(code& ec) const NOEXCEPT
1 ✔
303
{
304
#if defined(HAVE_WINDOWS)
305
    u_long count{};
306
    if (::ioctlsocket(descriptor_->handle, FIONREAD, &count) != 0)
307
#else
308
    int count{};
1 ✔
309
    if (::ioctl(descriptor_->handle, FIONREAD, &count) != 0)
1 ✔
310
#endif
311
    {
312
        ec = native_code();
×
313
        return zero;
×
314
    }
315

316
    ec = error::success;
1 ✔
317
#if defined(HAVE_WINDOWS)
318
    return possible_wide_cast<size_t>(count);
319
#else
320
    return possible_wide_cast<size_t>(sign_cast<unsigned int>(count));
1 ✔
321
#endif
322
}
323

324
endpoint socket::local_endpoint(code& ec) const NOEXCEPT
402 ✔
325
{
326
    socket_address address{};
402 ✔
327
    address.size = sizeof(address.storage);
402 ✔
328
    if (::getsockname(descriptor_->handle, address.data(), &address.size) != 0)
402 ✔
329
    {
330
        ec = native_code();
×
331
        return {};
×
332
    }
333

334
    ec = error::success;
402 ✔
335
    return to_endpoint(address.data());
402 ✔
336
}
337

338
endpoint socket::remote_endpoint(code& ec) const NOEXCEPT
268 ✔
339
{
340
    socket_address address{};
268 ✔
341
    address.size = sizeof(address.storage);
268 ✔
342
    if (::getpeername(descriptor_->handle, address.data(), &address.size) != 0)
268 ✔
343
    {
344
        ec = native_code();
×
345
        return {};
×
346
    }
347

348
    ec = error::success;
268 ✔
349
    return to_endpoint(address.data());
268 ✔
350
}
351

352
endpoint socket::local_endpoint() const NOEXCEPT
34 ✔
353
{
354
    code ignore{};
34 ✔
355
    return local_endpoint(ignore);
34 ✔
356
}
357

358
endpoint socket::remote_endpoint() const NOEXCEPT
4 ✔
359
{
360
    code ignore{};
4 ✔
361
    return remote_endpoint(ignore);
4 ✔
362
}
363

364
bool socket::half_closed() const NOEXCEPT
13 ✔
365
{
366
    if (!is_open())
13 ✔
367
        return false;
368

369
#if defined(HAVE_WINDOWS)
370
    TCP_INFO_v0 info{};
371
    DWORD version{};
372
    DWORD size{};
373
    if (is_zero(::WSAIoctl(descriptor_->handle, SIO_TCP_INFO, &version,
374
        sizeof(version), &info, sizeof(info), &size, nullptr, nullptr)))
375
        return info.State == TCPSTATE_CLOSE_WAIT;
376

377
#elif defined(HAVE_APPLE)
378
    struct tcp_connection_info info{};
379
    socklen_t size = sizeof(info);
380
    if (is_zero(::getsockopt(descriptor_->handle, IPPROTO_TCP,
381
        TCP_CONNECTION_INFO, &info, &size)))
382
        return info.tcpi_state == TCPS_CLOSE_WAIT;
383

384
#elif defined(HAVE_LINUX)
385
    struct tcp_info info{};
12 ✔
386
    socklen_t size = sizeof(info);
12 ✔
387
    if (is_zero(::getsockopt(descriptor_->handle, IPPROTO_TCP, TCP_INFO,
12 ✔
388
        &info, &size)))
389
        return info.tcpi_state == TCP_CLOSE_WAIT;
12 ✔
390
#endif
391
    return false;
392
}
393

394
void socket::async_connect(const endpoint& point,
191 ✔
395
    result_handler&& handler) NOEXCEPT
396
{
397
    io_handler completion = [handler = std::move(handler)](const code& ec,
382 ✔
398
        size_t) NOEXCEPT
399
    {
400
        handler(ec);
191 ✔
401
    };
191 ✔
402

403
    if (!is_open())
191 ✔
404
    {
405
        code ec{};
188 ✔
406
        open(tcp::of(point), ec);
188 ✔
407
        if (ec)
188 ✔
408
        {
409
            post_result(executor_, completion, ec);
×
410
            return;
×
411
        }
412
    }
413

414
    const auto op = new operation{ operation::kind::connect, descriptor_,
191 ✔
415
        executor_ };
191 ✔
416
    op->target = to_socket_address(point);
191 ✔
417
    op->handler = std::move(completion);
191 ✔
418
    backend_of(executor_).start(op);
191 ✔
419
}
191 ✔
420

421
void socket::do_read(mutable_buffers&& buffers, io_handler&& handler) NOEXCEPT
76,984 ✔
422
{
423
    if (!is_open())
76,984 ✔
424
    {
425
        post_result(executor_, handler, error::bad_stream);
11 ✔
426
        return;
11 ✔
427
    }
428

429
    const auto op = new operation{ operation::kind::read, descriptor_,
76,973 ✔
430
        executor_ };
76,973 ✔
431
    op->handler = std::move(handler);
76,973 ✔
432
    op->requested = total_size(buffers);
76,973 ✔
433
#if defined(HAVE_WINDOWS)
434
    op->buffers = to_native(buffers);
435
#else
436
    op->in = std::move(buffers);
76,973 ✔
437
#endif
438
    backend_of(executor_).start(op);
76,973 ✔
439
}
440

441
void socket::do_write(const_buffers&& buffers, io_handler&& handler) NOEXCEPT
971 ✔
442
{
443
    if (!is_open())
971 ✔
444
    {
445
        post_result(executor_, handler, error::bad_stream);
10 ✔
446
        return;
10 ✔
447
    }
448

449
    const auto op = new operation{ operation::kind::write, descriptor_,
961 ✔
450
        executor_ };
961 ✔
451
    op->handler = std::move(handler);
961 ✔
452
    op->requested = total_size(buffers);
961 ✔
453
#if defined(HAVE_WINDOWS)
454
    op->buffers = to_native(buffers);
455
#else
456
    op->out = std::move(buffers);
961 ✔
457
#endif
458
    backend_of(executor_).start(op);
961 ✔
459
}
460

461
// Synchronous.
462
// ----------------------------------------------------------------------------
463

464
void socket::connect(const endpoint& point, code& ec) NOEXCEPT
269 ✔
465
{
466
    if (!is_open())
269 ✔
467
    {
468
        open(tcp::of(point), ec);
269 ✔
469
        if (ec)
269 ✔
470
            return;
×
471
    }
472

473
    const auto address = to_socket_address(point);
269 ✔
474
    const auto handle = descriptor_->handle;
269 ✔
475
    if (::connect(handle, address.data(), address.size) == 0)
269 ✔
476
    {
477
        ec = error::success;
×
478
        return;
×
479
    }
480

481
    auto native = last_error();
269 ✔
482
    if (!is_in_progress(native) || descriptor_->non_blocking)
269 ✔
483
    {
484
        ec = to_code(native);
×
485
        return;
×
486
    }
487

488
    if (!wait_ready(handle, true, native))
269 ✔
489
    {
490
        ec = to_code(native);
×
491
        return;
×
492
    }
493

494
    int result{};
269 ✔
495
    socklen_t size = sizeof(result);
269 ✔
496
    if (::getsockopt(handle, SOL_SOCKET, SO_ERROR,
269 ✔
497
        pointer_cast<char>(&result), &size) != 0)
269 ✔
498
        result = last_error();
×
499

500
    ec = to_code(result);
269 ✔
501
}
502

503
size_t socket::do_read_some(const mutable_buffers& buffers, code& ec) NOEXCEPT
5,185 ✔
504
{
505
    const auto requested = total_size(buffers);
5,185 ✔
506
    if (is_zero(requested))
5,185 ✔
507
    {
508
        ec = error::success;
×
509
        return zero;
×
510
    }
511

512
    auto native_buffers = to_native(buffers);
5,185 ✔
513
    while (true)
514
    {
515
#if defined(HAVE_WINDOWS)
516
        DWORD bytes{};
517
        DWORD flags{};
518
        const auto result = ::WSARecv(descriptor_->handle,
519
            native_buffers.data(),
520
            possible_narrow_cast<DWORD>(native_buffers.size()), &bytes,
521
            &flags, nullptr, nullptr);
522

523
        const auto failed = result == SOCKET_ERROR;
524
        const size_t count = bytes;
525
#else
526
        const auto result = ::readv(descriptor_->handle, native_buffers.data(),
5,978 ✔
527
            possible_narrow_sign_cast<int>(native_buffers.size()));
528

529
        const auto failed = result < 0;
530
        const auto count = failed ? zero :
5,978 ✔
531
            possible_narrow_sign_cast<size_t>(result);
5,183 ✔
532
#endif
533
        if (!failed)
5,978 ✔
534
        {
535
            ec = is_zero(count) ? code{ error::peer_disconnect } :
5,183 ✔
536
                code{ error::success };
537

538
            return count;
5,183 ✔
539
        }
540

541
        auto native = last_error();
795 ✔
542
#if !defined(HAVE_WINDOWS)
543
        if (native == EINTR)
795 ✔
544
            continue;
×
545
#endif
546
        if (is_would_block(native) && !descriptor_->non_blocking &&
1,588 ✔
547
            wait_ready(descriptor_->handle, false, native))
793 ✔
548
            continue;
793 ✔
549

550
        ec = to_code(native);
2 ✔
551
        return zero;
552
    }
793 ✔
553
}
5,185 ✔
554

555
size_t socket::do_write_some(const const_buffers& buffers, code& ec) NOEXCEPT
686 ✔
556
{
557
    const auto requested = total_size(buffers);
686 ✔
558
    if (is_zero(requested))
686 ✔
559
    {
560
        ec = error::success;
×
561
        return zero;
×
562
    }
563

564
    auto native_buffers = to_native(buffers);
686 ✔
565
    while (true)
566
    {
567
#if defined(HAVE_WINDOWS)
568
        DWORD bytes{};
569
        const auto result = ::WSASend(descriptor_->handle,
570
            native_buffers.data(),
571
            possible_narrow_cast<DWORD>(native_buffers.size()), &bytes,
572
            send_flags, nullptr, nullptr);
573

574
        const auto failed = result == SOCKET_ERROR;
575
        const size_t count = bytes;
576
#else
577
        msghdr message{};
686 ✔
578
        message.msg_iov = native_buffers.data();
686 ✔
579
        message.msg_iovlen = native_buffers.size();
686 ✔
580
        const auto result = ::sendmsg(descriptor_->handle, &message,
686 ✔
581
            send_flags);
582

583
        const auto failed = result < 0;
584
        const auto count = failed ? zero :
686 ✔
585
            possible_narrow_sign_cast<size_t>(result);
686 ✔
586
#endif
587
        if (!failed)
686 ✔
588
        {
589
            ec = error::success;
686 ✔
590
            return count;
686 ✔
591
        }
592

593
        auto native = last_error();
×
594
#if !defined(HAVE_WINDOWS)
595
        if (native == EINTR)
×
596
            continue;
×
597
#endif
598
        if (is_would_block(native) && !descriptor_->non_blocking &&
×
599
            wait_ready(descriptor_->handle, true, native))
×
600
            continue;
×
601

602
        ec = to_code(native);
×
603
        return zero;
604
    }
×
605
}
686 ✔
606

607
// acceptor
608
// ----------------------------------------------------------------------------
609

610
acceptor::acceptor(const executor& executor) NOEXCEPT
582 ✔
611
  : executor_(executor), descriptor_(make_descriptor(executor))
582 ✔
612
{
613
}
582 ✔
614

615
acceptor::acceptor(const executor& executor, const endpoint& point) NOEXCEPT
94 ✔
616
  : acceptor(executor)
94 ✔
617
{
618
    code ec{};
94 ✔
619
    open(tcp::of(point), ec);
94 ✔
620
    if (!ec) set_option(reuse_address{ true }, ec);
94 ✔
621
    if (!ec) bind(point, ec);
94 ✔
622
    if (!ec) listen(ec);
94 ✔
623
    if (ec) close(ec);
94 ✔
624
}
94 ✔
625

626
acceptor::~acceptor() NOEXCEPT
582 ✔
627
{
628
    code ignore{};
582 ✔
629
    close(ignore);
582 ✔
630
}
582 ✔
631

632
acceptor::executor_type acceptor::get_executor() const NOEXCEPT
×
633
{
634
    return executor_;
×
635
}
636

637
bool acceptor::is_open() const NOEXCEPT
2,011 ✔
638
{
639
    return descriptor_->handle != invalid_socket;
2,011 ✔
640
}
641

642
acceptor::native_handle_type acceptor::native_handle() const NOEXCEPT
×
643
{
644
    return static_cast<native_handle_type>(descriptor_->handle);
×
645
}
646

647
void acceptor::open(const tcp& protocol, code& ec) NOEXCEPT
562 ✔
648
{
649
    if (is_open())
562 ✔
650
    {
651
        ec = error::connect_failed;
×
652
        return;
×
653
    }
654

655
    int native{};
562 ✔
656
    backend_of(executor_).open(descriptor_, protocol.is_v6(), native);
562 ✔
657
    ec = to_code(native);
562 ✔
658
}
659

660
void acceptor::set_option(const reuse_address& option, code& ec) NOEXCEPT
268 ✔
661
{
662
    ec = set_socket_option(descriptor_->handle, SOL_SOCKET, SO_REUSEADDR,
268 ✔
663
        option.value ? 1 : 0);
268 ✔
664
}
268 ✔
665

666
void acceptor::set_option(const v6_only& option, code& ec) NOEXCEPT
3 ✔
667
{
668
    ec = set_socket_option(descriptor_->handle, IPPROTO_IPV6, IPV6_V6ONLY,
3 ✔
669
        option.value ? 1 : 0);
3 ✔
670
}
3 ✔
671

672
void acceptor::bind(const endpoint& point, code& ec) NOEXCEPT
562 ✔
673
{
674
    const auto address = to_socket_address(point);
562 ✔
675
    if (::bind(descriptor_->handle, address.data(), address.size) != 0)
562 ✔
676
    {
677
        ec = native_code();
6 ✔
678
        return;
6 ✔
679
    }
680

681
    descriptor_->bound = true;
556 ✔
682
    ec = error::success;
556 ✔
683
}
684

685
void acceptor::listen(code& ec) NOEXCEPT
556 ✔
686
{
687
    ec = ::listen(descriptor_->handle, SOMAXCONN) != 0 ? native_code() :
556 ✔
688
        code{ error::success };
689
}
556 ✔
690

691
void acceptor::close(code& ec) NOEXCEPT
819 ✔
692
{
693
    if (is_open())
819 ✔
694
        backend_of(executor_).close(*descriptor_);
562 ✔
695

696
    ec = error::success;
819 ✔
697
}
819 ✔
698

699
void acceptor::close() NOEXCEPT
55 ✔
700
{
701
    code ignore{};
55 ✔
702
    close(ignore);
55 ✔
703
}
55 ✔
704

705
void acceptor::cancel(code& ec) NOEXCEPT
145 ✔
706
{
707
    if (!is_open())
145 ✔
708
    {
709
        ec = error::bad_stream;
7 ✔
710
        return;
7 ✔
711
    }
712

713
    backend_of(executor_).cancel(*descriptor_);
138 ✔
714
    ec = error::success;
138 ✔
715
}
716

717
endpoint acceptor::local_endpoint(code& ec) const NOEXCEPT
467 ✔
718
{
719
    socket_address address{};
467 ✔
720
    address.size = sizeof(address.storage);
467 ✔
721
    if (::getsockname(descriptor_->handle, address.data(), &address.size) != 0)
467 ✔
722
    {
723
        ec = native_code();
×
724
        return {};
×
725
    }
726

727
    ec = error::success;
467 ✔
728
    return to_endpoint(address.data());
467 ✔
729
}
730

731
endpoint acceptor::local_endpoint() const NOEXCEPT
463 ✔
732
{
733
    code ignore{};
463 ✔
734
    return local_endpoint(ignore);
463 ✔
735
}
736

737
void acceptor::async_accept(socket& peer, result_handler&& handler) NOEXCEPT
354 ✔
738
{
739
    socket::io_handler completion = [handler = std::move(handler)](const code& ec,
708 ✔
740
        size_t) NOEXCEPT
741
    {
742
        handler(ec);
354 ✔
743
    };
354 ✔
744

745
    if (!is_open() || peer.is_open())
354 ✔
746
    {
747
        post_result(executor_, completion, is_open() ?
1 ✔
748
            error::connect_failed : error::bad_stream);
749
        return;
750
    }
751

752
    const auto op = new operation{ operation::kind::accept, descriptor_,
353 ✔
753
        executor_ };
353 ✔
754
    op->peer = peer.descriptor_;
353 ✔
755
    op->handler = std::move(completion);
353 ✔
756
    backend_of(executor_).start(op);
353 ✔
757
}
354 ✔
758

759
void acceptor::accept(socket& peer, code& ec) NOEXCEPT
108 ✔
760
{
761
    if (!is_open() || peer.is_open())
108 ✔
762
    {
763
        ec = is_open() ? error::connect_failed : error::bad_stream;
×
764
        return;
×
765
    }
766

767
    while (true)
768
    {
769
        const auto accepted = ::accept(descriptor_->handle, nullptr, nullptr);
210 ✔
770
        if (accepted != invalid_socket)
210 ✔
771
        {
772
            int native{};
108 ✔
773
#if !defined(HAVE_WINDOWS)
774
            ::fcntl(accepted, F_SETFD, FD_CLOEXEC);
108 ✔
775
            if (!set_blocking(accepted, false, native))
108 ✔
776
            {
777
                close_socket(accepted);
×
778
                ec = to_code(native);
×
779
                return;
×
780
            }
781
#endif
782
            if (!backend_of(executor_).adopt(peer.descriptor_, accepted,
216 ✔
783
                descriptor_->v6, native))
108 ✔
784
            {
785
                close_socket(accepted);
×
786
                ec = to_code(native);
×
787
                return;
×
788
            }
789

790
            ec = error::success;
108 ✔
791
            return;
108 ✔
792
        }
793

794
        auto native = last_error();
102 ✔
795
#if !defined(HAVE_WINDOWS)
796
        if (native == EINTR || native == ECONNABORTED)
102 ✔
797
            continue;
×
798
#endif
799
        if (is_would_block(native) && !descriptor_->non_blocking &&
204 ✔
800
            wait_ready(descriptor_->handle, false, native))
102 ✔
801
            continue;
102 ✔
802

803
        ec = to_code(native);
×
804
        return;
805
    }
102 ✔
806
}
807

808
// resolver
809
// ----------------------------------------------------------------------------
810

811
struct resolver::state
812
{
813
    std::atomic<size_t> cancels{};
814
};
815

816
static code resolve_code(int result) NOEXCEPT
128 ✔
817
{
818
    switch (result)
128 ✔
819
    {
820
        case 0:
124 ✔
821
            return error::success;
124 ✔
822
#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
823
        case EAI_NODATA:
×
824
#endif
825
        case EAI_FAIL:
826
            return error::operation_failed;
×
827
        default:
4 ✔
828
            return error::resolve_failed;
4 ✔
829
    }
830
}
831

832
resolver::resolver(const executor& executor) NOEXCEPT
274 ✔
833
  : executor_(executor), state_(std::make_shared<state>())
274 ✔
834
{
835
}
274 ✔
836

837
resolver::~resolver() NOEXCEPT
274 ✔
838
{
839
    cancel();
274 ✔
840
}
274 ✔
841

842
resolver::executor_type resolver::get_executor() const NOEXCEPT
×
843
{
844
    return executor_;
×
845
}
846

847
void resolver::async_resolve(const std::string& host,
128 ✔
848
    const std::string& service, resolve_handler&& handler) NOEXCEPT
849
{
850
    auto& scheduler = executor_.context().scheduler();
128 ✔
851
    scheduler.work_started();
128 ✔
852

853
    const auto started = state_->cancels.load();
128 ✔
854
    scheduler.resolve([&scheduler, state = state_, started,
256 ✔
855
        executor = executor_, host, service,
856
        handler = std::move(handler)]() NOEXCEPT
128 ✔
857
    {
858
        addrinfo hints{};
128 ✔
859
        hints.ai_family = AF_UNSPEC;
860
        hints.ai_socktype = SOCK_STREAM;
128 ✔
861
        hints.ai_protocol = IPPROTO_TCP;
128 ✔
862
        hints.ai_flags = AI_ADDRCONFIG;
128 ✔
863

864
        addrinfo* results{};
128 ✔
865
        const auto result = ::getaddrinfo(host.c_str(), service.c_str(),
128 ✔
866
            &hints, &results);
867

868
        endpoints range{};
128 ✔
869
        for (auto it = results; it != nullptr; it = it->ai_next)
252 ✔
870
            if (it->ai_family == AF_INET || it->ai_family == AF_INET6)
124 ✔
871
                range.push_back(to_endpoint(it->ai_addr));
124 ✔
872

873
        if (results != nullptr)
128 ✔
874
            ::freeaddrinfo(results);
124 ✔
875

876
        scheduler.complete([state, started, executor, handler,
256 ✔
877
            ec = resolve_code(result), range = std::move(range)]() NOEXCEPT
128 ✔
878
        {
879
            const auto canceled = state->cancels.load() != started;
128 ✔
880
            const code value = canceled ? error::operation_canceled : ec;
128 ✔
881
            executor.dispatch([handler, value, range]() NOEXCEPT
128 ✔
882
            {
883
                handler(value, value ? endpoints{} : range);
128 ✔
884
            });
128 ✔
885
        });
128 ✔
886
    });
128 ✔
887
}
128 ✔
888

889
void resolver::cancel() NOEXCEPT
316 ✔
890
{
891
    ++state_->cancels;
316 ✔
892
}
316 ✔
893

894
// async_connect (range)
895
// ----------------------------------------------------------------------------
896

897
namespace {
898

899
struct range_connect
900
  : std::enable_shared_from_this<range_connect>
901
{
902
    range_connect(io::socket& socket, const endpoints& range,
120 ✔
903
        connect_handler&& handler) NOEXCEPT
904
      : socket(socket), range(range), handler(std::move(handler))
120 ✔
905
    {
906
    }
120 ✔
907

908
    void next() NOEXCEPT
135 ✔
909
    {
910
        if (index == range.size())
135 ✔
911
        {
912
            const auto ec = last ? last : code{ error::resolve_failed };
15 ✔
913
            socket.get_executor().post([self = shared_from_this(),
15 ✔
914
                ec]() NOEXCEPT
915
            {
916
                self->handler(ec, {});
15 ✔
917
            });
15 ✔
918

919
            return;
920
        }
921

922
        code ignore{};
120 ✔
923
        socket.close(ignore);
120 ✔
924
        socket.async_connect(range.at(index),
120 ✔
925
            [self = shared_from_this()](const code& ec) NOEXCEPT
240 ✔
926
            {
927
                self->handle(ec);
120 ✔
928
            });
120 ✔
929
    }
930

931
    void handle(const code& ec) NOEXCEPT
120 ✔
932
    {
933
        if (!ec || ec == error::operation_canceled)
120 ✔
934
        {
935
            handler(ec, ec ? endpoint{} : range.at(index));
103 ✔
936
            return;
103 ✔
937
        }
938

939
        if (!socket.is_open())
17 ✔
940
        {
941
            handler(error::operation_canceled, {});
2 ✔
942
            return;
2 ✔
943
        }
944

945
        last = ec;
15 ✔
946
        ++index;
15 ✔
947
        next();
15 ✔
948
    }
949

950
    io::socket& socket;
951
    const endpoints range;
952
    const connect_handler handler;
953
    size_t index{};
954
    code last{};
955
};
956

957
} // namespace
958

959
void async_connect(io::socket& socket, const endpoints& range,
120 ✔
960
    connect_handler&& handler) NOEXCEPT
961
{
962
    std::make_shared<range_connect>(socket, range,
120 ✔
963
        std::move(handler))->next();
120 ✔
964
}
120 ✔
965

966
BC_POP_WARNING()
967
BC_POP_WARNING()
968

969
} // namespace io
970
} // namespace network
971
} // namespace libbitcoin
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc