librats 1.0.2 → 2.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +137 -332
- package/binding.gyp +16 -3
- package/lib/index.d.ts +271 -696
- package/lib/index.js +383 -44
- package/native-src/3rdparty/android/ifaddrs-android.c +1 -0
- package/native-src/3rdparty/android/ifaddrs-android.h +1 -0
- package/native-src/CMakeLists.txt +396 -179
- package/native-src/LICENSE +1 -1
- package/native-src/src/librats/bindings/rats.cpp +731 -0
- package/native-src/src/librats/bindings/rats.h +352 -0
- package/native-src/src/librats/bittorrent/bencode.cpp +437 -0
- package/native-src/src/librats/bittorrent/bencode.h +176 -0
- package/native-src/src/librats/bittorrent/bitfield.cpp +97 -0
- package/native-src/src/librats/bittorrent/bitfield.h +76 -0
- package/native-src/src/librats/bittorrent/byte_io.h +58 -0
- package/native-src/src/librats/bittorrent/choker.cpp +25 -0
- package/native-src/src/librats/bittorrent/choker.h +46 -0
- package/native-src/src/librats/bittorrent/client.cpp +413 -0
- package/native-src/src/librats/bittorrent/client.h +227 -0
- package/native-src/src/librats/bittorrent/disk_io.cpp +209 -0
- package/native-src/src/librats/bittorrent/disk_io.h +150 -0
- package/native-src/src/librats/bittorrent/extensions.cpp +191 -0
- package/native-src/src/librats/bittorrent/extensions.h +94 -0
- package/native-src/src/librats/bittorrent/file_storage.cpp +77 -0
- package/native-src/src/librats/bittorrent/file_storage.h +79 -0
- package/native-src/src/librats/bittorrent/log.h +42 -0
- package/native-src/src/librats/bittorrent/magnet_uri.cpp +98 -0
- package/native-src/src/librats/bittorrent/magnet_uri.h +35 -0
- package/native-src/src/librats/bittorrent/peer_connection.cpp +502 -0
- package/native-src/src/librats/bittorrent/peer_connection.h +194 -0
- package/native-src/src/librats/bittorrent/peer_list.cpp +68 -0
- package/native-src/src/librats/bittorrent/peer_list.h +75 -0
- package/native-src/src/librats/bittorrent/piece_picker.cpp +352 -0
- package/native-src/src/librats/bittorrent/piece_picker.h +201 -0
- package/native-src/src/librats/bittorrent/reactor.cpp +97 -0
- package/native-src/src/librats/bittorrent/reactor.h +89 -0
- package/native-src/src/librats/bittorrent/resume_data.cpp +72 -0
- package/native-src/src/librats/bittorrent/resume_data.h +41 -0
- package/native-src/src/librats/bittorrent/store_buffer.cpp +48 -0
- package/native-src/src/librats/bittorrent/store_buffer.h +47 -0
- package/native-src/src/librats/bittorrent/torrent.cpp +870 -0
- package/native-src/src/librats/bittorrent/torrent.h +260 -0
- package/native-src/src/librats/bittorrent/torrent_creator.cpp +129 -0
- package/native-src/src/librats/bittorrent/torrent_creator.h +58 -0
- package/native-src/src/librats/bittorrent/torrent_info.cpp +314 -0
- package/native-src/src/librats/bittorrent/torrent_info.h +118 -0
- package/native-src/src/librats/bittorrent/tracker.cpp +374 -0
- package/native-src/src/librats/bittorrent/tracker.h +108 -0
- package/native-src/src/librats/bittorrent/types.cpp +206 -0
- package/native-src/src/librats/bittorrent/types.h +86 -0
- package/native-src/src/librats/core/address.cpp +35 -0
- package/native-src/src/librats/core/address.h +78 -0
- package/native-src/src/librats/core/bytes.h +69 -0
- package/native-src/src/librats/core/chained_send_buffer.cpp +172 -0
- package/native-src/src/librats/core/chained_send_buffer.h +183 -0
- package/native-src/src/librats/core/endpoint_parse.cpp +41 -0
- package/native-src/src/librats/core/endpoint_parse.h +31 -0
- package/native-src/src/librats/core/event_bus.h +70 -0
- package/native-src/src/librats/core/host_endpoint.h +56 -0
- package/native-src/src/{io_poller.cpp → librats/core/io_poller.cpp} +520 -65
- package/native-src/src/{io_poller.h → librats/core/io_poller.h} +12 -6
- package/native-src/src/librats/core/ip_address.cpp +120 -0
- package/native-src/src/librats/core/ip_address.h +109 -0
- package/native-src/src/librats/core/mpsc_queue.h +47 -0
- package/native-src/src/librats/core/notifier.h +74 -0
- package/native-src/src/librats/core/receive_buffer.cpp +219 -0
- package/native-src/src/librats/core/receive_buffer.h +171 -0
- package/native-src/src/librats/core/service_registry.h +58 -0
- package/native-src/src/{socket.cpp → librats/core/socket.cpp} +625 -118
- package/native-src/src/librats/core/socket.h +496 -0
- package/native-src/src/librats/core/timer_queue.h +105 -0
- package/native-src/src/librats/core/types.cpp +42 -0
- package/native-src/src/librats/core/types.h +93 -0
- package/native-src/src/librats/core/wakeup_pipe.h +83 -0
- package/native-src/src/{crypto → librats/crypto}/blake2_endian.h +21 -23
- package/native-src/src/{crypto → librats/crypto}/blake2b.c +34 -33
- package/native-src/src/{crypto → librats/crypto}/blake2b.h +7 -6
- package/native-src/src/{crypto → librats/crypto}/blake2s.c +55 -54
- package/native-src/src/{crypto → librats/crypto}/blake2s.h +13 -12
- package/native-src/src/{crypto → librats/crypto}/chacha.c +22 -21
- package/native-src/src/{crypto → librats/crypto}/chacha.h +14 -13
- package/native-src/src/{crypto → librats/crypto}/chachapoly.c +56 -56
- package/native-src/src/{crypto → librats/crypto}/chachapoly.h +24 -17
- package/native-src/src/{crc32.cpp → librats/crypto/crc32.cpp} +1 -1
- package/native-src/src/{crc32.h → librats/crypto/crc32.h} +3 -1
- package/native-src/src/{crypto → librats/crypto}/curve25519.c +6 -4
- package/native-src/src/{crypto → librats/crypto}/curve25519.h +6 -3
- package/native-src/src/librats/crypto/hkdf.c +266 -0
- package/native-src/src/{crypto → librats/crypto}/hkdf.h +19 -19
- package/native-src/src/{noise.cpp → librats/crypto/noise.cpp} +84 -73
- package/native-src/src/{noise.h → librats/crypto/noise.h} +18 -8
- package/native-src/src/{crypto → librats/crypto}/poly1305.c +47 -46
- package/native-src/src/librats/crypto/poly1305.h +37 -0
- package/native-src/src/{sha1.cpp → librats/crypto/sha1.cpp} +33 -1
- package/native-src/src/{sha1.h → librats/crypto/sha1.h} +14 -6
- package/native-src/src/{crypto → librats/crypto}/sha256.c +15 -14
- package/native-src/src/{crypto → librats/crypto}/sha256.h +8 -7
- package/native-src/src/{crypto → librats/crypto}/sha512.c +15 -14
- package/native-src/src/{crypto → librats/crypto}/sha512.h +8 -7
- package/native-src/src/librats/dht/announce.cpp +37 -0
- package/native-src/src/librats/dht/announce.h +41 -0
- package/native-src/src/librats/dht/bep42.cpp +109 -0
- package/native-src/src/librats/dht/bep42.h +48 -0
- package/native-src/src/librats/dht/dht.cpp +501 -0
- package/native-src/src/librats/dht/dht.h +119 -0
- package/native-src/src/librats/dht/dht_runner.cpp +103 -0
- package/native-src/src/librats/dht/dht_runner.h +71 -0
- package/native-src/src/librats/dht/dos_blocker.cpp +42 -0
- package/native-src/src/librats/dht/dos_blocker.h +47 -0
- package/native-src/src/librats/dht/find_peers.cpp +52 -0
- package/native-src/src/librats/dht/find_peers.h +73 -0
- package/native-src/src/librats/dht/id.h +167 -0
- package/native-src/src/{krpc.cpp → librats/dht/krpc.cpp} +32 -81
- package/native-src/src/{krpc.h → librats/dht/krpc.h} +19 -23
- package/native-src/src/librats/dht/log.h +38 -0
- package/native-src/src/librats/dht/node.cpp +473 -0
- package/native-src/src/librats/dht/node.h +164 -0
- package/native-src/src/librats/dht/node_entry.h +81 -0
- package/native-src/src/librats/dht/observer.h +72 -0
- package/native-src/src/librats/dht/persistence.cpp +90 -0
- package/native-src/src/librats/dht/persistence.h +32 -0
- package/native-src/src/librats/dht/routing_table.cpp +559 -0
- package/native-src/src/librats/dht/routing_table.h +185 -0
- package/native-src/src/librats/dht/rpc_manager.cpp +127 -0
- package/native-src/src/librats/dht/rpc_manager.h +77 -0
- package/native-src/src/librats/dht/storage.cpp +92 -0
- package/native-src/src/librats/dht/storage.h +74 -0
- package/native-src/src/librats/dht/transport.h +27 -0
- package/native-src/src/librats/dht/traversal.cpp +326 -0
- package/native-src/src/librats/dht/traversal.h +120 -0
- package/native-src/src/librats/dht/udp_transport.cpp +49 -0
- package/native-src/src/librats/dht/udp_transport.h +51 -0
- package/native-src/src/librats/mdns/log.h +22 -0
- package/native-src/src/{mdns.cpp → librats/mdns/mdns.cpp} +75 -40
- package/native-src/src/{mdns.h → librats/mdns/mdns.h} +9 -8
- package/native-src/src/{natpmp.cpp → librats/nat/natpmp.cpp} +12 -9
- package/native-src/src/{natpmp.h → librats/nat/natpmp.h} +3 -3
- package/native-src/src/{port_mapping.h → librats/nat/port_mapping.h} +3 -2
- package/native-src/src/{stun.cpp → librats/nat/stun.cpp} +4 -4
- package/native-src/src/{stun.h → librats/nat/stun.h} +1 -1
- package/native-src/src/{upnp.cpp → librats/nat/upnp.cpp} +6 -6
- package/native-src/src/{upnp.h → librats/nat/upnp.h} +2 -2
- package/native-src/src/librats/node/config.h +110 -0
- package/native-src/src/librats/node/dial_service.h +54 -0
- package/native-src/src/librats/node/dialer.cpp +264 -0
- package/native-src/src/librats/node/dialer.h +188 -0
- package/native-src/src/librats/node/host_events.h +26 -0
- package/native-src/src/librats/node/identify.cpp +130 -0
- package/native-src/src/librats/node/identify.h +71 -0
- package/native-src/src/librats/node/nat_status.cpp +103 -0
- package/native-src/src/librats/node/nat_status.h +118 -0
- package/native-src/src/librats/node/node.cpp +826 -0
- package/native-src/src/librats/node/node.h +333 -0
- package/native-src/src/librats/node/node_context.h +33 -0
- package/native-src/src/librats/node/peer_network.h +91 -0
- package/native-src/src/librats/peer/peer.h +49 -0
- package/native-src/src/librats/peer/peer_book.cpp +181 -0
- package/native-src/src/librats/peer/peer_book.h +88 -0
- package/native-src/src/librats/peer/peer_id.cpp +72 -0
- package/native-src/src/librats/peer/peer_id.h +62 -0
- package/native-src/src/librats/peer/peer_info.h +37 -0
- package/native-src/src/librats/peer/peer_table.cpp +137 -0
- package/native-src/src/librats/peer/peer_table.h +148 -0
- package/native-src/src/librats/security/handshaker.h +66 -0
- package/native-src/src/librats/security/identity.h +43 -0
- package/native-src/src/librats/security/noise_security.cpp +122 -0
- package/native-src/src/librats/security/noise_security.h +37 -0
- package/native-src/src/librats/security/plaintext_security.h +106 -0
- package/native-src/src/librats/security/session.h +37 -0
- package/native-src/src/{storage.cpp → librats/storage/storage.cpp} +369 -522
- package/native-src/src/{storage.h → librats/storage/storage.h} +135 -299
- package/native-src/src/librats/subsystems/bittorrent.cpp +211 -0
- package/native-src/src/librats/subsystems/bittorrent.h +136 -0
- package/native-src/src/librats/subsystems/dht_discovery.cpp +202 -0
- package/native-src/src/librats/subsystems/dht_discovery.h +123 -0
- package/native-src/src/librats/subsystems/dht_service.h +36 -0
- package/native-src/src/librats/subsystems/file_transfer.cpp +972 -0
- package/native-src/src/librats/subsystems/file_transfer.h +367 -0
- package/native-src/src/librats/subsystems/hole_punch.cpp +532 -0
- package/native-src/src/librats/subsystems/hole_punch.h +266 -0
- package/native-src/src/librats/subsystems/hole_punch_service.h +38 -0
- package/native-src/src/librats/subsystems/mdns_discovery.cpp +66 -0
- package/native-src/src/librats/subsystems/mdns_discovery.h +55 -0
- package/native-src/src/librats/subsystems/message_json.cpp +112 -0
- package/native-src/src/librats/subsystems/message_json.h +88 -0
- package/native-src/src/librats/subsystems/peer_exchange.cpp +241 -0
- package/native-src/src/librats/subsystems/peer_exchange.h +136 -0
- package/native-src/src/librats/subsystems/ping_service.cpp +98 -0
- package/native-src/src/librats/subsystems/ping_service.h +66 -0
- package/native-src/src/librats/subsystems/port_mapping_service.cpp +192 -0
- package/native-src/src/librats/subsystems/port_mapping_service.h +84 -0
- package/native-src/src/librats/subsystems/pubsub.cpp +567 -0
- package/native-src/src/librats/subsystems/pubsub.h +175 -0
- package/native-src/src/librats/subsystems/reconnection.cpp +239 -0
- package/native-src/src/librats/subsystems/reconnection.h +126 -0
- package/native-src/src/librats/transport/connection.cpp +343 -0
- package/native-src/src/librats/transport/connection.h +283 -0
- package/native-src/src/librats/transport/link.h +96 -0
- package/native-src/src/librats/transport/reactor.cpp +540 -0
- package/native-src/src/librats/transport/reactor.h +224 -0
- package/native-src/src/librats/transport/reactor_pool.h +81 -0
- package/native-src/src/librats/transport/tcp_link.cpp +49 -0
- package/native-src/src/librats/transport/tcp_link.h +43 -0
- package/native-src/src/librats/transport/udp_mux.cpp +617 -0
- package/native-src/src/librats/transport/udp_mux.h +363 -0
- package/native-src/src/librats/transport/udp_packet.cpp +121 -0
- package/native-src/src/librats/transport/udp_packet.h +190 -0
- package/native-src/src/librats/transport/udp_stream.cpp +1194 -0
- package/native-src/src/librats/transport/udp_stream.h +614 -0
- package/native-src/src/librats/util/features.h.in +51 -0
- package/native-src/src/{fs.cpp → librats/util/fs.cpp} +51 -3
- package/native-src/src/librats/util/fs.h +136 -0
- package/native-src/src/librats/util/json.cpp +1002 -0
- package/native-src/src/librats/util/json.h +444 -0
- package/native-src/src/{logger.cpp → librats/util/logger.cpp} +1 -1
- package/native-src/src/{logger.h → librats/util/logger.h} +43 -31
- package/native-src/src/{network_monitor.cpp → librats/util/network_monitor.cpp} +12 -4
- package/native-src/src/{network_monitor.h → librats/util/network_monitor.h} +2 -1
- package/native-src/src/{network_utils.cpp → librats/util/network_utils.cpp} +38 -23
- package/native-src/src/{network_utils.h → librats/util/network_utils.h} +15 -8
- package/native-src/src/{os.cpp → librats/util/os.cpp} +48 -18
- package/native-src/src/librats/util/rats_export.h +69 -0
- package/native-src/src/{version.cpp → librats/util/version.cpp} +2 -2
- package/native-src/src/{version.h.in → librats/util/version.h.in} +1 -1
- package/native-src/src/librats/wire/frame.cpp +76 -0
- package/native-src/src/librats/wire/frame.h +110 -0
- package/native-src/src/librats/wire/message_router.cpp +45 -0
- package/native-src/src/librats/wire/message_router.h +47 -0
- package/package.json +5 -4
- package/scripts/build-librats.js +1 -0
- package/scripts/postinstall.js +3 -3
- package/scripts/prepare-package.js +4 -4
- package/scripts/verify-installation.js +63 -105
- package/src/librats_node.cpp +1042 -1324
- package/native-src/src/bencode.cpp +0 -485
- package/native-src/src/bencode.h +0 -145
- package/native-src/src/bittorrent.cpp +0 -14
- package/native-src/src/bittorrent.h +0 -74
- package/native-src/src/bt_bitfield.cpp +0 -372
- package/native-src/src/bt_bitfield.h +0 -316
- package/native-src/src/bt_choker.cpp +0 -228
- package/native-src/src/bt_choker.h +0 -147
- package/native-src/src/bt_client.cpp +0 -1047
- package/native-src/src/bt_client.h +0 -445
- package/native-src/src/bt_create_torrent.cpp +0 -677
- package/native-src/src/bt_create_torrent.h +0 -473
- package/native-src/src/bt_extension.cpp +0 -469
- package/native-src/src/bt_extension.h +0 -309
- package/native-src/src/bt_file_storage.cpp +0 -261
- package/native-src/src/bt_file_storage.h +0 -298
- package/native-src/src/bt_handshake.cpp +0 -134
- package/native-src/src/bt_handshake.h +0 -157
- package/native-src/src/bt_messages.cpp +0 -364
- package/native-src/src/bt_messages.h +0 -324
- package/native-src/src/bt_network.cpp +0 -1007
- package/native-src/src/bt_network.h +0 -417
- package/native-src/src/bt_peer_connection.cpp +0 -742
- package/native-src/src/bt_peer_connection.h +0 -592
- package/native-src/src/bt_piece_picker.cpp +0 -786
- package/native-src/src/bt_piece_picker.h +0 -473
- package/native-src/src/bt_resume_data.cpp +0 -410
- package/native-src/src/bt_resume_data.h +0 -249
- package/native-src/src/bt_torrent.cpp +0 -2120
- package/native-src/src/bt_torrent.h +0 -641
- package/native-src/src/bt_torrent_info.cpp +0 -659
- package/native-src/src/bt_torrent_info.h +0 -418
- package/native-src/src/bt_types.h +0 -621
- package/native-src/src/chained_send_buffer.cpp +0 -75
- package/native-src/src/chained_send_buffer.h +0 -137
- package/native-src/src/crypto/hkdf.c +0 -266
- package/native-src/src/crypto/poly1305.h +0 -36
- package/native-src/src/dht.cpp +0 -3311
- package/native-src/src/dht.h +0 -717
- package/native-src/src/disk_io.cpp +0 -632
- package/native-src/src/disk_io.h +0 -315
- package/native-src/src/file_transfer.cpp +0 -1415
- package/native-src/src/file_transfer.h +0 -286
- package/native-src/src/fs.h +0 -108
- package/native-src/src/gossipsub.cpp +0 -1139
- package/native-src/src/gossipsub.h +0 -403
- package/native-src/src/ice.cpp +0 -893
- package/native-src/src/ice.h +0 -559
- package/native-src/src/json.hpp +0 -25526
- package/native-src/src/librats.cpp +0 -2378
- package/native-src/src/librats.h +0 -2324
- package/native-src/src/librats_bittorrent.cpp +0 -601
- package/native-src/src/librats_c.cpp +0 -1557
- package/native-src/src/librats_c.h +0 -323
- package/native-src/src/librats_discovery.cpp +0 -402
- package/native-src/src/librats_encryption.cpp +0 -275
- package/native-src/src/librats_file_transfer.cpp +0 -144
- package/native-src/src/librats_gossipsub.cpp +0 -289
- package/native-src/src/librats_ice.cpp +0 -213
- package/native-src/src/librats_log_macros.h +0 -36
- package/native-src/src/librats_logging.cpp +0 -173
- package/native-src/src/librats_mdns.cpp +0 -166
- package/native-src/src/librats_persistence.cpp +0 -796
- package/native-src/src/librats_portmap.cpp +0 -419
- package/native-src/src/librats_reconnection.cpp +0 -218
- package/native-src/src/librats_statistic.cpp +0 -105
- package/native-src/src/librats_storage.cpp +0 -189
- package/native-src/src/rats_export.h +0 -17
- package/native-src/src/receive_buffer.cpp +0 -82
- package/native-src/src/receive_buffer.h +0 -127
- package/native-src/src/socket.h +0 -228
- package/native-src/src/threadmanager.cpp +0 -105
- package/native-src/src/threadmanager.h +0 -53
- package/native-src/src/tracker.cpp +0 -1264
- package/native-src/src/tracker.h +0 -319
- package/native-src/src/turn.cpp +0 -762
- package/native-src/src/turn.h +0 -460
- package/native-src/src/wakeup_pipe.h +0 -60
- /package/native-src/src/{os.h → librats/util/os.h} +0 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
#include "librats/transport/connection.h"
|
|
2
|
+
#include "librats/transport/reactor.h"
|
|
3
|
+
#include "librats/core/io_poller.h"
|
|
4
|
+
#include "librats/util/logger.h"
|
|
5
|
+
|
|
6
|
+
#include <algorithm>
|
|
7
|
+
#include <cerrno>
|
|
8
|
+
#include <cstring>
|
|
9
|
+
|
|
10
|
+
namespace librats {
|
|
11
|
+
|
|
12
|
+
namespace {
|
|
13
|
+
|
|
14
|
+
constexpr size_t kRecvChunk = 16 * 1024; ///< bytes offered to each recv()
|
|
15
|
+
|
|
16
|
+
/// Ceiling on how far rx_ may be grown on the strength of a *declared* block length
|
|
17
|
+
/// alone. Sizing the buffer for the whole block up front (as libtorrent does, growing
|
|
18
|
+
/// straight to packet_size) turns a large frame into one allocation instead of a
|
|
19
|
+
/// 1.5x-at-a-time climb that memmoves everything received so far at every step. But
|
|
20
|
+
/// the length is the peer's word: a 4-byte prefix claiming kMaxBlockSize must not
|
|
21
|
+
/// make us allocate 64 MiB for a peer that then sends nothing. Past this, growth
|
|
22
|
+
/// falls back to geometric, so the allocation only ever tracks bytes that really
|
|
23
|
+
/// arrived — a big block still costs a handful of reallocations, not tens.
|
|
24
|
+
constexpr size_t kMaxEagerReserve = 1024 * 1024;
|
|
25
|
+
|
|
26
|
+
/// Frames up to this size are copied into the send queue rather than moved into a
|
|
27
|
+
/// chunk of their own (see queue_block). Matches the queue's scratch chunk, so a
|
|
28
|
+
/// small frame and its length prefix land in the same allocation.
|
|
29
|
+
constexpr size_t kInlineBlockLimit = ChainedSendBuffer::kScratchCapacity;
|
|
30
|
+
|
|
31
|
+
} // namespace
|
|
32
|
+
|
|
33
|
+
Connection::Connection(ConnId id, std::unique_ptr<Link> link, ConnRole role,
|
|
34
|
+
Reactor& reactor, ConnectionDelegate& delegate)
|
|
35
|
+
: id_(id), link_(std::move(link)), role_(role), reactor_(reactor), delegate_(delegate) {}
|
|
36
|
+
|
|
37
|
+
Connection::~Connection() = default;
|
|
38
|
+
|
|
39
|
+
IpAddress Connection::remote_ip() const {
|
|
40
|
+
// The peer's source IP as the transport sees it, with no textual round-trip.
|
|
41
|
+
// (The source port is not dialable — ephemeral on TCP, NAT-rewritten on UDP —
|
|
42
|
+
// so it is discarded; identify supplies the real listen port.)
|
|
43
|
+
const auto ep = link_->remote_endpoint();
|
|
44
|
+
return ep ? ep->ip : IpAddress{};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
uint8_t Connection::reactor_index() const noexcept { return reactor_.index(); }
|
|
48
|
+
|
|
49
|
+
// ── Outbound application frames ─────────────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
bool Connection::send(FrameHeader header, ByteView payload) {
|
|
52
|
+
if (state_ != ConnState::Established) return false; // frames only flow post-handshake
|
|
53
|
+
|
|
54
|
+
Bytes inner;
|
|
55
|
+
framer::encode_message(inner, header, payload);
|
|
56
|
+
|
|
57
|
+
Bytes cipher;
|
|
58
|
+
if (!session_->encrypt(inner, cipher)) {
|
|
59
|
+
LOG_ERROR("connection", "Peer " << remote_id_.short_hex() << " encrypt failed");
|
|
60
|
+
reactor_.close(id_, CloseReason::ProtocolError);
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
queue_block(std::move(cipher));
|
|
65
|
+
|
|
66
|
+
const size_t backlog_now = backlog();
|
|
67
|
+
if (backlog_now > send_high_water_) {
|
|
68
|
+
LOG_WARN("connection", "Peer " << remote_id_.short_hex() << " over send high-water ("
|
|
69
|
+
<< backlog_now << " B); closing as slow consumer");
|
|
70
|
+
close_reason_ = CloseReason::SlowConsumer;
|
|
71
|
+
state_ = ConnState::Closing;
|
|
72
|
+
reactor_.close(id_, CloseReason::SlowConsumer);
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// The queue has grown past what a caller should keep adding to. Everything
|
|
77
|
+
// still goes out — nothing is dropped here — but the answer to "may I send
|
|
78
|
+
// more?" becomes no, and stays no until the queue drains back under the mark
|
|
79
|
+
// and on_writable says so. Without this an application has no way at all to
|
|
80
|
+
// tell that it is outrunning the link, and the only thing that eventually
|
|
81
|
+
// tells it is the disconnection above.
|
|
82
|
+
if (!over_low_water_ && backlog_now > send_low_water_) {
|
|
83
|
+
over_low_water_ = true;
|
|
84
|
+
delegate_.on_writable_changed(*this, false);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Aggregate rather than write through. Several frames produced in one batch
|
|
88
|
+
// of reactor tasks belong in ONE write, and flushing on each of them costs a
|
|
89
|
+
// system call per frame — measured at roughly ten times the throughput on
|
|
90
|
+
// small messages, on both transports. The flush happens at the end of the
|
|
91
|
+
// reactor turn (Reactor::flush_dirty), which is still the same turn: nothing
|
|
92
|
+
// waits on a timer and no latency is added.
|
|
93
|
+
//
|
|
94
|
+
// Past a point, though, waiting buys nothing: a backlog this size already
|
|
95
|
+
// fills a write, and letting it grow further would only hold memory and
|
|
96
|
+
// delay the bytes. So a large queue goes out at once, exactly as before.
|
|
97
|
+
if (tx_.size() >= kCoalesceLimit) {
|
|
98
|
+
if (!flush()) reactor_.close(id_, close_reason_);
|
|
99
|
+
} else if (!flush_queued_) {
|
|
100
|
+
flush_queued_ = true;
|
|
101
|
+
reactor_.mark_dirty(id_);
|
|
102
|
+
}
|
|
103
|
+
return !over_low_water_;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
bool Connection::flush_pending() {
|
|
107
|
+
flush_queued_ = false;
|
|
108
|
+
return flush();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
void Connection::queue_block(Bytes body) {
|
|
112
|
+
// The length prefix is queued as its own slice instead of being spliced in front
|
|
113
|
+
// of `body` — gather I/O reunites them in one writev, so framing costs no copy.
|
|
114
|
+
uint8_t prefix[framer::kLengthPrefixSize];
|
|
115
|
+
framer::encode_block_header(prefix, body.size());
|
|
116
|
+
tx_.append(ByteView(prefix, sizeof(prefix)));
|
|
117
|
+
|
|
118
|
+
// Small frame: copy it in, so prefix and body share one packed chunk. A memcpy of
|
|
119
|
+
// a few hundred bytes is cheaper than the allocation it saves — and it keeps a
|
|
120
|
+
// backlog of small frames from costing a chunk each.
|
|
121
|
+
// Large frame: move it, so a megabyte-sized payload is never copied to be framed.
|
|
122
|
+
if (body.size() <= kInlineBlockLimit) tx_.append(ByteView(body));
|
|
123
|
+
else tx_.append(std::move(body));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ── Handshake lifecycle ─────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
void Connection::start_handshake() {
|
|
129
|
+
if (state_ == ConnState::Connecting) begin_handshake();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
void Connection::begin_handshake() {
|
|
133
|
+
state_ = ConnState::Handshaking;
|
|
134
|
+
// The transport is up, so the write-interest raised for the connect itself is
|
|
135
|
+
// no longer wanted; flush() re-arms it if the handshake outgrows the link.
|
|
136
|
+
want_write_ = false;
|
|
137
|
+
link_->want_write(false);
|
|
138
|
+
|
|
139
|
+
handshaker_ = reactor_.security().create(role_);
|
|
140
|
+
Bytes out;
|
|
141
|
+
if (!handshaker_->start(out)) {
|
|
142
|
+
reactor_.close(id_, CloseReason::HandshakeFailed);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (!out.empty()) {
|
|
146
|
+
queue_block(std::move(out)); // initiator's first message
|
|
147
|
+
if (!flush()) { reactor_.close(id_, close_reason_); return; }
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
bool Connection::drive_handshake(ByteView body) {
|
|
152
|
+
Bytes out;
|
|
153
|
+
auto outcome = handshaker_->consume(body, out);
|
|
154
|
+
if (outcome.status == Handshaker::Outcome::Failed) {
|
|
155
|
+
return fail(CloseReason::HandshakeFailed);
|
|
156
|
+
}
|
|
157
|
+
if (!out.empty()) {
|
|
158
|
+
queue_block(std::move(out)); // reply (responder's message, or initiator's final)
|
|
159
|
+
if (!flush()) return false;
|
|
160
|
+
}
|
|
161
|
+
if (outcome.status == Handshaker::Outcome::Done) {
|
|
162
|
+
session_ = std::move(outcome.session);
|
|
163
|
+
remote_id_ = outcome.remote_id;
|
|
164
|
+
handshaker_.reset();
|
|
165
|
+
complete_established();
|
|
166
|
+
}
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
void Connection::cancel_establish_timer() {
|
|
171
|
+
if (establish_timer_ != kInvalidTimerId) {
|
|
172
|
+
reactor_.cancel(establish_timer_);
|
|
173
|
+
establish_timer_ = kInvalidTimerId;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
void Connection::complete_established() {
|
|
178
|
+
state_ = ConnState::Established;
|
|
179
|
+
cancel_establish_timer();
|
|
180
|
+
LOG_DEBUG("connection", "Peer " << remote_id_.short_hex() << " established ("
|
|
181
|
+
<< (role_ == ConnRole::Inbound ? "inbound" : "outbound")
|
|
182
|
+
<< (is_secure() ? ", encrypted)" : ", plaintext)"));
|
|
183
|
+
delegate_.on_established(*this);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ── Inbound ─────────────────────────────────────────────────────────────────
|
|
187
|
+
|
|
188
|
+
bool Connection::on_readable() {
|
|
189
|
+
// Drain the kernel buffer (the socket is non-blocking), parsing as we go. The
|
|
190
|
+
// parse step is inside the loop on purpose: it keeps rx_ down to one in-flight
|
|
191
|
+
// block no matter how fast the peer sends, where draining first and parsing
|
|
192
|
+
// afterwards would let rx_ grow to everything the peer managed to deliver.
|
|
193
|
+
//
|
|
194
|
+
// We must drain to would-block (or to a short read, which means the kernel
|
|
195
|
+
// buffer is now empty) — the kqueue backend is edge-triggered, so stopping with
|
|
196
|
+
// data still queued would leave the connection stalled until the *next* byte.
|
|
197
|
+
while (true) {
|
|
198
|
+
const ByteSpan into = rx_.prepare(read_size());
|
|
199
|
+
|
|
200
|
+
const Link::IoResult r = link_->read(into);
|
|
201
|
+
if (r.status == Link::Status::Closed) {
|
|
202
|
+
if (!process_blocks()) return false; // deliver what the peer sent before FIN
|
|
203
|
+
return fail(CloseReason::PeerClosed);
|
|
204
|
+
}
|
|
205
|
+
if (r.status == Link::Status::Error) return fail(link_->error_reason());
|
|
206
|
+
if (r.status == Link::Status::WouldBlock) break;
|
|
207
|
+
|
|
208
|
+
rx_.commit(r.bytes);
|
|
209
|
+
if (!process_blocks()) return false;
|
|
210
|
+
|
|
211
|
+
if (r.bytes < into.size()) break; // link drained
|
|
212
|
+
}
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
size_t Connection::read_size() const {
|
|
217
|
+
// Not established yet: the peer is still anonymous, so its declared length buys it
|
|
218
|
+
// nothing. A handshake block is a Noise message plus the protocol id — hundreds of
|
|
219
|
+
// bytes — so there is no large allocation to save here, while honouring the length
|
|
220
|
+
// would let four bytes from an unauthenticated socket reserve kMaxEagerReserve. rx_
|
|
221
|
+
// still grows geometrically to hold whatever actually arrives, so an unusually large
|
|
222
|
+
// handshake block is served just as correctly, only without the eager reserve.
|
|
223
|
+
if (state_ != ConnState::Established) return kRecvChunk;
|
|
224
|
+
|
|
225
|
+
// Ask for the rest of the block we are mid-way through, so a large frame lands in
|
|
226
|
+
// one allocation rather than a series of 1.5x growth steps. Bounded by
|
|
227
|
+
// kMaxEagerReserve, since rx_need_ is a length the *peer* declared.
|
|
228
|
+
//
|
|
229
|
+
// Note this asks for the remainder even when it is *smaller* than kRecvChunk. It
|
|
230
|
+
// is tempting to floor it at kRecvChunk ("read as much as we can anyway"), but
|
|
231
|
+
// prepare(n) is a demand for n bytes of free tail, and a buffer sized exactly for
|
|
232
|
+
// the block has no kRecvChunk of tail left near the end of it — so the floor would
|
|
233
|
+
// force one last 1.5x grow (memcpy'ing the whole block that already arrived) for
|
|
234
|
+
// room that the block does not need. Nothing is lost by asking for less: prepare()
|
|
235
|
+
// hands back the *entire* free tail regardless, so the recv() is just as big.
|
|
236
|
+
if (rx_need_ > rx_.size()) return (std::min)(rx_need_ - rx_.size(), kMaxEagerReserve);
|
|
237
|
+
return kRecvChunk;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
bool Connection::process_blocks() {
|
|
241
|
+
rx_need_ = 0; // recomputed below: 0 == no block is mid-flight
|
|
242
|
+
|
|
243
|
+
// Body views point into rx_, so each block must be consumed before the next is
|
|
244
|
+
// decoded (and before any recv() reallocates the buffer under us).
|
|
245
|
+
while (!rx_.empty()) {
|
|
246
|
+
const auto block = framer::try_take_block(rx_.data(), rx_.size());
|
|
247
|
+
if (block.status == framer::Block::Incomplete) {
|
|
248
|
+
rx_need_ = block.needed; // 0 while even the length prefix is short
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
if (block.status == framer::Block::Error) return fail(CloseReason::ProtocolError);
|
|
252
|
+
|
|
253
|
+
const bool keep = (state_ == ConnState::Handshaking) ? drive_handshake(block.body)
|
|
254
|
+
: deliver_frame(block.body);
|
|
255
|
+
rx_.consume(block.consumed);
|
|
256
|
+
if (!keep) return false;
|
|
257
|
+
if (state_ == ConnState::Closing || state_ == ConnState::Closed) return false;
|
|
258
|
+
}
|
|
259
|
+
return true;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
bool Connection::deliver_frame(ByteView body) {
|
|
263
|
+
if (!session_) return fail(CloseReason::ProtocolError); // data before handshake
|
|
264
|
+
|
|
265
|
+
Bytes plain;
|
|
266
|
+
if (!session_->decrypt(body, plain)) return fail(CloseReason::ProtocolError);
|
|
267
|
+
|
|
268
|
+
auto msg = framer::parse_message(plain);
|
|
269
|
+
if (!msg.ok) return fail(CloseReason::ProtocolError);
|
|
270
|
+
|
|
271
|
+
delegate_.on_frame(*this, msg.frame);
|
|
272
|
+
return true;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
bool Connection::on_writable() {
|
|
276
|
+
// Finish the transport-level connect (a non-blocking TCP connect, or a Syn
|
|
277
|
+
// that has just been acknowledged), then begin the handshake.
|
|
278
|
+
if (state_ == ConnState::Connecting) {
|
|
279
|
+
if (!link_->connect_completed()) {
|
|
280
|
+
LOG_DEBUG("connection", "Peer " << id_ << " connect failed");
|
|
281
|
+
return fail(CloseReason::ConnectFailed);
|
|
282
|
+
}
|
|
283
|
+
begin_handshake();
|
|
284
|
+
if (state_ == ConnState::Closing) return false;
|
|
285
|
+
}
|
|
286
|
+
return flush();
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
bool Connection::on_error() {
|
|
290
|
+
return fail(link_->error_reason());
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// ── Send buffer flush + write interest ──────────────────────────────────────
|
|
294
|
+
|
|
295
|
+
bool Connection::flush() {
|
|
296
|
+
while (!tx_.empty()) {
|
|
297
|
+
// Hand the whole backlog to the kernel at once: a burst of queued frames
|
|
298
|
+
// (and the length prefix that precedes each one) leaves in a single syscall
|
|
299
|
+
// instead of one per chunk.
|
|
300
|
+
ByteView slices[kMaxSendSlices];
|
|
301
|
+
const size_t count = tx_.gather(slices, kMaxSendSlices);
|
|
302
|
+
|
|
303
|
+
const Link::IoResult r = link_->write(slices, count);
|
|
304
|
+
if (r.status == Link::Status::Ok) { tx_.pop_front(r.bytes); continue; }
|
|
305
|
+
if (r.status == Link::Status::WouldBlock) break; // retry on the next writable event
|
|
306
|
+
return fail(link_->error_reason());
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (tx_.empty()) disarm_write();
|
|
310
|
+
else arm_write();
|
|
311
|
+
|
|
312
|
+
// Drained back under the mark: whoever was told to stop can start again. The
|
|
313
|
+
// re-entrancy guard is what keeps a handler that answers by sending from
|
|
314
|
+
// recursing through flush() — it may queue, and the next drain reports the
|
|
315
|
+
// next opening, but it cannot nest.
|
|
316
|
+
if (over_low_water_ && !in_writable_ && backlog() <= send_low_water_) {
|
|
317
|
+
over_low_water_ = false;
|
|
318
|
+
in_writable_ = true;
|
|
319
|
+
delegate_.on_writable_changed(*this, true);
|
|
320
|
+
in_writable_ = false;
|
|
321
|
+
}
|
|
322
|
+
return true;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
void Connection::arm_write() {
|
|
326
|
+
if (want_write_) return;
|
|
327
|
+
want_write_ = true;
|
|
328
|
+
link_->want_write(true);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
void Connection::disarm_write() {
|
|
332
|
+
if (!want_write_) return;
|
|
333
|
+
want_write_ = false;
|
|
334
|
+
link_->want_write(false);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
bool Connection::fail(CloseReason reason) {
|
|
338
|
+
close_reason_ = reason;
|
|
339
|
+
state_ = ConnState::Closing;
|
|
340
|
+
return false; // tells the reactor to tear this connection down
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
} // namespace librats
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file connection.h
|
|
5
|
+
* @brief A single peer connection: byte-stream link, secure handshake, framing.
|
|
6
|
+
*
|
|
7
|
+
* A Connection is owned by exactly one Reactor and is only ever touched by that
|
|
8
|
+
* reactor's thread. Because of that single-threaded ownership it holds NO locks
|
|
9
|
+
* and uses NO atomics — all synchronisation lives at the reactor boundary (the
|
|
10
|
+
* task queue). This is the heart of the shared-nothing model.
|
|
11
|
+
*
|
|
12
|
+
* It is also transport-agnostic: everything here needs is an ordered, reliable,
|
|
13
|
+
* non-blocking byte stream, which is exactly what a Link is (see link.h). A
|
|
14
|
+
* connection over a kernel TCP socket and one over the library's reliable-UDP
|
|
15
|
+
* stream run the *same* code — same framing, same handshake, same backpressure —
|
|
16
|
+
* and differ only in which Link they hold.
|
|
17
|
+
*
|
|
18
|
+
* Lifecycle: Connecting → Handshaking → Established → Closing/Closed.
|
|
19
|
+
* - Connecting: outbound transport connect in flight (inbound skips this).
|
|
20
|
+
* - Handshaking: a Handshaker (from the reactor's SecurityProvider) runs to
|
|
21
|
+
* completion, yielding a Session and the remote PeerId.
|
|
22
|
+
* - Established: inbound blocks are decrypted into inner frames and delivered;
|
|
23
|
+
* outbound frames are encrypted and queued.
|
|
24
|
+
*
|
|
25
|
+
* The Connection reports events through a ConnectionDelegate and asks its Link
|
|
26
|
+
* to (dis)arm write-interest; it never touches the poller directly.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
#include "librats/util/rats_export.h"
|
|
30
|
+
#include "librats/core/types.h"
|
|
31
|
+
#include "librats/core/bytes.h"
|
|
32
|
+
#include "librats/wire/frame.h"
|
|
33
|
+
#include "librats/peer/peer_id.h"
|
|
34
|
+
#include "librats/security/handshaker.h"
|
|
35
|
+
#include "librats/core/receive_buffer.h"
|
|
36
|
+
#include "librats/core/chained_send_buffer.h"
|
|
37
|
+
#include "librats/core/socket.h"
|
|
38
|
+
#include "librats/transport/link.h"
|
|
39
|
+
|
|
40
|
+
#include <atomic>
|
|
41
|
+
#include <memory>
|
|
42
|
+
|
|
43
|
+
namespace librats {
|
|
44
|
+
|
|
45
|
+
class Connection;
|
|
46
|
+
class Reactor;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @brief Sink for connection lifecycle and inbound frames.
|
|
50
|
+
*
|
|
51
|
+
* All callbacks run on the owning reactor's thread. `on_established` fires once
|
|
52
|
+
* the secure handshake completes (so conn.remote_id() is valid). `on_closed` is
|
|
53
|
+
* the last call and is invoked by the Reactor during teardown, so a delegate may
|
|
54
|
+
* safely close other connections from within it.
|
|
55
|
+
*/
|
|
56
|
+
class RATS_API ConnectionDelegate {
|
|
57
|
+
public:
|
|
58
|
+
virtual ~ConnectionDelegate() = default;
|
|
59
|
+
|
|
60
|
+
/// Admission gate for a freshly accepted inbound socket, asked by the Reactor
|
|
61
|
+
/// before it adopts the connection (i.e. before any handshake cost is paid).
|
|
62
|
+
/// Returning false makes the Reactor close the socket immediately. Runs on the
|
|
63
|
+
/// acceptor reactor thread. Default: always admit.
|
|
64
|
+
virtual bool admit_inbound() { return true; }
|
|
65
|
+
|
|
66
|
+
virtual void on_established(Connection& conn) = 0;
|
|
67
|
+
virtual void on_frame(Connection& conn, const Frame& frame) = 0;
|
|
68
|
+
virtual void on_closed(Connection& conn, CloseReason reason) = 0;
|
|
69
|
+
|
|
70
|
+
/// The send queue crossed its low-water mark: `writable` is false when it
|
|
71
|
+
/// filled past the mark (send() will now answer "no room") and true when it
|
|
72
|
+
/// has drained back under, so a sender that was told to stop may start again.
|
|
73
|
+
/// Runs on the reactor thread — from inside send() for the first, from inside
|
|
74
|
+
/// the flush that emptied the queue for the second — so a handler must not
|
|
75
|
+
/// block. Default: ignore.
|
|
76
|
+
virtual void on_writable_changed(Connection& /*conn*/, bool /*writable*/) {}
|
|
77
|
+
|
|
78
|
+
/// An outbound dial died before there was ever a Connection to report it on —
|
|
79
|
+
/// an unresolvable host, no route, no socket. Without this a dial policy that
|
|
80
|
+
/// reserved the ConnId (see Reactor::connect) would wait out a timeout for an
|
|
81
|
+
/// attempt that never began. Runs on the reactor thread. Default: ignore.
|
|
82
|
+
virtual void on_dial_aborted(uint8_t /*reactor_index*/, ConnId /*id*/,
|
|
83
|
+
const std::string& /*host*/, uint16_t /*port*/) {}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
class Connection {
|
|
87
|
+
public:
|
|
88
|
+
/// High-water mark for the memory held by the send queue; exceeding it closes the
|
|
89
|
+
/// connection with CloseReason::SlowConsumer.
|
|
90
|
+
static constexpr size_t kDefaultSendHighWater = 8 * 1024 * 1024;
|
|
91
|
+
|
|
92
|
+
/// Where send() starts answering "no room". A quarter of the hard limit, so a
|
|
93
|
+
/// caller that heeds the answer never comes near the point where the
|
|
94
|
+
/// connection is dropped, and one that ignores it is no worse off than before
|
|
95
|
+
/// this mark existed. This is the whole difference between a transport that
|
|
96
|
+
/// can be used correctly and one that merely disconnects you.
|
|
97
|
+
static constexpr size_t kDefaultSendLowWater = kDefaultSendHighWater / 4;
|
|
98
|
+
|
|
99
|
+
/// Queued bytes past which send() stops holding the batch back and writes
|
|
100
|
+
/// immediately. Aggregating below this is what turns a burst of small frames
|
|
101
|
+
/// into one system call; above it there is already enough to fill a write, so
|
|
102
|
+
/// waiting would only cost memory and delay.
|
|
103
|
+
static constexpr size_t kCoalesceLimit = 64 * 1024;
|
|
104
|
+
|
|
105
|
+
Connection(ConnId id, std::unique_ptr<Link> link, ConnRole role,
|
|
106
|
+
Reactor& reactor, ConnectionDelegate& delegate);
|
|
107
|
+
~Connection();
|
|
108
|
+
|
|
109
|
+
Connection(const Connection&) = delete;
|
|
110
|
+
Connection& operator=(const Connection&) = delete;
|
|
111
|
+
|
|
112
|
+
// — identity / state (reactor thread) —
|
|
113
|
+
ConnId id() const noexcept { return id_; }
|
|
114
|
+
uint8_t reactor_index() const noexcept; ///< index of the owning reactor
|
|
115
|
+
Link& link() const noexcept { return *link_; }
|
|
116
|
+
/// The kernel socket behind this connection, or RATS_INVALID_SOCKET when the
|
|
117
|
+
/// transport has none of its own (UDP streams share the mux's socket).
|
|
118
|
+
socket_t socket() const noexcept { return link_->fd(); }
|
|
119
|
+
TransportKind transport() const noexcept { return link_->kind(); }
|
|
120
|
+
ConnRole role() const noexcept { return role_; }
|
|
121
|
+
ConnState state() const noexcept { return state_; }
|
|
122
|
+
CloseReason close_reason() const noexcept { return close_reason_; }
|
|
123
|
+
const PeerId& remote_id() const noexcept { return remote_id_; }
|
|
124
|
+
bool is_secure() const noexcept { return session_ && session_->is_secure(); }
|
|
125
|
+
|
|
126
|
+
/// Queue an application frame for the peer. No-op unless Established.
|
|
127
|
+
///
|
|
128
|
+
/// @return whether there is still room for more. False means the queue is
|
|
129
|
+
/// past its low-water mark — this frame is queued like any other,
|
|
130
|
+
/// nothing is dropped, but the caller should stop and wait for
|
|
131
|
+
/// ConnectionDelegate::on_writable rather than keep piling on until
|
|
132
|
+
/// the connection is dropped as a slow consumer.
|
|
133
|
+
bool send(FrameHeader header, ByteView payload);
|
|
134
|
+
|
|
135
|
+
/// Whether the send queue currently has room (see send()).
|
|
136
|
+
bool writable() const noexcept { return !over_low_water_; }
|
|
137
|
+
|
|
138
|
+
/// Adopt the peer's in-transit counter: bytes a caller has handed to this
|
|
139
|
+
/// reactor that have not reached send() yet (see PeerTable::Entry::owed).
|
|
140
|
+
///
|
|
141
|
+
/// They are as much of the backlog as the queue itself — held in the reactor's
|
|
142
|
+
/// task queue rather than in tx_, and on their way here — so the marks below
|
|
143
|
+
/// weigh them together with what is queued. Without that the two halves would
|
|
144
|
+
/// answer separately: a caller told "no room" because its charge crossed the
|
|
145
|
+
/// mark would be waiting for an opening from a queue that never crossed
|
|
146
|
+
/// anything, and so would never be told the room came back.
|
|
147
|
+
void set_in_transit(std::shared_ptr<const std::atomic<size_t>> owed) noexcept {
|
|
148
|
+
in_transit_ = std::move(owed);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/// What this connection is carrying: queued here plus still on its way.
|
|
152
|
+
size_t backlog() const noexcept {
|
|
153
|
+
// Watch the memory the queue actually holds, not just the bytes still owed
|
|
154
|
+
// to the socket: a partially sent chunk keeps its whole allocation, so
|
|
155
|
+
// allocated() is what a peer that stops reading can make us carry.
|
|
156
|
+
return tx_.allocated() +
|
|
157
|
+
(in_transit_ ? in_transit_->load(std::memory_order_relaxed) : 0);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/// Resize the send queue's marks. `high` is where the connection is dropped
|
|
161
|
+
/// as a slow consumer; the low-water mark send() reports against is derived
|
|
162
|
+
/// from it, so the two can never be set inconsistently. Called once, right
|
|
163
|
+
/// after the handshake, when the node's config asks for something other than
|
|
164
|
+
/// the default.
|
|
165
|
+
void set_send_high_water(size_t high) noexcept {
|
|
166
|
+
send_high_water_ = high;
|
|
167
|
+
send_low_water_ = high / 4;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/// Write out what send() queued during this reactor turn. Called by the
|
|
171
|
+
/// Reactor at the end of the turn; false ⇒ the connection must be torn down.
|
|
172
|
+
bool flush_pending();
|
|
173
|
+
|
|
174
|
+
/// Convenience: send raw bytes on an application channel.
|
|
175
|
+
bool send(uint16_t channel, ByteView payload) {
|
|
176
|
+
return send(FrameHeader{MessageType::App, 0, channel}, payload);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// — reactor-driven I/O callbacks. Return false to request teardown. —
|
|
180
|
+
bool on_readable();
|
|
181
|
+
bool on_writable();
|
|
182
|
+
bool on_error();
|
|
183
|
+
|
|
184
|
+
/// Bring an accepted (inbound) socket up: it is already connected, so begin
|
|
185
|
+
/// the handshake immediately. Called by the Reactor right after adopt().
|
|
186
|
+
void start_handshake();
|
|
187
|
+
|
|
188
|
+
/// Associate the establishment-timeout timer so it can be cancelled once the
|
|
189
|
+
/// connection reaches Established. Set by the Reactor after adopt().
|
|
190
|
+
void set_establish_timer(TimerId id) noexcept { establish_timer_ = id; }
|
|
191
|
+
|
|
192
|
+
/// Cancel the establishment-timeout timer if still armed; idempotent. Called
|
|
193
|
+
/// both on success (complete_established) and on teardown (Reactor::remove) so
|
|
194
|
+
/// the reaper can never outlive the connection and fire against a reused fd.
|
|
195
|
+
void cancel_establish_timer();
|
|
196
|
+
|
|
197
|
+
/// Periodic housekeeping, driven by the Reactor's maintenance timer. Hands back
|
|
198
|
+
/// the receive buffer of a peer that has gone quiet — a peer that sent one big
|
|
199
|
+
/// frame and then fell silent would otherwise sit on that allocation for the life
|
|
200
|
+
/// of the connection. Reactor thread; never closes the connection.
|
|
201
|
+
void on_maintenance_tick() { rx_.decay(); }
|
|
202
|
+
|
|
203
|
+
/// Record the address this outbound connection was dialed at (so the peer's
|
|
204
|
+
/// reconnect address is known). Set by the Reactor after adopt(); reactor thread.
|
|
205
|
+
void set_dial_address(std::string host, uint16_t port) {
|
|
206
|
+
dial_host_ = std::move(host);
|
|
207
|
+
dial_port_ = port;
|
|
208
|
+
}
|
|
209
|
+
bool has_dial_address() const noexcept { return dial_port_ != 0; }
|
|
210
|
+
const std::string& dial_host() const noexcept { return dial_host_; }
|
|
211
|
+
uint16_t dial_port() const noexcept { return dial_port_; }
|
|
212
|
+
|
|
213
|
+
/// The peer's IP as the link sees it, without the source port (ephemeral on
|
|
214
|
+
/// TCP, NAT-rewritten on UDP). Combined with the peer's advertised listen port
|
|
215
|
+
/// by the node's identify exchange to form a dialable address — the only way
|
|
216
|
+
/// to learn the dialable address of an *inbound* peer. Unspecified on error.
|
|
217
|
+
IpAddress remote_ip() const;
|
|
218
|
+
|
|
219
|
+
/// The peer's source endpoint as the link sees it, port included.
|
|
220
|
+
///
|
|
221
|
+
/// The port is meaningless on TCP (an ephemeral one the peer's OS picked for an
|
|
222
|
+
/// outbound socket) but is NOT on a datagram link: there every peer shares one
|
|
223
|
+
/// socket, so what a NAT rewrote the source port to is exactly the port a third
|
|
224
|
+
/// party would have to aim at to reach this peer. That is what identify carries
|
|
225
|
+
/// on to it, and what a hole punch is aimed at. nullopt if the link cannot say.
|
|
226
|
+
std::optional<Address> remote_endpoint() const { return link_->remote_endpoint(); }
|
|
227
|
+
|
|
228
|
+
/// Tell the link the connection is going away, so a transport that owes the
|
|
229
|
+
/// peer a goodbye (or a last flush) can arrange it. Called by the Reactor
|
|
230
|
+
/// during teardown, before the Connection is destroyed.
|
|
231
|
+
void shutdown_link(CloseReason reason) { link_->close(reason); }
|
|
232
|
+
|
|
233
|
+
private:
|
|
234
|
+
void begin_handshake(); ///< transport up → Handshaking
|
|
235
|
+
size_t read_size() const; ///< bytes to offer the next recv() (see rx_need_)
|
|
236
|
+
bool process_blocks(); ///< decode every complete block in rx_; false ⇒ teardown
|
|
237
|
+
bool drive_handshake(ByteView body);///< feed one handshake block; false ⇒ teardown
|
|
238
|
+
bool deliver_frame(ByteView body); ///< decrypt+parse+dispatch; false ⇒ teardown
|
|
239
|
+
void complete_established(); ///< handshake done → Established
|
|
240
|
+
void queue_block(Bytes body); ///< length-prefix `body` into the send buffer
|
|
241
|
+
bool flush(); ///< drain tx_ to the socket; false ⇒ teardown
|
|
242
|
+
void arm_write();
|
|
243
|
+
void disarm_write();
|
|
244
|
+
bool fail(CloseReason); ///< record reason, return false (teardown)
|
|
245
|
+
|
|
246
|
+
ConnId id_;
|
|
247
|
+
std::unique_ptr<Link> link_;
|
|
248
|
+
ConnRole role_;
|
|
249
|
+
ConnState state_ = ConnState::Connecting;
|
|
250
|
+
CloseReason close_reason_ = CloseReason::PeerClosed;
|
|
251
|
+
Reactor& reactor_;
|
|
252
|
+
ConnectionDelegate& delegate_;
|
|
253
|
+
|
|
254
|
+
std::unique_ptr<Handshaker> handshaker_; ///< non-null only while Handshaking
|
|
255
|
+
std::unique_ptr<Session> session_; ///< non-null once Established
|
|
256
|
+
PeerId remote_id_;
|
|
257
|
+
|
|
258
|
+
ReceiveBuffer rx_; ///< allocates on first read, shrinks back when idle
|
|
259
|
+
/// Wire size of the block rx_ is mid-way through, once its length prefix has been
|
|
260
|
+
/// read; 0 when no block is in flight. Lets the next recv() size rx_ for the whole
|
|
261
|
+
/// block in one go instead of growing into it. Peer-declared, hence capped.
|
|
262
|
+
size_t rx_need_ = 0;
|
|
263
|
+
ChainedSendBuffer tx_;
|
|
264
|
+
bool want_write_ = false;
|
|
265
|
+
/// A flush is already booked for the end of this reactor turn, so further
|
|
266
|
+
/// frames only need to join the queue rather than book another one.
|
|
267
|
+
bool flush_queued_ = false;
|
|
268
|
+
/// The queue went past its low-water mark and the caller has been told to
|
|
269
|
+
/// stop; cleared, with a notification, once it drains back under.
|
|
270
|
+
bool over_low_water_ = false;
|
|
271
|
+
/// Inside the on_writable callback — see flush().
|
|
272
|
+
bool in_writable_ = false;
|
|
273
|
+
/// Charged by send() callers on any thread, discharged by the reactor task
|
|
274
|
+
/// just before the frame reaches send() — see set_in_transit().
|
|
275
|
+
std::shared_ptr<const std::atomic<size_t>> in_transit_;
|
|
276
|
+
size_t send_high_water_ = kDefaultSendHighWater;
|
|
277
|
+
size_t send_low_water_ = kDefaultSendLowWater;
|
|
278
|
+
TimerId establish_timer_ = kInvalidTimerId; ///< connect+handshake deadline
|
|
279
|
+
std::string dial_host_; ///< address we dialed (outbound)
|
|
280
|
+
uint16_t dial_port_ = 0;
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
} // namespace librats
|