librats 1.0.2 → 2.2.0
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 +145 -331
- package/binding.gyp +16 -3
- package/lib/index.d.ts +288 -696
- package/lib/index.js +407 -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 +404 -179
- package/native-src/LICENSE +1 -1
- package/native-src/src/librats/bindings/rats.cpp +762 -0
- package/native-src/src/librats/bindings/rats.h +380 -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 +43 -0
- package/native-src/src/librats/core/types.h +103 -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/circuit_service.h +84 -0
- 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 +865 -0
- package/native-src/src/librats/node/node.h +344 -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 +170 -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 +605 -0
- package/native-src/src/librats/subsystems/hole_punch.h +290 -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/subsystems/relay.cpp +1142 -0
- package/native-src/src/librats/subsystems/relay.h +211 -0
- package/native-src/src/librats/subsystems/relay_service.h +46 -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 +588 -0
- package/native-src/src/librats/transport/reactor.h +262 -0
- package/native-src/src/librats/transport/reactor_pool.h +81 -0
- package/native-src/src/librats/transport/relay_link.cpp +208 -0
- package/native-src/src/librats/transport/relay_link.h +303 -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 +111 -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 +1067 -1323
- 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,588 @@
|
|
|
1
|
+
#include "librats/transport/reactor.h"
|
|
2
|
+
#include "librats/transport/tcp_link.h"
|
|
3
|
+
#include "librats/core/ip_address.h"
|
|
4
|
+
#include "librats/util/logger.h"
|
|
5
|
+
#include "librats/util/network_utils.h"
|
|
6
|
+
|
|
7
|
+
#include <cstring>
|
|
8
|
+
#include <utility>
|
|
9
|
+
|
|
10
|
+
namespace librats {
|
|
11
|
+
|
|
12
|
+
namespace {
|
|
13
|
+
|
|
14
|
+
/// Turn a dial target into the numeric endpoint the datagram transport needs.
|
|
15
|
+
/// A literal is used as-is; a hostname is resolved in the order this socket can
|
|
16
|
+
/// actually use.
|
|
17
|
+
///
|
|
18
|
+
/// The order matters as much as the result. One mux socket serves every peer and
|
|
19
|
+
/// is bound in one family, which the default configuration makes IPv4 — so asking
|
|
20
|
+
/// for AAAA first, as TCP can afford to, would keep handing back addresses this
|
|
21
|
+
/// socket cannot send to while the same host advertises a perfectly dialable A
|
|
22
|
+
/// record. Answering with a reachable address is the whole job here.
|
|
23
|
+
std::optional<Address> resolve_dial_target(const std::string& host, int port,
|
|
24
|
+
AddressFamily af) {
|
|
25
|
+
if (auto ip = IpAddress::parse(host))
|
|
26
|
+
return Address{*ip, static_cast<uint16_t>(port)};
|
|
27
|
+
|
|
28
|
+
if (af != AddressFamily::IPv4) {
|
|
29
|
+
if (auto ip = IpAddress::parse(network_utils::resolve_hostname_v6(host)))
|
|
30
|
+
return Address{*ip, static_cast<uint16_t>(port)};
|
|
31
|
+
}
|
|
32
|
+
if (af != AddressFamily::IPv6) {
|
|
33
|
+
if (auto ip = IpAddress::parse(network_utils::resolve_hostname(host)))
|
|
34
|
+
return Address{*ip, static_cast<uint16_t>(port)};
|
|
35
|
+
}
|
|
36
|
+
return std::nullopt;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
} // namespace
|
|
40
|
+
|
|
41
|
+
Reactor::Reactor(uint8_t index, ConnectionDelegate& delegate, SecurityProvider& security)
|
|
42
|
+
: index_(index), delegate_(delegate), security_(security), poller_(IOPoller::create()) {}
|
|
43
|
+
|
|
44
|
+
Reactor::~Reactor() {
|
|
45
|
+
stop();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ── Lifecycle ───────────────────────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
void Reactor::listen(socket_t server_socket) {
|
|
51
|
+
server_socket_ = server_socket;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
void Reactor::listen_udp(socket_t udp_socket, AddressFamily family) {
|
|
55
|
+
mux_ = std::make_unique<UdpMux>(udp_socket, family, *this);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
void Reactor::start() {
|
|
59
|
+
if (running_.exchange(true)) return;
|
|
60
|
+
thread_ = std::thread(&Reactor::run, this);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
void Reactor::stop() {
|
|
64
|
+
if (!running_.exchange(false)) {
|
|
65
|
+
if (thread_.joinable()) thread_.join();
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
wakeup_.signal(); // break the poll wait
|
|
69
|
+
if (thread_.joinable()) thread_.join();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ── Work submission ─────────────────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
bool Reactor::on_reactor_thread() const noexcept {
|
|
75
|
+
return std::this_thread::get_id() == thread_id_.load(std::memory_order_acquire);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
void Reactor::post(Task task) {
|
|
79
|
+
// After stop() the loop has exited and (apart from the brief stopping window
|
|
80
|
+
// caught by the final drain in run()) the task will never run. Enqueue anyway
|
|
81
|
+
// so its captures are released at destruction, but flag the dropped work.
|
|
82
|
+
if (!running_.load(std::memory_order_acquire))
|
|
83
|
+
LOG_DEBUG("reactor", "Reactor " << static_cast<int>(index_)
|
|
84
|
+
<< " post() after stop; task may not run");
|
|
85
|
+
tasks_.push(std::move(task));
|
|
86
|
+
wakeup_.signal();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
void Reactor::execute(Task task) {
|
|
90
|
+
if (on_reactor_thread()) task();
|
|
91
|
+
else post(std::move(task));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
ConnId Reactor::connect(std::string host, int port, TransportKind kind, DialProfile profile) {
|
|
95
|
+
if (kind == TransportKind::Udp && !mux_) {
|
|
96
|
+
LOG_DEBUG("reactor", "Reactor " << static_cast<int>(index_)
|
|
97
|
+
<< " has no datagram transport; refusing UDP dial to " << host << ":" << port);
|
|
98
|
+
return kInvalidConnId;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Reserve the id here, on the calling thread, rather than inside the task. The
|
|
102
|
+
// caller can then cancel this exact attempt (close()) before the task has even
|
|
103
|
+
// run — which is what makes racing one transport against the other cancellable
|
|
104
|
+
// instead of a matter of luck.
|
|
105
|
+
const ConnId id = next_conn_id_.fetch_add(1, std::memory_order_relaxed);
|
|
106
|
+
post([this, host = std::move(host), port, kind, profile, id] {
|
|
107
|
+
start_dial(id, host, port, kind, profile);
|
|
108
|
+
});
|
|
109
|
+
return id;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
void Reactor::start_dial(ConnId id, const std::string& host, int port, TransportKind kind,
|
|
113
|
+
DialProfile profile) {
|
|
114
|
+
if (kind == TransportKind::Udp) {
|
|
115
|
+
const auto target = resolve_dial_target(host, port, mux_->family());
|
|
116
|
+
if (!target) {
|
|
117
|
+
LOG_DEBUG("reactor", "Cannot resolve " << host << " for a UDP dial");
|
|
118
|
+
abort_dial(id, host, port);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
// The one mux socket cannot leave its own address family, and that is
|
|
122
|
+
// knowable here rather than after the fact. Saying so now costs nothing —
|
|
123
|
+
// the dialer brings the other transport in at once. Opening the stream
|
|
124
|
+
// anyway would spend the Syn's whole give-up on datagrams the kernel
|
|
125
|
+
// refuses one by one, and refusals at that layer are per-packet and
|
|
126
|
+
// silent: a dial that never had a chance would be indistinguishable from
|
|
127
|
+
// a peer that is merely slow to answer.
|
|
128
|
+
if (!family_can_reach(mux_->family(), target->ip.is_v6())) {
|
|
129
|
+
LOG_DEBUG("reactor", "Datagram socket cannot reach " << target->to_string()
|
|
130
|
+
<< "; leaving " << host << ":" << port << " to the other transport");
|
|
131
|
+
abort_dial(id, host, port);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
std::unique_ptr<Link> link = mux_->connect(*target, profile);
|
|
135
|
+
if (!link) { abort_dial(id, host, port); return; }
|
|
136
|
+
|
|
137
|
+
Connection* conn = adopt(std::move(link), ConnRole::Outbound, id);
|
|
138
|
+
if (conn) conn->set_dial_address(host, static_cast<uint16_t>(port));
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
socket_t sock = tcp_connect_start(host, port);
|
|
143
|
+
if (!is_valid_socket(sock)) {
|
|
144
|
+
LOG_DEBUG("reactor", "Outbound connect to " << host << ":" << port << " failed to start");
|
|
145
|
+
abort_dial(id, host, port);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
Connection* conn = adopt(std::make_unique<TcpLink>(sock, *this), ConnRole::Outbound, id);
|
|
149
|
+
if (!conn) { close_socket(sock); return; } // the dial was cancelled while queued
|
|
150
|
+
conn->set_dial_address(host, static_cast<uint16_t>(port));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
void Reactor::adopt_link(ConnId id, std::unique_ptr<Link> link, ConnRole role, bool connected) {
|
|
154
|
+
// A unique_ptr cannot be captured by a std::function, which is what the task
|
|
155
|
+
// queue holds — so the link travels in a shared holder it is moved out of on
|
|
156
|
+
// arrival. If the task is never run (a reactor stopped under us), the holder
|
|
157
|
+
// dies with it and takes the link with it, which is the right outcome.
|
|
158
|
+
auto holder = std::make_shared<std::unique_ptr<Link>>(std::move(link));
|
|
159
|
+
execute([this, id, holder, role, connected] {
|
|
160
|
+
Connection* conn = adopt(std::move(*holder), role, id);
|
|
161
|
+
if (!conn) return;
|
|
162
|
+
// Already-connected links skip the Connecting state entirely, exactly as an
|
|
163
|
+
// accepted socket does; the rest wait for the PollOut that wake() will bring
|
|
164
|
+
// once the far end answers.
|
|
165
|
+
if (connected) conn->start_handshake();
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
void Reactor::wake(ConnId id, uint32_t events) {
|
|
170
|
+
if (events == 0) return;
|
|
171
|
+
execute([this, id, events] { dispatch_events(id, events); });
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
void Reactor::abort_dial(ConnId id, const std::string& host, int port) {
|
|
175
|
+
resolve_dial(id); // the id is spent either way; release any cancellation slot
|
|
176
|
+
delegate_.on_dial_aborted(index_, id, host, static_cast<uint16_t>(port));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
void Reactor::close(ConnId id, CloseReason reason) {
|
|
180
|
+
close_conn(id, reason, /*spare_established=*/false);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
void Reactor::cancel_dial(ConnId id, CloseReason reason) {
|
|
184
|
+
close_conn(id, reason, /*spare_established=*/true);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
void Reactor::close_conn(ConnId id, CloseReason reason, bool spare_established) {
|
|
188
|
+
execute([this, id, reason, spare_established] {
|
|
189
|
+
auto it = conns_.find(id);
|
|
190
|
+
if (it != conns_.end()) {
|
|
191
|
+
// It got there first: the dial it was is over, and what it left behind
|
|
192
|
+
// is a connection to a peer. Whether that one or another link survives
|
|
193
|
+
// is decided by the peer table on both ends alike — see peer_table.cpp.
|
|
194
|
+
if (spare_established && it->second->state() == ConnState::Established) return;
|
|
195
|
+
mark_for_close(id, reason);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
// Not adopted yet: its connect task is still queued behind this one. Leave
|
|
199
|
+
// a note so adopt() drops it on arrival. Ids at or below the watermark have
|
|
200
|
+
// already come and gone, so they need no slot (and cannot leak one).
|
|
201
|
+
if (id != kInvalidConnId && id > resolved_watermark_) cancelled_dials_.insert(id);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
void Reactor::broadcast(FrameHeader header, std::shared_ptr<const Bytes> payload) {
|
|
206
|
+
execute([this, header, payload = std::move(payload)] {
|
|
207
|
+
for (auto& [id, conn] : conns_) {
|
|
208
|
+
if (conn->state() == ConnState::Established)
|
|
209
|
+
conn->send(header, ByteView(*payload));
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ── Timers ──────────────────────────────────────────────────────────────────
|
|
215
|
+
|
|
216
|
+
TimerId Reactor::schedule(std::chrono::milliseconds delay, Task on_fire) {
|
|
217
|
+
return timers_.schedule(delay, std::move(on_fire));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
void Reactor::cancel(TimerId id) {
|
|
221
|
+
timers_.cancel(id);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// ── Lookups / interest ──────────────────────────────────────────────────────
|
|
225
|
+
|
|
226
|
+
Connection* Reactor::find(ConnId id) noexcept {
|
|
227
|
+
auto it = conns_.find(id);
|
|
228
|
+
return it == conns_.end() ? nullptr : it->second.get();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
void Reactor::set_interest(socket_t sock, uint32_t events) {
|
|
232
|
+
poller_->modify(sock, events);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// ── Reactor loop ────────────────────────────────────────────────────────────
|
|
236
|
+
|
|
237
|
+
void Reactor::run() {
|
|
238
|
+
thread_id_ = std::this_thread::get_id();
|
|
239
|
+
LOG_INFO("reactor", "Reactor " << static_cast<int>(index_)
|
|
240
|
+
<< " started (backend: " << poller_->name() << ")");
|
|
241
|
+
|
|
242
|
+
set_socket_nonblocking(wakeup_.fd());
|
|
243
|
+
poller_->add(wakeup_.fd(), PollIn);
|
|
244
|
+
if (is_valid_socket(server_socket_)) {
|
|
245
|
+
set_socket_nonblocking(server_socket_);
|
|
246
|
+
poller_->add(server_socket_, PollIn);
|
|
247
|
+
}
|
|
248
|
+
if (mux_) {
|
|
249
|
+
set_socket_nonblocking(mux_->socket());
|
|
250
|
+
poller_->add(mux_->socket(), PollIn);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
PollResult events[kMaxEvents];
|
|
254
|
+
std::vector<Task> task_batch;
|
|
255
|
+
|
|
256
|
+
schedule_maintenance();
|
|
257
|
+
|
|
258
|
+
while (running_.load(std::memory_order_relaxed)) {
|
|
259
|
+
int timeout = timers_.next_timeout_ms(kMaxPollMs);
|
|
260
|
+
// The datagram transport keeps its own deadlines — retransmission timeouts,
|
|
261
|
+
// delayed acks, keep-alives — rather than being swept on a fixed cadence, so
|
|
262
|
+
// the wait has to cover the earliest of them too. With no stream due it
|
|
263
|
+
// contributes nothing, and an idle mux costs no wake-ups at all.
|
|
264
|
+
if (mux_) timeout = mux_->next_timeout_ms(timeout);
|
|
265
|
+
const int n = poller_->wait(events, kMaxEvents, timeout);
|
|
266
|
+
|
|
267
|
+
// Empty the wakeup pipe BEFORE taking the task snapshot, never after.
|
|
268
|
+
//
|
|
269
|
+
// post() pushes a task and then writes a byte, and those two arrive here as
|
|
270
|
+
// two separate facts. Draining last would let a byte written *while this
|
|
271
|
+
// turn was running its tasks* be thrown away along with the ones that
|
|
272
|
+
// brought us here — and the task it belonged to was pushed after the
|
|
273
|
+
// snapshot, so it does not run either. It would then sit in the queue with
|
|
274
|
+
// nothing left to announce it, until the next poll timeout woke the loop
|
|
275
|
+
// for some other reason: up to kMaxPollMs of latency, at random.
|
|
276
|
+
//
|
|
277
|
+
// Draining first inverts that. A byte written any time after this line is
|
|
278
|
+
// still in the pipe when the loop comes back round, so wait() returns at
|
|
279
|
+
// once — whether or not its task made this turn's snapshot. The cost is one
|
|
280
|
+
// recv per turn, which is nothing against the work a turn does.
|
|
281
|
+
drain_wakeup();
|
|
282
|
+
drain_tasks(task_batch); // connect/close/send-arm
|
|
283
|
+
for (int i = 0; i < n; ++i) handle_event(events[i]);
|
|
284
|
+
timers_.run_due();
|
|
285
|
+
// Service whatever datagram deadlines have come due. Deliberately not a
|
|
286
|
+
// TimerQueue entry: those deadlines move on nearly every packet, and
|
|
287
|
+
// re-scheduling a timer that often would cost far more than the one
|
|
288
|
+
// comparison this is when nothing is due. Before process_pending_close(),
|
|
289
|
+
// so a stream that dies here is torn down in the same turn.
|
|
290
|
+
if (mux_) mux_->tick();
|
|
291
|
+
// Everything queued by send() during this turn goes to the wire here, in
|
|
292
|
+
// one write per connection rather than one per frame. Deliberately after
|
|
293
|
+
// the mux tick — a handler it dispatched may well have sent something —
|
|
294
|
+
// and before the close pass, so a connection that dies on the write is
|
|
295
|
+
// torn down in the same turn.
|
|
296
|
+
flush_dirty();
|
|
297
|
+
process_pending_close();
|
|
298
|
+
// The mux batches its datagrams; the ones it collected inside a readable
|
|
299
|
+
// event or a tick have already gone out, but an application send reaches a
|
|
300
|
+
// UDP stream from a task, outside any batch of its own. Flushing once here
|
|
301
|
+
// covers those without giving any of them a timer to wait on.
|
|
302
|
+
if (mux_) mux_->flush_output();
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Graceful drain: run whatever was queued before/around stop() so in-flight
|
|
306
|
+
// sends flush and explicit closes settle, then tear everything down. (post()
|
|
307
|
+
// may still race in after this; such tasks are dropped — see post().)
|
|
308
|
+
drain_tasks(task_batch);
|
|
309
|
+
flush_dirty(); // in-flight sends leave before the door closes
|
|
310
|
+
process_pending_close();
|
|
311
|
+
shutdown_connections();
|
|
312
|
+
if (mux_) mux_->shutdown(); // after the connections, so no Link outlives it
|
|
313
|
+
|
|
314
|
+
// Timers outlive the loop that scheduled them unless the heap goes with it,
|
|
315
|
+
// and schedule_maintenance() re-arms itself: an entry left behind would start
|
|
316
|
+
// a second self-perpetuating chain on the next run(), so N restarts would cost
|
|
317
|
+
// N+1 sweeps per interval and N+1 timed wake-ups, for as long as the node
|
|
318
|
+
// lives. Whatever else is still pending — establish deadlines, backoffs —
|
|
319
|
+
// belongs to connections that no longer exist.
|
|
320
|
+
timers_.clear();
|
|
321
|
+
|
|
322
|
+
// Hand back the sockets this loop registered above. A registration belongs to a
|
|
323
|
+
// descriptor, and a stopped reactor can be started again on freshly opened ones
|
|
324
|
+
// — which the kernel is free to hand the very same descriptor numbers, and on
|
|
325
|
+
// the BSDs, which allocate the lowest free one, very nearly always does. The
|
|
326
|
+
// backends that keep a registration table of their own (kqueue, poll, IOCP)
|
|
327
|
+
// would then refuse to add a descriptor they believe is already watched, while
|
|
328
|
+
// the kernel forgot it the moment it was closed — leaving a restarted node
|
|
329
|
+
// polling nothing at all: no accepts, no datagrams, every dial to it timing out.
|
|
330
|
+
// epoll drops a closed descriptor by itself, which is why this only ever showed
|
|
331
|
+
// on macOS.
|
|
332
|
+
//
|
|
333
|
+
// The listen sockets themselves are the node's to close, and it does so after
|
|
334
|
+
// joining this thread — so they are still open here, which is exactly when a
|
|
335
|
+
// registration can still be withdrawn cleanly.
|
|
336
|
+
poller_->remove(wakeup_.fd());
|
|
337
|
+
if (is_valid_socket(server_socket_)) {
|
|
338
|
+
poller_->remove(server_socket_);
|
|
339
|
+
// Forgotten as well as unwatched: the next run() must not re-add a
|
|
340
|
+
// descriptor the node closed in between, which by then may name something
|
|
341
|
+
// else entirely. listen() hands over the new one before every start.
|
|
342
|
+
server_socket_ = RATS_INVALID_SOCKET;
|
|
343
|
+
}
|
|
344
|
+
if (mux_) {
|
|
345
|
+
poller_->remove(mux_->socket());
|
|
346
|
+
mux_.reset();
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
LOG_INFO("reactor", "Reactor " << static_cast<int>(index_) << " stopped");
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
void Reactor::drain_tasks(std::vector<Task>& scratch) {
|
|
353
|
+
tasks_.drain(scratch);
|
|
354
|
+
for (auto& task : scratch) task();
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
void Reactor::drain_wakeup() {
|
|
358
|
+
wakeup_.drain();
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
void Reactor::handle_event(const PollResult& ev) {
|
|
362
|
+
const socket_t fd = ev.fd;
|
|
363
|
+
|
|
364
|
+
// Already emptied at the top of the turn — see run(). Nothing to do but
|
|
365
|
+
// recognise it, so it is not mistaken for a connection's socket.
|
|
366
|
+
if (fd == wakeup_.fd()) return;
|
|
367
|
+
if (fd == server_socket_) { if (ev.events & PollIn) do_accept(); return; }
|
|
368
|
+
if (mux_ && fd == mux_->socket()) {
|
|
369
|
+
// Any event, not just PollIn. An error on a datagram socket belongs to one
|
|
370
|
+
// datagram — Windows reports an ICMP complaint about an earlier send on the
|
|
371
|
+
// *next* receive — and not to the socket, which stays usable; the cure is
|
|
372
|
+
// therefore to read, which surfaces or clears it and drains whatever queued
|
|
373
|
+
// up behind it. Treating PollErr as "nothing to do" would strand every
|
|
374
|
+
// datagram after it, since nothing else here ever re-arms this socket.
|
|
375
|
+
//
|
|
376
|
+
// Stopped short of draining: come straight back rather than waiting for the
|
|
377
|
+
// next datagram to re-arm an edge-triggered poller.
|
|
378
|
+
if (mux_->on_readable()) wakeup_.signal();
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
auto it = fd_to_conn_.find(fd);
|
|
383
|
+
if (it == fd_to_conn_.end()) return;
|
|
384
|
+
dispatch_events(it->second, ev.events);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
void Reactor::dispatch_events(ConnId id, uint32_t events) {
|
|
388
|
+
Connection* conn = find(id);
|
|
389
|
+
if (!conn) return;
|
|
390
|
+
if (conn->state() == ConnState::Closing || conn->state() == ConnState::Closed) return;
|
|
391
|
+
// Condemned earlier this turn, just not torn down yet — process_pending_close()
|
|
392
|
+
// runs at the end of the turn, so events polled alongside the one that closed it
|
|
393
|
+
// would otherwise still be delivered, and the connection could change state
|
|
394
|
+
// *after* the decision to close it was taken against the state it had.
|
|
395
|
+
//
|
|
396
|
+
// Which is not merely wasted work on bytes about to be dropped. cancel_dial()
|
|
397
|
+
// spares an attempt that has established; let one finish its handshake here,
|
|
398
|
+
// just after that check found it still handshaking, and it reaches the peer
|
|
399
|
+
// table as a duplicate and supersedes the sibling that beat it — while itself
|
|
400
|
+
// still standing condemned. Both are then gone at the end of the turn and the
|
|
401
|
+
// peer is lost, which is the very failure the cancel/close split exists to
|
|
402
|
+
// prevent. The map is empty on every turn that closes nothing, nearly all of them.
|
|
403
|
+
if (!pending_close_.empty() && pending_close_.count(id)) return;
|
|
404
|
+
|
|
405
|
+
bool keep = true;
|
|
406
|
+
if (events & (PollErr | PollHup)) {
|
|
407
|
+
keep = conn->on_error();
|
|
408
|
+
} else {
|
|
409
|
+
if (keep && (events & PollIn)) keep = conn->on_readable();
|
|
410
|
+
if (keep && (events & PollOut)) keep = conn->on_writable();
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (!keep) mark_for_close(id, conn->close_reason());
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
void Reactor::dispatch_link_events(ConnId id, uint32_t events) {
|
|
417
|
+
dispatch_events(id, events);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
void Reactor::schedule_maintenance() {
|
|
421
|
+
timers_.schedule(kMaintenanceInterval, [this] {
|
|
422
|
+
// Connections being torn down this tick are still in conns_; the sweep is
|
|
423
|
+
// read-only with respect to the map, and on_maintenance_tick() never closes.
|
|
424
|
+
for (auto& [id, conn] : conns_) conn->on_maintenance_tick();
|
|
425
|
+
schedule_maintenance();
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
void Reactor::do_accept() {
|
|
430
|
+
// Level-triggered: drain all pending connections this tick. Use a raw,
|
|
431
|
+
// non-logging accept — accept_client() logs an error on every EWOULDBLOCK,
|
|
432
|
+
// which is the normal "no more pending" signal here.
|
|
433
|
+
while (true) {
|
|
434
|
+
socket_t client = ::accept(server_socket_, nullptr, nullptr);
|
|
435
|
+
if (!is_valid_socket(client)) break; // EWOULDBLOCK / error → done this tick
|
|
436
|
+
suppress_sigpipe(client); // not inherited from the listener; raw accept()
|
|
437
|
+
|
|
438
|
+
// Admission control: refuse new inbound peers when at capacity, before
|
|
439
|
+
// paying any handshake cost. Keep draining the backlog so the listener
|
|
440
|
+
// doesn't stay readable. The precise per-peer cap is enforced again at
|
|
441
|
+
// on_established (this coarse gate can't yet know the remote's identity).
|
|
442
|
+
if (!delegate_.admit_inbound()) {
|
|
443
|
+
close_socket(client);
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
Connection* conn = adopt(std::make_unique<TcpLink>(client, *this), ConnRole::Inbound,
|
|
448
|
+
next_conn_id_.fetch_add(1, std::memory_order_relaxed));
|
|
449
|
+
if (!conn) { close_socket(client); continue; }
|
|
450
|
+
conn->start_handshake(); // accepted sockets are already connected
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
ConnId Reactor::adopt_inbound_link(std::unique_ptr<Link> link) {
|
|
455
|
+
// Same coarse admission gate the TCP listener applies, for the same reason:
|
|
456
|
+
// turn a flood away before any handshake work is done for it.
|
|
457
|
+
if (!delegate_.admit_inbound()) return kInvalidConnId;
|
|
458
|
+
|
|
459
|
+
const ConnId id = next_conn_id_.fetch_add(1, std::memory_order_relaxed);
|
|
460
|
+
Connection* conn = adopt(std::move(link), ConnRole::Inbound, id);
|
|
461
|
+
if (!conn) return kInvalidConnId;
|
|
462
|
+
conn->start_handshake(); // an accepted stream is already connected
|
|
463
|
+
return id;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
bool Reactor::resolve_dial(ConnId id) {
|
|
467
|
+
if (id > resolved_watermark_) resolved_watermark_ = id;
|
|
468
|
+
return cancelled_dials_.erase(id) > 0;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
Connection* Reactor::adopt(std::unique_ptr<Link> link, ConnRole role, ConnId id) {
|
|
472
|
+
// A dial cancelled while its task sat in the queue never becomes a connection;
|
|
473
|
+
// dropping `link` here is what actually calls the attempt off.
|
|
474
|
+
if (resolve_dial(id)) return nullptr;
|
|
475
|
+
|
|
476
|
+
const socket_t fd = link->fd();
|
|
477
|
+
if (is_valid_socket(fd)) {
|
|
478
|
+
set_socket_nonblocking(fd);
|
|
479
|
+
// Only a TCP link has a socket of its own, so this reaches exactly the
|
|
480
|
+
// sockets Nagle applies to. Safe to do here and nowhere else because the
|
|
481
|
+
// send queue now aggregates on our side — see set_tcp_nodelay().
|
|
482
|
+
set_tcp_nodelay(fd);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
auto conn = std::make_unique<Connection>(id, std::move(link), role, *this, delegate_);
|
|
486
|
+
Connection* raw = conn.get();
|
|
487
|
+
raw->link().attach(id);
|
|
488
|
+
|
|
489
|
+
conns_.emplace(id, std::move(conn));
|
|
490
|
+
conn_count_.fetch_add(1, std::memory_order_relaxed);
|
|
491
|
+
|
|
492
|
+
if (is_valid_socket(fd)) {
|
|
493
|
+
fd_to_conn_.emplace(fd, id);
|
|
494
|
+
// Inbound sockets are connected: watch for readable. Outbound sockets are
|
|
495
|
+
// still connecting: watch for writable, which signals connect completion.
|
|
496
|
+
poller_->add(fd, role == ConnRole::Inbound ? PollIn : PollOut);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Reap connections that never reach Established (stuck connect or handshake).
|
|
500
|
+
TimerId timer = timers_.schedule(kEstablishTimeout, [this, id] {
|
|
501
|
+
auto it = conns_.find(id);
|
|
502
|
+
if (it == conns_.end()) return;
|
|
503
|
+
const ConnState st = it->second->state();
|
|
504
|
+
if (st != ConnState::Established && st != ConnState::Closing && st != ConnState::Closed) {
|
|
505
|
+
mark_for_close(id, st == ConnState::Connecting ? CloseReason::ConnectFailed
|
|
506
|
+
: CloseReason::HandshakeFailed);
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
raw->set_establish_timer(timer);
|
|
510
|
+
return raw;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// ── Teardown ────────────────────────────────────────────────────────────────
|
|
514
|
+
|
|
515
|
+
void Reactor::mark_for_close(ConnId id, CloseReason reason) {
|
|
516
|
+
pending_close_.emplace(id, reason); // first reason wins
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
void Reactor::flush_dirty() {
|
|
520
|
+
// Swapped out first: a flush can close a connection, and a close can queue
|
|
521
|
+
// further work — none of which should extend the batch being walked.
|
|
522
|
+
//
|
|
523
|
+
// Looped, because a flush can also *book another one*: writing a relayed
|
|
524
|
+
// circuit hands its bytes to the connection carrying it (see
|
|
525
|
+
// transport/relay_link.h), which books its own flush from inside this one. A
|
|
526
|
+
// single pass would leave those bytes queued until the next turn of the loop —
|
|
527
|
+
// i.e. behind a poll wait — adding tens of milliseconds to every hop of a
|
|
528
|
+
// relayed path. The bound is a backstop against a pathological chain, not an
|
|
529
|
+
// expected limit: honest work needs two passes at most.
|
|
530
|
+
std::vector<ConnId> batch;
|
|
531
|
+
for (int pass = 0; pass < kMaxFlushPasses && !dirty_.empty(); ++pass) {
|
|
532
|
+
batch.clear();
|
|
533
|
+
batch.swap(dirty_);
|
|
534
|
+
for (const ConnId id : batch) {
|
|
535
|
+
Connection* conn = find(id);
|
|
536
|
+
if (!conn) continue; // gone since it booked
|
|
537
|
+
if (conn->state() != ConnState::Established) continue; // closing; nothing to owe
|
|
538
|
+
if (!conn->flush_pending()) mark_for_close(id, conn->close_reason());
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
void Reactor::process_pending_close() {
|
|
544
|
+
if (pending_close_.empty()) return;
|
|
545
|
+
auto batch = std::move(pending_close_);
|
|
546
|
+
pending_close_.clear();
|
|
547
|
+
for (const auto& [id, reason] : batch) remove(id, reason);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
void Reactor::remove(ConnId id, CloseReason reason) {
|
|
551
|
+
auto it = conns_.find(id);
|
|
552
|
+
if (it == conns_.end()) return;
|
|
553
|
+
|
|
554
|
+
std::unique_ptr<Connection> conn = std::move(it->second);
|
|
555
|
+
conns_.erase(it);
|
|
556
|
+
|
|
557
|
+
const socket_t fd = conn->socket();
|
|
558
|
+
if (is_valid_socket(fd)) {
|
|
559
|
+
fd_to_conn_.erase(fd);
|
|
560
|
+
poller_->remove(fd);
|
|
561
|
+
}
|
|
562
|
+
conn->cancel_establish_timer(); // stop the reaper before this fd can be reused
|
|
563
|
+
conn_count_.fetch_sub(1, std::memory_order_relaxed);
|
|
564
|
+
|
|
565
|
+
LOG_DEBUG("reactor", "Connection " << id << " closed (" << to_string(reason) << ")");
|
|
566
|
+
conn->shutdown_link(reason); // let the transport say goodbye / flush
|
|
567
|
+
delegate_.on_closed(*conn, reason);
|
|
568
|
+
if (is_valid_socket(fd)) close_socket(fd);
|
|
569
|
+
// ~Connection here releases the Link, which is what hands a UDP stream back to
|
|
570
|
+
// the mux (to linger briefly or go straight away).
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
void Reactor::shutdown_connections() {
|
|
574
|
+
for (auto& [id, conn] : conns_) {
|
|
575
|
+
const socket_t fd = conn->socket();
|
|
576
|
+
if (is_valid_socket(fd)) poller_->remove(fd);
|
|
577
|
+
conn->shutdown_link(CloseReason::ReactorShutdown);
|
|
578
|
+
delegate_.on_closed(*conn, CloseReason::ReactorShutdown);
|
|
579
|
+
if (is_valid_socket(fd)) close_socket(fd);
|
|
580
|
+
}
|
|
581
|
+
conns_.clear();
|
|
582
|
+
fd_to_conn_.clear();
|
|
583
|
+
pending_close_.clear();
|
|
584
|
+
cancelled_dials_.clear();
|
|
585
|
+
conn_count_.store(0, std::memory_order_relaxed);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
} // namespace librats
|