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,96 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file link.h
|
|
5
|
+
* @brief The byte-stream a Connection runs on, independent of how it is carried.
|
|
6
|
+
*
|
|
7
|
+
* Everything above this line — block framing, the Noise handshake, the Session,
|
|
8
|
+
* backpressure, the establish deadline — only ever needs an ordered, reliable,
|
|
9
|
+
* non-blocking byte stream. A Link is exactly that and nothing more, so the same
|
|
10
|
+
* Connection code drives a kernel TCP socket (TcpLink) and the library's own
|
|
11
|
+
* reliability layer over datagrams (UdpStreamLink → UdpStream). Neither transport
|
|
12
|
+
* is a special case of the other: both are first-class, and a peer connected over
|
|
13
|
+
* either is indistinguishable to every layer above.
|
|
14
|
+
*
|
|
15
|
+
* Ownership and threading follow the rest of the transport layer: a Link belongs
|
|
16
|
+
* to exactly one Connection, which belongs to exactly one Reactor, and is touched
|
|
17
|
+
* only by that reactor's thread. It therefore holds no locks and no atomics.
|
|
18
|
+
*
|
|
19
|
+
* The read/write calls mirror non-blocking socket semantics deliberately, because
|
|
20
|
+
* that is what the reactor loop is built around:
|
|
21
|
+
* - read() fills the caller's span and reports WouldBlock once the link has no
|
|
22
|
+
* more data ready, or Closed when the peer has finished sending.
|
|
23
|
+
* - write() takes what it can and reports WouldBlock when it can take no more;
|
|
24
|
+
* the Connection keeps the remainder queued and calls want_write(true)
|
|
25
|
+
* so it is told when to retry.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
#include "librats/core/address.h"
|
|
29
|
+
#include "librats/core/bytes.h"
|
|
30
|
+
#include "librats/core/socket.h"
|
|
31
|
+
#include "librats/core/types.h"
|
|
32
|
+
|
|
33
|
+
#include <cstddef>
|
|
34
|
+
#include <optional>
|
|
35
|
+
|
|
36
|
+
namespace librats {
|
|
37
|
+
|
|
38
|
+
class Link {
|
|
39
|
+
public:
|
|
40
|
+
virtual ~Link() = default;
|
|
41
|
+
|
|
42
|
+
/// Outcome of a read/write attempt. `Closed` is orderly (the peer is done
|
|
43
|
+
/// sending); `Error` means the link is broken and must be torn down.
|
|
44
|
+
enum class Status { Ok, WouldBlock, Closed, Error };
|
|
45
|
+
|
|
46
|
+
struct IoResult {
|
|
47
|
+
size_t bytes = 0;
|
|
48
|
+
Status status = Status::WouldBlock;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
virtual TransportKind kind() const noexcept = 0;
|
|
52
|
+
|
|
53
|
+
/// Read up to `into.size()` bytes. A Closed or Error result always carries
|
|
54
|
+
/// zero bytes — data and end-of-stream are never reported together, so the
|
|
55
|
+
/// caller drains first and only then sees the close.
|
|
56
|
+
virtual IoResult read(ByteSpan into) = 0;
|
|
57
|
+
|
|
58
|
+
/// Hand `count` contiguous slices to the link in order, writing as many bytes
|
|
59
|
+
/// as it will take (a partial write is normal and expected).
|
|
60
|
+
virtual IoResult write(const ByteView* slices, size_t count) = 0;
|
|
61
|
+
|
|
62
|
+
/// Called once the link reports writable while the connection is still
|
|
63
|
+
/// connecting. Returns false if the connect attempt failed.
|
|
64
|
+
virtual bool connect_completed() = 0;
|
|
65
|
+
|
|
66
|
+
/// Ask to be woken (via Connection::on_writable) when more can be written.
|
|
67
|
+
/// Called only when the state actually changes, so an implementation may
|
|
68
|
+
/// treat it as an unconditional set.
|
|
69
|
+
virtual void want_write(bool on) = 0;
|
|
70
|
+
|
|
71
|
+
/// The peer's endpoint. For TCP the port is the peer's *ephemeral* source
|
|
72
|
+
/// port, so only the IP is meaningful (see identify); for UDP it is the
|
|
73
|
+
/// endpoint datagrams are exchanged with. nullopt if not yet known.
|
|
74
|
+
virtual std::optional<Address> remote_endpoint() const = 0;
|
|
75
|
+
|
|
76
|
+
/// The kernel socket backing this link, or RATS_INVALID_SOCKET when the link
|
|
77
|
+
/// has none of its own (a UDP stream shares the mux's socket with every other
|
|
78
|
+
/// stream). The Reactor uses it to decide whether the link is poller-driven.
|
|
79
|
+
virtual socket_t fd() const noexcept { return RATS_INVALID_SOCKET; }
|
|
80
|
+
|
|
81
|
+
/// Told the ConnId the Reactor assigned, right after adoption. A link that has
|
|
82
|
+
/// to be reachable from somewhere other than its Connection (a UDP stream, from
|
|
83
|
+
/// the datagram path) needs it; a plain socket does not.
|
|
84
|
+
virtual void attach(ConnId) {}
|
|
85
|
+
|
|
86
|
+
/// Why the link broke, consulted when read/write reported Error. Lets a
|
|
87
|
+
/// transport that knows more than "the socket failed" — a dial that was never
|
|
88
|
+
/// answered, a peer that went silent — say so.
|
|
89
|
+
virtual CloseReason error_reason() const { return CloseReason::PeerReset; }
|
|
90
|
+
|
|
91
|
+
/// Begin an orderly shutdown. Called by the Reactor as it tears the connection
|
|
92
|
+
/// down; the Link may still linger internally to flush what it owes the peer.
|
|
93
|
+
virtual void close(CloseReason reason) = 0;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
} // namespace librats
|
|
@@ -0,0 +1,540 @@
|
|
|
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::abort_dial(ConnId id, const std::string& host, int port) {
|
|
154
|
+
resolve_dial(id); // the id is spent either way; release any cancellation slot
|
|
155
|
+
delegate_.on_dial_aborted(index_, id, host, static_cast<uint16_t>(port));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
void Reactor::close(ConnId id, CloseReason reason) {
|
|
159
|
+
close_conn(id, reason, /*spare_established=*/false);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
void Reactor::cancel_dial(ConnId id, CloseReason reason) {
|
|
163
|
+
close_conn(id, reason, /*spare_established=*/true);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
void Reactor::close_conn(ConnId id, CloseReason reason, bool spare_established) {
|
|
167
|
+
execute([this, id, reason, spare_established] {
|
|
168
|
+
auto it = conns_.find(id);
|
|
169
|
+
if (it != conns_.end()) {
|
|
170
|
+
// It got there first: the dial it was is over, and what it left behind
|
|
171
|
+
// is a connection to a peer. Whether that one or another link survives
|
|
172
|
+
// is decided by the peer table on both ends alike — see peer_table.cpp.
|
|
173
|
+
if (spare_established && it->second->state() == ConnState::Established) return;
|
|
174
|
+
mark_for_close(id, reason);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
// Not adopted yet: its connect task is still queued behind this one. Leave
|
|
178
|
+
// a note so adopt() drops it on arrival. Ids at or below the watermark have
|
|
179
|
+
// already come and gone, so they need no slot (and cannot leak one).
|
|
180
|
+
if (id != kInvalidConnId && id > resolved_watermark_) cancelled_dials_.insert(id);
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
void Reactor::broadcast(FrameHeader header, std::shared_ptr<const Bytes> payload) {
|
|
185
|
+
execute([this, header, payload = std::move(payload)] {
|
|
186
|
+
for (auto& [id, conn] : conns_) {
|
|
187
|
+
if (conn->state() == ConnState::Established)
|
|
188
|
+
conn->send(header, ByteView(*payload));
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ── Timers ──────────────────────────────────────────────────────────────────
|
|
194
|
+
|
|
195
|
+
TimerId Reactor::schedule(std::chrono::milliseconds delay, Task on_fire) {
|
|
196
|
+
return timers_.schedule(delay, std::move(on_fire));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
void Reactor::cancel(TimerId id) {
|
|
200
|
+
timers_.cancel(id);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ── Lookups / interest ──────────────────────────────────────────────────────
|
|
204
|
+
|
|
205
|
+
Connection* Reactor::find(ConnId id) noexcept {
|
|
206
|
+
auto it = conns_.find(id);
|
|
207
|
+
return it == conns_.end() ? nullptr : it->second.get();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
void Reactor::set_interest(socket_t sock, uint32_t events) {
|
|
211
|
+
poller_->modify(sock, events);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ── Reactor loop ────────────────────────────────────────────────────────────
|
|
215
|
+
|
|
216
|
+
void Reactor::run() {
|
|
217
|
+
thread_id_ = std::this_thread::get_id();
|
|
218
|
+
LOG_INFO("reactor", "Reactor " << static_cast<int>(index_)
|
|
219
|
+
<< " started (backend: " << poller_->name() << ")");
|
|
220
|
+
|
|
221
|
+
set_socket_nonblocking(wakeup_.fd());
|
|
222
|
+
poller_->add(wakeup_.fd(), PollIn);
|
|
223
|
+
if (is_valid_socket(server_socket_)) {
|
|
224
|
+
set_socket_nonblocking(server_socket_);
|
|
225
|
+
poller_->add(server_socket_, PollIn);
|
|
226
|
+
}
|
|
227
|
+
if (mux_) {
|
|
228
|
+
set_socket_nonblocking(mux_->socket());
|
|
229
|
+
poller_->add(mux_->socket(), PollIn);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
PollResult events[kMaxEvents];
|
|
233
|
+
std::vector<Task> task_batch;
|
|
234
|
+
|
|
235
|
+
schedule_maintenance();
|
|
236
|
+
|
|
237
|
+
while (running_.load(std::memory_order_relaxed)) {
|
|
238
|
+
int timeout = timers_.next_timeout_ms(kMaxPollMs);
|
|
239
|
+
// The datagram transport keeps its own deadlines — retransmission timeouts,
|
|
240
|
+
// delayed acks, keep-alives — rather than being swept on a fixed cadence, so
|
|
241
|
+
// the wait has to cover the earliest of them too. With no stream due it
|
|
242
|
+
// contributes nothing, and an idle mux costs no wake-ups at all.
|
|
243
|
+
if (mux_) timeout = mux_->next_timeout_ms(timeout);
|
|
244
|
+
const int n = poller_->wait(events, kMaxEvents, timeout);
|
|
245
|
+
|
|
246
|
+
drain_tasks(task_batch); // connect/close/send-arm
|
|
247
|
+
for (int i = 0; i < n; ++i) handle_event(events[i]);
|
|
248
|
+
timers_.run_due();
|
|
249
|
+
// Service whatever datagram deadlines have come due. Deliberately not a
|
|
250
|
+
// TimerQueue entry: those deadlines move on nearly every packet, and
|
|
251
|
+
// re-scheduling a timer that often would cost far more than the one
|
|
252
|
+
// comparison this is when nothing is due. Before process_pending_close(),
|
|
253
|
+
// so a stream that dies here is torn down in the same turn.
|
|
254
|
+
if (mux_) mux_->tick();
|
|
255
|
+
// Everything queued by send() during this turn goes to the wire here, in
|
|
256
|
+
// one write per connection rather than one per frame. Deliberately after
|
|
257
|
+
// the mux tick — a handler it dispatched may well have sent something —
|
|
258
|
+
// and before the close pass, so a connection that dies on the write is
|
|
259
|
+
// torn down in the same turn.
|
|
260
|
+
flush_dirty();
|
|
261
|
+
process_pending_close();
|
|
262
|
+
// The mux batches its datagrams; the ones it collected inside a readable
|
|
263
|
+
// event or a tick have already gone out, but an application send reaches a
|
|
264
|
+
// UDP stream from a task, outside any batch of its own. Flushing once here
|
|
265
|
+
// covers those without giving any of them a timer to wait on.
|
|
266
|
+
if (mux_) mux_->flush_output();
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Graceful drain: run whatever was queued before/around stop() so in-flight
|
|
270
|
+
// sends flush and explicit closes settle, then tear everything down. (post()
|
|
271
|
+
// may still race in after this; such tasks are dropped — see post().)
|
|
272
|
+
drain_tasks(task_batch);
|
|
273
|
+
flush_dirty(); // in-flight sends leave before the door closes
|
|
274
|
+
process_pending_close();
|
|
275
|
+
shutdown_connections();
|
|
276
|
+
if (mux_) mux_->shutdown(); // after the connections, so no Link outlives it
|
|
277
|
+
|
|
278
|
+
// Timers outlive the loop that scheduled them unless the heap goes with it,
|
|
279
|
+
// and schedule_maintenance() re-arms itself: an entry left behind would start
|
|
280
|
+
// a second self-perpetuating chain on the next run(), so N restarts would cost
|
|
281
|
+
// N+1 sweeps per interval and N+1 timed wake-ups, for as long as the node
|
|
282
|
+
// lives. Whatever else is still pending — establish deadlines, backoffs —
|
|
283
|
+
// belongs to connections that no longer exist.
|
|
284
|
+
timers_.clear();
|
|
285
|
+
|
|
286
|
+
// Hand back the sockets this loop registered above. A registration belongs to a
|
|
287
|
+
// descriptor, and a stopped reactor can be started again on freshly opened ones
|
|
288
|
+
// — which the kernel is free to hand the very same descriptor numbers, and on
|
|
289
|
+
// the BSDs, which allocate the lowest free one, very nearly always does. The
|
|
290
|
+
// backends that keep a registration table of their own (kqueue, poll, IOCP)
|
|
291
|
+
// would then refuse to add a descriptor they believe is already watched, while
|
|
292
|
+
// the kernel forgot it the moment it was closed — leaving a restarted node
|
|
293
|
+
// polling nothing at all: no accepts, no datagrams, every dial to it timing out.
|
|
294
|
+
// epoll drops a closed descriptor by itself, which is why this only ever showed
|
|
295
|
+
// on macOS.
|
|
296
|
+
//
|
|
297
|
+
// The listen sockets themselves are the node's to close, and it does so after
|
|
298
|
+
// joining this thread — so they are still open here, which is exactly when a
|
|
299
|
+
// registration can still be withdrawn cleanly.
|
|
300
|
+
poller_->remove(wakeup_.fd());
|
|
301
|
+
if (is_valid_socket(server_socket_)) {
|
|
302
|
+
poller_->remove(server_socket_);
|
|
303
|
+
// Forgotten as well as unwatched: the next run() must not re-add a
|
|
304
|
+
// descriptor the node closed in between, which by then may name something
|
|
305
|
+
// else entirely. listen() hands over the new one before every start.
|
|
306
|
+
server_socket_ = RATS_INVALID_SOCKET;
|
|
307
|
+
}
|
|
308
|
+
if (mux_) {
|
|
309
|
+
poller_->remove(mux_->socket());
|
|
310
|
+
mux_.reset();
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
LOG_INFO("reactor", "Reactor " << static_cast<int>(index_) << " stopped");
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
void Reactor::drain_tasks(std::vector<Task>& scratch) {
|
|
317
|
+
tasks_.drain(scratch);
|
|
318
|
+
for (auto& task : scratch) task();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
void Reactor::drain_wakeup() {
|
|
322
|
+
wakeup_.drain();
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
void Reactor::handle_event(const PollResult& ev) {
|
|
326
|
+
const socket_t fd = ev.fd;
|
|
327
|
+
|
|
328
|
+
if (fd == wakeup_.fd()) { drain_wakeup(); return; }
|
|
329
|
+
if (fd == server_socket_) { if (ev.events & PollIn) do_accept(); return; }
|
|
330
|
+
if (mux_ && fd == mux_->socket()) {
|
|
331
|
+
// Any event, not just PollIn. An error on a datagram socket belongs to one
|
|
332
|
+
// datagram — Windows reports an ICMP complaint about an earlier send on the
|
|
333
|
+
// *next* receive — and not to the socket, which stays usable; the cure is
|
|
334
|
+
// therefore to read, which surfaces or clears it and drains whatever queued
|
|
335
|
+
// up behind it. Treating PollErr as "nothing to do" would strand every
|
|
336
|
+
// datagram after it, since nothing else here ever re-arms this socket.
|
|
337
|
+
//
|
|
338
|
+
// Stopped short of draining: come straight back rather than waiting for the
|
|
339
|
+
// next datagram to re-arm an edge-triggered poller.
|
|
340
|
+
if (mux_->on_readable()) wakeup_.signal();
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
auto it = fd_to_conn_.find(fd);
|
|
345
|
+
if (it == fd_to_conn_.end()) return;
|
|
346
|
+
dispatch_events(it->second, ev.events);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
void Reactor::dispatch_events(ConnId id, uint32_t events) {
|
|
350
|
+
Connection* conn = find(id);
|
|
351
|
+
if (!conn) return;
|
|
352
|
+
if (conn->state() == ConnState::Closing || conn->state() == ConnState::Closed) return;
|
|
353
|
+
// Condemned earlier this turn, just not torn down yet — process_pending_close()
|
|
354
|
+
// runs at the end of the turn, so events polled alongside the one that closed it
|
|
355
|
+
// would otherwise still be delivered, and the connection could change state
|
|
356
|
+
// *after* the decision to close it was taken against the state it had.
|
|
357
|
+
//
|
|
358
|
+
// Which is not merely wasted work on bytes about to be dropped. cancel_dial()
|
|
359
|
+
// spares an attempt that has established; let one finish its handshake here,
|
|
360
|
+
// just after that check found it still handshaking, and it reaches the peer
|
|
361
|
+
// table as a duplicate and supersedes the sibling that beat it — while itself
|
|
362
|
+
// still standing condemned. Both are then gone at the end of the turn and the
|
|
363
|
+
// peer is lost, which is the very failure the cancel/close split exists to
|
|
364
|
+
// prevent. The map is empty on every turn that closes nothing, nearly all of them.
|
|
365
|
+
if (!pending_close_.empty() && pending_close_.count(id)) return;
|
|
366
|
+
|
|
367
|
+
bool keep = true;
|
|
368
|
+
if (events & (PollErr | PollHup)) {
|
|
369
|
+
keep = conn->on_error();
|
|
370
|
+
} else {
|
|
371
|
+
if (keep && (events & PollIn)) keep = conn->on_readable();
|
|
372
|
+
if (keep && (events & PollOut)) keep = conn->on_writable();
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (!keep) mark_for_close(id, conn->close_reason());
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
void Reactor::dispatch_link_events(ConnId id, uint32_t events) {
|
|
379
|
+
dispatch_events(id, events);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
void Reactor::schedule_maintenance() {
|
|
383
|
+
timers_.schedule(kMaintenanceInterval, [this] {
|
|
384
|
+
// Connections being torn down this tick are still in conns_; the sweep is
|
|
385
|
+
// read-only with respect to the map, and on_maintenance_tick() never closes.
|
|
386
|
+
for (auto& [id, conn] : conns_) conn->on_maintenance_tick();
|
|
387
|
+
schedule_maintenance();
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
void Reactor::do_accept() {
|
|
392
|
+
// Level-triggered: drain all pending connections this tick. Use a raw,
|
|
393
|
+
// non-logging accept — accept_client() logs an error on every EWOULDBLOCK,
|
|
394
|
+
// which is the normal "no more pending" signal here.
|
|
395
|
+
while (true) {
|
|
396
|
+
socket_t client = ::accept(server_socket_, nullptr, nullptr);
|
|
397
|
+
if (!is_valid_socket(client)) break; // EWOULDBLOCK / error → done this tick
|
|
398
|
+
suppress_sigpipe(client); // not inherited from the listener; raw accept()
|
|
399
|
+
|
|
400
|
+
// Admission control: refuse new inbound peers when at capacity, before
|
|
401
|
+
// paying any handshake cost. Keep draining the backlog so the listener
|
|
402
|
+
// doesn't stay readable. The precise per-peer cap is enforced again at
|
|
403
|
+
// on_established (this coarse gate can't yet know the remote's identity).
|
|
404
|
+
if (!delegate_.admit_inbound()) {
|
|
405
|
+
close_socket(client);
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
Connection* conn = adopt(std::make_unique<TcpLink>(client, *this), ConnRole::Inbound,
|
|
410
|
+
next_conn_id_.fetch_add(1, std::memory_order_relaxed));
|
|
411
|
+
if (!conn) { close_socket(client); continue; }
|
|
412
|
+
conn->start_handshake(); // accepted sockets are already connected
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
ConnId Reactor::adopt_inbound_link(std::unique_ptr<Link> link) {
|
|
417
|
+
// Same coarse admission gate the TCP listener applies, for the same reason:
|
|
418
|
+
// turn a flood away before any handshake work is done for it.
|
|
419
|
+
if (!delegate_.admit_inbound()) return kInvalidConnId;
|
|
420
|
+
|
|
421
|
+
const ConnId id = next_conn_id_.fetch_add(1, std::memory_order_relaxed);
|
|
422
|
+
Connection* conn = adopt(std::move(link), ConnRole::Inbound, id);
|
|
423
|
+
if (!conn) return kInvalidConnId;
|
|
424
|
+
conn->start_handshake(); // an accepted stream is already connected
|
|
425
|
+
return id;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
bool Reactor::resolve_dial(ConnId id) {
|
|
429
|
+
if (id > resolved_watermark_) resolved_watermark_ = id;
|
|
430
|
+
return cancelled_dials_.erase(id) > 0;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
Connection* Reactor::adopt(std::unique_ptr<Link> link, ConnRole role, ConnId id) {
|
|
434
|
+
// A dial cancelled while its task sat in the queue never becomes a connection;
|
|
435
|
+
// dropping `link` here is what actually calls the attempt off.
|
|
436
|
+
if (resolve_dial(id)) return nullptr;
|
|
437
|
+
|
|
438
|
+
const socket_t fd = link->fd();
|
|
439
|
+
if (is_valid_socket(fd)) {
|
|
440
|
+
set_socket_nonblocking(fd);
|
|
441
|
+
// Only a TCP link has a socket of its own, so this reaches exactly the
|
|
442
|
+
// sockets Nagle applies to. Safe to do here and nowhere else because the
|
|
443
|
+
// send queue now aggregates on our side — see set_tcp_nodelay().
|
|
444
|
+
set_tcp_nodelay(fd);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
auto conn = std::make_unique<Connection>(id, std::move(link), role, *this, delegate_);
|
|
448
|
+
Connection* raw = conn.get();
|
|
449
|
+
raw->link().attach(id);
|
|
450
|
+
|
|
451
|
+
conns_.emplace(id, std::move(conn));
|
|
452
|
+
conn_count_.fetch_add(1, std::memory_order_relaxed);
|
|
453
|
+
|
|
454
|
+
if (is_valid_socket(fd)) {
|
|
455
|
+
fd_to_conn_.emplace(fd, id);
|
|
456
|
+
// Inbound sockets are connected: watch for readable. Outbound sockets are
|
|
457
|
+
// still connecting: watch for writable, which signals connect completion.
|
|
458
|
+
poller_->add(fd, role == ConnRole::Inbound ? PollIn : PollOut);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Reap connections that never reach Established (stuck connect or handshake).
|
|
462
|
+
TimerId timer = timers_.schedule(kEstablishTimeout, [this, id] {
|
|
463
|
+
auto it = conns_.find(id);
|
|
464
|
+
if (it == conns_.end()) return;
|
|
465
|
+
const ConnState st = it->second->state();
|
|
466
|
+
if (st != ConnState::Established && st != ConnState::Closing && st != ConnState::Closed) {
|
|
467
|
+
mark_for_close(id, st == ConnState::Connecting ? CloseReason::ConnectFailed
|
|
468
|
+
: CloseReason::HandshakeFailed);
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
raw->set_establish_timer(timer);
|
|
472
|
+
return raw;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// ── Teardown ────────────────────────────────────────────────────────────────
|
|
476
|
+
|
|
477
|
+
void Reactor::mark_for_close(ConnId id, CloseReason reason) {
|
|
478
|
+
pending_close_.emplace(id, reason); // first reason wins
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
void Reactor::flush_dirty() {
|
|
482
|
+
if (dirty_.empty()) return;
|
|
483
|
+
// Swapped out first: a flush can close a connection, and a close can queue
|
|
484
|
+
// further work — none of which should extend the batch being walked.
|
|
485
|
+
std::vector<ConnId> batch;
|
|
486
|
+
batch.swap(dirty_);
|
|
487
|
+
for (const ConnId id : batch) {
|
|
488
|
+
Connection* conn = find(id);
|
|
489
|
+
if (!conn) continue; // gone since it booked
|
|
490
|
+
if (conn->state() != ConnState::Established) continue; // closing; nothing to owe
|
|
491
|
+
if (!conn->flush_pending()) mark_for_close(id, conn->close_reason());
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
void Reactor::process_pending_close() {
|
|
496
|
+
if (pending_close_.empty()) return;
|
|
497
|
+
auto batch = std::move(pending_close_);
|
|
498
|
+
pending_close_.clear();
|
|
499
|
+
for (const auto& [id, reason] : batch) remove(id, reason);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
void Reactor::remove(ConnId id, CloseReason reason) {
|
|
503
|
+
auto it = conns_.find(id);
|
|
504
|
+
if (it == conns_.end()) return;
|
|
505
|
+
|
|
506
|
+
std::unique_ptr<Connection> conn = std::move(it->second);
|
|
507
|
+
conns_.erase(it);
|
|
508
|
+
|
|
509
|
+
const socket_t fd = conn->socket();
|
|
510
|
+
if (is_valid_socket(fd)) {
|
|
511
|
+
fd_to_conn_.erase(fd);
|
|
512
|
+
poller_->remove(fd);
|
|
513
|
+
}
|
|
514
|
+
conn->cancel_establish_timer(); // stop the reaper before this fd can be reused
|
|
515
|
+
conn_count_.fetch_sub(1, std::memory_order_relaxed);
|
|
516
|
+
|
|
517
|
+
LOG_DEBUG("reactor", "Connection " << id << " closed (" << to_string(reason) << ")");
|
|
518
|
+
conn->shutdown_link(reason); // let the transport say goodbye / flush
|
|
519
|
+
delegate_.on_closed(*conn, reason);
|
|
520
|
+
if (is_valid_socket(fd)) close_socket(fd);
|
|
521
|
+
// ~Connection here releases the Link, which is what hands a UDP stream back to
|
|
522
|
+
// the mux (to linger briefly or go straight away).
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
void Reactor::shutdown_connections() {
|
|
526
|
+
for (auto& [id, conn] : conns_) {
|
|
527
|
+
const socket_t fd = conn->socket();
|
|
528
|
+
if (is_valid_socket(fd)) poller_->remove(fd);
|
|
529
|
+
conn->shutdown_link(CloseReason::ReactorShutdown);
|
|
530
|
+
delegate_.on_closed(*conn, CloseReason::ReactorShutdown);
|
|
531
|
+
if (is_valid_socket(fd)) close_socket(fd);
|
|
532
|
+
}
|
|
533
|
+
conns_.clear();
|
|
534
|
+
fd_to_conn_.clear();
|
|
535
|
+
pending_close_.clear();
|
|
536
|
+
cancelled_dials_.clear();
|
|
537
|
+
conn_count_.store(0, std::memory_order_relaxed);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
} // namespace librats
|