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,262 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file reactor.h
|
|
5
|
+
* @brief Single-threaded I/O reactor owning a shard of connections.
|
|
6
|
+
*
|
|
7
|
+
* A Reactor runs one thread that drives an IOPoller and owns every Connection
|
|
8
|
+
* assigned to it. ALL connection state lives behind this one thread, so the
|
|
9
|
+
* data path has zero locks. Other threads interact with it only by:
|
|
10
|
+
*
|
|
11
|
+
* - post(task) — run a closure on the reactor thread (wakes it);
|
|
12
|
+
* - execute(task) — same, but runs inline if already on the reactor thread;
|
|
13
|
+
* - connect()/close() — convenience wrappers that post the right task.
|
|
14
|
+
*
|
|
15
|
+
* The single synchronisation point is the MPSC task queue plus a WakeupPipe to
|
|
16
|
+
* interrupt the poll wait. Timers (handshake timeouts, backoff) ride on the same
|
|
17
|
+
* loop via a TimerQueue that also sizes each poll wait.
|
|
18
|
+
*
|
|
19
|
+
* ── Two transports, one loop ────────────────────────────────────────────────
|
|
20
|
+
* Connections are keyed by ConnId, not by socket, because only one of the two
|
|
21
|
+
* transports has a socket per connection:
|
|
22
|
+
*
|
|
23
|
+
* - TCP: one kernel socket per peer, registered with the poller. A poll event
|
|
24
|
+
* names a socket, which the fd → ConnId map turns into a connection.
|
|
25
|
+
* - UDP: every peer shares one socket, owned by the UdpMux. A poll event on it
|
|
26
|
+
* means "datagrams arrived"; the mux routes them to streams by connection id
|
|
27
|
+
* and hands back the same readable/writable/error events the poller would
|
|
28
|
+
* have produced. From dispatch_events() down, the two are indistinguishable.
|
|
29
|
+
*
|
|
30
|
+
* Both listeners are optional and independent: a reactor can accept TCP, accept
|
|
31
|
+
* UDP, both, or neither (dial-only). Only the reactor that owns the mux can dial
|
|
32
|
+
* over UDP, which is why the pool routes UDP dials to it (see reactor_pool.h).
|
|
33
|
+
*
|
|
34
|
+
* A ReactorPool (see reactor_pool.h) runs N of these and shards connections
|
|
35
|
+
* across them; with N == 1 this is a classic single-threaded reactor.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
#include "librats/core/types.h"
|
|
39
|
+
#include "librats/transport/connection.h"
|
|
40
|
+
#include "librats/transport/udp_mux.h"
|
|
41
|
+
#include "librats/core/mpsc_queue.h"
|
|
42
|
+
#include "librats/core/timer_queue.h"
|
|
43
|
+
#include "librats/core/io_poller.h"
|
|
44
|
+
#include "librats/core/notifier.h"
|
|
45
|
+
#include "librats/core/socket.h"
|
|
46
|
+
|
|
47
|
+
#include <atomic>
|
|
48
|
+
#include <chrono>
|
|
49
|
+
#include <functional>
|
|
50
|
+
#include <memory>
|
|
51
|
+
#include <string>
|
|
52
|
+
#include <thread>
|
|
53
|
+
#include <unordered_map>
|
|
54
|
+
#include <unordered_set>
|
|
55
|
+
#include <vector>
|
|
56
|
+
|
|
57
|
+
namespace librats {
|
|
58
|
+
|
|
59
|
+
class Reactor final : public UdpMuxDelegate {
|
|
60
|
+
public:
|
|
61
|
+
using Task = std::function<void()>;
|
|
62
|
+
|
|
63
|
+
/// @param index This reactor's slot in its pool (for sharding/logging).
|
|
64
|
+
/// @param delegate Sink for all connection lifecycle/frame events.
|
|
65
|
+
/// @param security Mints a Handshaker per connection (Noise / plaintext).
|
|
66
|
+
Reactor(uint8_t index, ConnectionDelegate& delegate, SecurityProvider& security);
|
|
67
|
+
~Reactor() override;
|
|
68
|
+
|
|
69
|
+
Reactor(const Reactor&) = delete;
|
|
70
|
+
Reactor& operator=(const Reactor&) = delete;
|
|
71
|
+
|
|
72
|
+
/// Adopt an already-bound, listening TCP socket. Call before start().
|
|
73
|
+
void listen(socket_t server_socket);
|
|
74
|
+
|
|
75
|
+
/// Adopt an already-bound UDP socket and run the datagram transport on it.
|
|
76
|
+
/// Call before start(). This is what makes the reactor able to accept *and*
|
|
77
|
+
/// dial over UDP; without it, UDP dials are refused.
|
|
78
|
+
void listen_udp(socket_t udp_socket, AddressFamily family);
|
|
79
|
+
|
|
80
|
+
/// Whether this reactor can carry UDP connections (i.e. it owns the mux).
|
|
81
|
+
bool has_udp() const noexcept { return mux_ != nullptr; }
|
|
82
|
+
|
|
83
|
+
void start(); ///< spawn the reactor thread
|
|
84
|
+
void stop(); ///< signal shutdown and join; closes all connections
|
|
85
|
+
|
|
86
|
+
// — cross-thread work submission —
|
|
87
|
+
void post(Task task); ///< enqueue + wake (any thread)
|
|
88
|
+
void execute(Task task); ///< inline if on-thread, else post()
|
|
89
|
+
bool on_reactor_thread() const noexcept;
|
|
90
|
+
|
|
91
|
+
/// Begin a non-blocking outbound connection over `kind` (thread-safe).
|
|
92
|
+
///
|
|
93
|
+
/// Returns the ConnId the connection *will* have, reserved synchronously so a
|
|
94
|
+
/// caller can cancel the attempt with cancel_dial() before it has even started
|
|
95
|
+
/// — which is what lets the node race a UDP dial against a TCP one and drop the
|
|
96
|
+
/// loser cleanly. kInvalidConnId if the transport is unavailable here.
|
|
97
|
+
///
|
|
98
|
+
/// `profile` shapes how hard a datagram dial retries its Syn (see DialProfile);
|
|
99
|
+
/// it has no meaning for TCP, where the kernel owns the connect retries.
|
|
100
|
+
ConnId connect(std::string host, int port, TransportKind kind = TransportKind::Tcp,
|
|
101
|
+
DialProfile profile = {});
|
|
102
|
+
|
|
103
|
+
/// Close a connection by id (thread-safe; deferred to the reactor thread).
|
|
104
|
+
/// Valid for an id whose dial has not started yet — it is cancelled instead.
|
|
105
|
+
void close(ConnId id, CloseReason reason);
|
|
106
|
+
|
|
107
|
+
// — links this reactor did not open itself ──────────────────────────────
|
|
108
|
+
//
|
|
109
|
+
// A relayed circuit is a byte stream that comes out of another peer's
|
|
110
|
+
// connection rather than out of a socket (see transport/relay_link.h), so the
|
|
111
|
+
// module that speaks the relay protocol builds the Link and hands it over.
|
|
112
|
+
// These three are that hand-over, and they are deliberately the whole of it:
|
|
113
|
+
// the reactor learns nothing about relaying, and the module learns nothing
|
|
114
|
+
// about reactors beyond "connections need to be woken".
|
|
115
|
+
//
|
|
116
|
+
// The circuit MUST be given to the reactor that owns its carrier's connection.
|
|
117
|
+
// That is what keeps every byte of it on one thread, exactly like the streams
|
|
118
|
+
// the mux hands over above, and it is the caller's job to arrange (see
|
|
119
|
+
// node/circuit_service.h).
|
|
120
|
+
|
|
121
|
+
/// Reserve a ConnId without starting anything. Thread-safe, and synchronous for
|
|
122
|
+
/// the same reason connect() is: the caller has to be able to name the
|
|
123
|
+
/// connection — to wake it, to close it — from the moment it asks for one,
|
|
124
|
+
/// rather than after a task it posted has run.
|
|
125
|
+
ConnId reserve_conn_id() noexcept {
|
|
126
|
+
return next_conn_id_.fetch_add(1, std::memory_order_relaxed);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/// Adopt an already-built Link under the id `reserve_conn_id()` handed out.
|
|
130
|
+
/// Thread-safe; the adoption itself runs on the reactor thread (inline when the
|
|
131
|
+
/// caller is already on it).
|
|
132
|
+
/// @param connected whether the far end is already there. Such a link starts its
|
|
133
|
+
/// handshake at once, like an accepted socket; one that is not waits for
|
|
134
|
+
/// a PollOut from wake(), exactly as an outbound connect does.
|
|
135
|
+
void adopt_link(ConnId id, std::unique_ptr<Link> link, ConnRole role, bool connected);
|
|
136
|
+
|
|
137
|
+
/// Deliver poll-equivalent events (PollIn/PollOut/PollErr) to a connection whose
|
|
138
|
+
/// Link has no socket for the poller to watch. Thread-safe. A no-op for an id
|
|
139
|
+
/// that is gone, so a late wake for a torn-down circuit costs nothing.
|
|
140
|
+
void wake(ConnId id, uint32_t events);
|
|
141
|
+
|
|
142
|
+
/// Drop a dial that lost its race — but only while it is still a dial. Once an
|
|
143
|
+
/// attempt has established it is not an attempt any more, it is one of possibly
|
|
144
|
+
/// several links to a peer, and which of those survives is the peer table's
|
|
145
|
+
/// call, made identically at both ends. The check happens here because this is
|
|
146
|
+
/// the one thread that can read the connection's state without racing it.
|
|
147
|
+
void cancel_dial(ConnId id, CloseReason reason);
|
|
148
|
+
|
|
149
|
+
/// Send a frame to every Established connection on this reactor. Thread-safe:
|
|
150
|
+
/// the iteration runs on the reactor thread. The payload is shared across
|
|
151
|
+
/// connections (the per-peer encryption still happens in each Connection).
|
|
152
|
+
void broadcast(FrameHeader header, std::shared_ptr<const Bytes> payload);
|
|
153
|
+
|
|
154
|
+
// — timers (reactor thread, or via post/execute) —
|
|
155
|
+
TimerId schedule(std::chrono::milliseconds delay, Task on_fire);
|
|
156
|
+
void cancel(TimerId id);
|
|
157
|
+
|
|
158
|
+
/// Look up an owned connection. Reactor thread only.
|
|
159
|
+
Connection* find(ConnId id) noexcept;
|
|
160
|
+
|
|
161
|
+
/// Adjust poll interest for a socket. Called by TcpLink; reactor thread.
|
|
162
|
+
void set_interest(socket_t sock, uint32_t events);
|
|
163
|
+
|
|
164
|
+
/// Book a flush for the end of this turn: `id` has frames queued that have
|
|
165
|
+
/// not been written yet. Called by Connection::send, which aggregates rather
|
|
166
|
+
/// than writing through — see the note there. Reactor thread; each connection
|
|
167
|
+
/// books at most one flush per turn.
|
|
168
|
+
void mark_dirty(ConnId id) { dirty_.push_back(id); }
|
|
169
|
+
|
|
170
|
+
/// The security provider used to mint per-connection handshakers.
|
|
171
|
+
SecurityProvider& security() noexcept { return security_; }
|
|
172
|
+
|
|
173
|
+
/// Approximate live connection count (lock-free, eventually consistent).
|
|
174
|
+
size_t connection_count() const noexcept {
|
|
175
|
+
return conn_count_.load(std::memory_order_relaxed);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
uint8_t index() const noexcept { return index_; }
|
|
179
|
+
|
|
180
|
+
private:
|
|
181
|
+
// — UdpMuxDelegate —
|
|
182
|
+
ConnId adopt_inbound_link(std::unique_ptr<Link> link) override;
|
|
183
|
+
void dispatch_link_events(ConnId id, uint32_t events) override;
|
|
184
|
+
|
|
185
|
+
void run();
|
|
186
|
+
void drain_tasks(std::vector<Task>& scratch);
|
|
187
|
+
void drain_wakeup();
|
|
188
|
+
void handle_event(const PollResult& ev);
|
|
189
|
+
void dispatch_events(ConnId id, uint32_t events);
|
|
190
|
+
void do_accept();
|
|
191
|
+
void schedule_maintenance();
|
|
192
|
+
void start_dial(ConnId id, const std::string& host, int port, TransportKind kind,
|
|
193
|
+
DialProfile profile);
|
|
194
|
+
void abort_dial(ConnId id, const std::string& host, int port);
|
|
195
|
+
Connection* adopt(std::unique_ptr<Link> link, ConnRole role, ConnId id);
|
|
196
|
+
bool resolve_dial(ConnId id);
|
|
197
|
+
/// Body of close()/cancel_dial(); `spare_established` makes it a no-op for a
|
|
198
|
+
/// connection that has already finished its handshake.
|
|
199
|
+
void close_conn(ConnId id, CloseReason reason, bool spare_established);
|
|
200
|
+
void mark_for_close(ConnId id, CloseReason reason);
|
|
201
|
+
void process_pending_close();
|
|
202
|
+
void flush_dirty(); ///< write out what send() queued this turn
|
|
203
|
+
void remove(ConnId id, CloseReason reason);
|
|
204
|
+
void shutdown_connections();
|
|
205
|
+
|
|
206
|
+
static constexpr int kMaxEvents = 256;
|
|
207
|
+
// Idle poll cap. Kept short as a stopgap for an IOCP quirk: connecting
|
|
208
|
+
// sockets live in WSAPoll-fallback mode and are only re-checked once per
|
|
209
|
+
// wait() iteration, so connect-completion latency is bounded by this value.
|
|
210
|
+
// Negligible idle cost; on epoll/kqueue the loop still wakes on real events,
|
|
211
|
+
// so this only caps idle latency.
|
|
212
|
+
static constexpr int kMaxPollMs = 50;
|
|
213
|
+
/// Passes flush_dirty() makes over the connections booked for a write. More than
|
|
214
|
+
/// one because a write can book another connection's write — see flush_dirty().
|
|
215
|
+
static constexpr int kMaxFlushPasses = 4;
|
|
216
|
+
/// Deadline from adopt() to reaching Established (covers connect + handshake).
|
|
217
|
+
static constexpr std::chrono::milliseconds kEstablishTimeout{15000};
|
|
218
|
+
/// Cadence of the housekeeping sweep over this reactor's connections (currently
|
|
219
|
+
/// only Connection::on_maintenance_tick, which ages idle receive buffers). One
|
|
220
|
+
/// timer per reactor rather than one per connection: the work is a few pointer
|
|
221
|
+
/// comparisons per peer, so a sweep is cheaper than N timer entries.
|
|
222
|
+
static constexpr std::chrono::milliseconds kMaintenanceInterval{10000};
|
|
223
|
+
|
|
224
|
+
uint8_t index_;
|
|
225
|
+
ConnectionDelegate& delegate_;
|
|
226
|
+
SecurityProvider& security_;
|
|
227
|
+
std::unique_ptr<IOPoller> poller_;
|
|
228
|
+
Notifier wakeup_;
|
|
229
|
+
MpscQueue<Task> tasks_;
|
|
230
|
+
TimerQueue timers_;
|
|
231
|
+
|
|
232
|
+
// Declared before conns_ so it is destroyed *after* them: every UDP
|
|
233
|
+
// connection's Link points into the mux and tells it, on destruction, that its
|
|
234
|
+
// stream may go. Reversing these two would have that run against freed memory.
|
|
235
|
+
std::unique_ptr<UdpMux> mux_;
|
|
236
|
+
|
|
237
|
+
// Connections are keyed by their stable ConnId, since a UDP connection has no
|
|
238
|
+
// socket of its own; fd_to_conn_ maps the sockets that do exist (TCP, and the
|
|
239
|
+
// listener) back to that key for poll dispatch.
|
|
240
|
+
std::unordered_map<ConnId, std::unique_ptr<Connection>> conns_;
|
|
241
|
+
std::unordered_map<socket_t, ConnId> fd_to_conn_;
|
|
242
|
+
std::unordered_map<ConnId, CloseReason> pending_close_;
|
|
243
|
+
/// Connections with frames queued but not yet written this turn. Holds ids,
|
|
244
|
+
/// not pointers, so a connection torn down in between is simply not found.
|
|
245
|
+
std::vector<ConnId> dirty_;
|
|
246
|
+
/// Dials cancelled before their connect task ran (see connect()/close()).
|
|
247
|
+
std::unordered_set<ConnId> cancelled_dials_;
|
|
248
|
+
/// Highest ConnId that has already been adopted or abandoned; anything at or
|
|
249
|
+
/// below it has resolved, so a late close() for it needs no cancellation slot.
|
|
250
|
+
ConnId resolved_watermark_ = 0;
|
|
251
|
+
|
|
252
|
+
socket_t server_socket_ = RATS_INVALID_SOCKET;
|
|
253
|
+
std::atomic<ConnId> next_conn_id_{1};
|
|
254
|
+
std::atomic<size_t> conn_count_{0};
|
|
255
|
+
std::atomic<bool> running_{false};
|
|
256
|
+
std::thread thread_;
|
|
257
|
+
// Published with release by the reactor thread in run(), read with acquire by
|
|
258
|
+
// any thread in on_reactor_thread(); plain access would be a data race.
|
|
259
|
+
std::atomic<std::thread::id> thread_id_{};
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
} // namespace librats
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file reactor_pool.h
|
|
5
|
+
* @brief A fixed set of reactors; connections are sharded across them.
|
|
6
|
+
*
|
|
7
|
+
* Default size 1 — a single reactor, which is plenty for thousands of peers.
|
|
8
|
+
* Larger pools shard outbound connections round-robin; because each Connection
|
|
9
|
+
* is pinned to one reactor for life, nothing on the data path needs to change as
|
|
10
|
+
* the pool grows. (Sharding *inbound* connections across reactors — handing an
|
|
11
|
+
* accepted fd to a non-acceptor reactor — is a future enhancement; for now a
|
|
12
|
+
* single reactor accepts and owns inbound connections.)
|
|
13
|
+
*
|
|
14
|
+
* UDP is a deliberate exception to the round-robin. Every UDP connection shares
|
|
15
|
+
* one socket — that is the point of it — so the mux, and therefore every stream
|
|
16
|
+
* on it, belongs to the single reactor that owns that socket. Sharding those
|
|
17
|
+
* across threads would mean handing datagrams between reactors on every packet,
|
|
18
|
+
* which costs more than the parallelism buys. pick() honours that by routing UDP
|
|
19
|
+
* dials to the mux's reactor and everything else round-robin.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
#include "librats/transport/reactor.h"
|
|
23
|
+
|
|
24
|
+
#include <atomic>
|
|
25
|
+
#include <cstdint>
|
|
26
|
+
#include <memory>
|
|
27
|
+
#include <vector>
|
|
28
|
+
|
|
29
|
+
namespace librats {
|
|
30
|
+
|
|
31
|
+
class ReactorPool {
|
|
32
|
+
public:
|
|
33
|
+
ReactorPool(size_t count, ConnectionDelegate& delegate, SecurityProvider& security) {
|
|
34
|
+
if (count == 0) count = 1;
|
|
35
|
+
reactors_.reserve(count);
|
|
36
|
+
for (size_t i = 0; i < count; ++i)
|
|
37
|
+
reactors_.push_back(std::make_unique<Reactor>(static_cast<uint8_t>(i), delegate, security));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/// Hand the listening socket to the acceptor reactor (index 0). Before start().
|
|
41
|
+
void listen(socket_t server_socket) { reactors_[0]->listen(server_socket); }
|
|
42
|
+
|
|
43
|
+
/// Hand the shared UDP socket to the acceptor reactor (index 0), which then
|
|
44
|
+
/// owns every datagram connection. Before start().
|
|
45
|
+
void listen_udp(socket_t udp_socket, AddressFamily family) {
|
|
46
|
+
reactors_[0]->listen_udp(udp_socket, family);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/// Whether the pool can carry UDP connections at all.
|
|
50
|
+
bool has_udp() const noexcept { return reactors_[0]->has_udp(); }
|
|
51
|
+
|
|
52
|
+
void start() { for (auto& r : reactors_) r->start(); }
|
|
53
|
+
void stop() { for (auto& r : reactors_) r->stop(); }
|
|
54
|
+
|
|
55
|
+
size_t size() const noexcept { return reactors_.size(); }
|
|
56
|
+
Reactor& by_index(uint8_t i) noexcept { return *reactors_[i]; }
|
|
57
|
+
|
|
58
|
+
/// Choose a reactor for a new outbound connection over `kind`. UDP always
|
|
59
|
+
/// lands on the reactor that owns the socket it must go out on; anything else
|
|
60
|
+
/// is spread round-robin.
|
|
61
|
+
Reactor& pick(TransportKind kind = TransportKind::Tcp) noexcept {
|
|
62
|
+
if (kind == TransportKind::Udp) return *reactors_[0];
|
|
63
|
+
const size_t i = next_.fetch_add(1, std::memory_order_relaxed) % reactors_.size();
|
|
64
|
+
return *reactors_[i];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
template <typename F>
|
|
68
|
+
void for_each(F&& fn) { for (auto& r : reactors_) fn(*r); }
|
|
69
|
+
|
|
70
|
+
size_t connection_count() const noexcept {
|
|
71
|
+
size_t total = 0;
|
|
72
|
+
for (const auto& r : reactors_) total += r->connection_count();
|
|
73
|
+
return total;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private:
|
|
77
|
+
std::vector<std::unique_ptr<Reactor>> reactors_;
|
|
78
|
+
std::atomic<size_t> next_{0};
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
} // namespace librats
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
#include "librats/transport/relay_link.h"
|
|
2
|
+
|
|
3
|
+
#include "librats/core/io_poller.h" // PollIn / PollOut / PollErr
|
|
4
|
+
#include "librats/util/logger.h"
|
|
5
|
+
|
|
6
|
+
#include <algorithm>
|
|
7
|
+
#include <cstring>
|
|
8
|
+
#include <utility>
|
|
9
|
+
|
|
10
|
+
namespace librats {
|
|
11
|
+
|
|
12
|
+
namespace {
|
|
13
|
+
|
|
14
|
+
/// Slices one data message gathers from the caller's list. The send buffer hands
|
|
15
|
+
/// out a length prefix and a body per queued frame, so this covers a run of frames
|
|
16
|
+
/// rather than a single one; a longer backlog than this simply comes back on the
|
|
17
|
+
/// next turn of Connection::flush.
|
|
18
|
+
constexpr size_t kMaxParts = 16;
|
|
19
|
+
|
|
20
|
+
} // namespace
|
|
21
|
+
|
|
22
|
+
Circuit::Circuit(uint32_t id, std::shared_ptr<CircuitCarrier> carrier, bool open,
|
|
23
|
+
uint32_t recv_window, uint32_t peer_window)
|
|
24
|
+
: id_(id),
|
|
25
|
+
carrier_(std::move(carrier)),
|
|
26
|
+
state_(open ? State::Open : State::Pending),
|
|
27
|
+
recv_window_(recv_window == 0 ? kDefaultWindow : recv_window),
|
|
28
|
+
send_credit_(open ? peer_window : 0) {}
|
|
29
|
+
|
|
30
|
+
std::shared_ptr<Circuit> Circuit::opening(uint32_t id, std::shared_ptr<CircuitCarrier> carrier,
|
|
31
|
+
uint32_t recv_window) {
|
|
32
|
+
return std::shared_ptr<Circuit>(
|
|
33
|
+
new Circuit(id, std::move(carrier), /*open=*/false, recv_window, /*peer_window=*/0));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
std::shared_ptr<Circuit> Circuit::accepted(uint32_t id, std::shared_ptr<CircuitCarrier> carrier,
|
|
37
|
+
uint32_t peer_window, uint32_t recv_window) {
|
|
38
|
+
return std::shared_ptr<Circuit>(
|
|
39
|
+
new Circuit(id, std::move(carrier), /*open=*/true, recv_window, peer_window));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ── Fed by the relay module ─────────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
uint32_t Circuit::on_data(ByteView bytes) {
|
|
45
|
+
if (state_ == State::Closed || bytes.empty()) return 0;
|
|
46
|
+
|
|
47
|
+
// The window is the ONE thing bounding what this circuit can cost us and the
|
|
48
|
+
// relay carrying it, so a peer that sends past what it was granted is not
|
|
49
|
+
// merely impolite — it is doing the thing the window exists to prevent. Fail
|
|
50
|
+
// the circuit rather than accept the bytes.
|
|
51
|
+
if (bytes.size() > recv_window_ - recv_in_flight_) {
|
|
52
|
+
LOG_WARN("relay", "Circuit " << id_ << " overran its receive window ("
|
|
53
|
+
<< bytes.size() << " B with " << (recv_window_ - recv_in_flight_)
|
|
54
|
+
<< " B granted); failing it");
|
|
55
|
+
return on_closed(CloseReason::ProtocolError, /*orderly=*/false);
|
|
56
|
+
}
|
|
57
|
+
recv_in_flight_ += static_cast<uint32_t>(bytes.size());
|
|
58
|
+
|
|
59
|
+
// Hand back the consumed prefix before growing: the connection normally drains
|
|
60
|
+
// the inbox to empty on every readable event, so this is nearly always a reset
|
|
61
|
+
// of an already-empty buffer rather than a move.
|
|
62
|
+
if (inbox_read_ == inbox_.size()) {
|
|
63
|
+
inbox_.clear();
|
|
64
|
+
inbox_read_ = 0;
|
|
65
|
+
} else if (inbox_read_ >= kCompactThreshold) {
|
|
66
|
+
inbox_.erase(inbox_.begin(), inbox_.begin() + static_cast<std::ptrdiff_t>(inbox_read_));
|
|
67
|
+
inbox_read_ = 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
inbox_.insert(inbox_.end(), bytes.begin(), bytes.end());
|
|
71
|
+
return PollIn;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
uint32_t Circuit::on_accept(uint32_t peer_window) {
|
|
75
|
+
if (state_ != State::Pending) return 0;
|
|
76
|
+
state_ = State::Open;
|
|
77
|
+
send_credit_ = peer_window;
|
|
78
|
+
// Unconditional, unlike the other openings below: the Connection is still in
|
|
79
|
+
// Connecting and has nothing queued, so want_write_ is false — yet this is
|
|
80
|
+
// precisely the event it is waiting for to finish connecting and start its
|
|
81
|
+
// handshake (see Connection::on_writable).
|
|
82
|
+
return PollOut;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
uint32_t Circuit::on_credit(uint32_t bytes) {
|
|
86
|
+
if (state_ == State::Closed || bytes == 0) return 0;
|
|
87
|
+
// Saturate rather than wrap: a far end that grants nonsense should cost us
|
|
88
|
+
// nothing worse than an over-generous window, and the receiver's own window
|
|
89
|
+
// check is what actually bounds the traffic.
|
|
90
|
+
if (bytes > UINT32_MAX - send_credit_) send_credit_ = UINT32_MAX;
|
|
91
|
+
else send_credit_ += bytes;
|
|
92
|
+
|
|
93
|
+
if (!credit_blocked_) return 0;
|
|
94
|
+
credit_blocked_ = false;
|
|
95
|
+
return writable_event();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
uint32_t Circuit::on_closed(CloseReason reason, bool orderly) {
|
|
99
|
+
if (state_ == State::Closed) return 0;
|
|
100
|
+
state_ = State::Closed;
|
|
101
|
+
reason_ = reason;
|
|
102
|
+
orderly_ = orderly;
|
|
103
|
+
// An orderly close still owes the application whatever the far end sent before
|
|
104
|
+
// it: read() drains the inbox first and only then reports the end of stream, so
|
|
105
|
+
// what this needs is a readable event, not an error. That is the same contract
|
|
106
|
+
// the other links keep (see Link::read).
|
|
107
|
+
return orderly ? PollIn : PollErr;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
uint32_t Circuit::on_carrier_writable() {
|
|
111
|
+
if (!carrier_blocked_) return 0;
|
|
112
|
+
carrier_blocked_ = false;
|
|
113
|
+
return writable_event();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
uint32_t Circuit::writable_event() const noexcept {
|
|
117
|
+
if (blocked() || !want_write_ || state_ != State::Open) return 0;
|
|
118
|
+
return PollOut;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ── Driven by the Connection ────────────────────────────────────────────────
|
|
122
|
+
|
|
123
|
+
Link::IoResult Circuit::read(ByteSpan into) {
|
|
124
|
+
const size_t available = inbox_.size() - inbox_read_;
|
|
125
|
+
const size_t n = (std::min)(available, into.size());
|
|
126
|
+
if (n > 0) {
|
|
127
|
+
std::memcpy(into.data(), inbox_.data() + inbox_read_, n);
|
|
128
|
+
inbox_read_ += n;
|
|
129
|
+
if (inbox_read_ == inbox_.size()) {
|
|
130
|
+
inbox_.clear();
|
|
131
|
+
inbox_read_ = 0;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// These bytes are out of the pipe, so the far end may put that much back
|
|
135
|
+
// in. Granted in batches: one small message per half-window rather than one
|
|
136
|
+
// per chunk, which is what keeps the credit scheme's overhead invisible.
|
|
137
|
+
recv_in_flight_ -= static_cast<uint32_t>(n);
|
|
138
|
+
recv_ungranted_ += static_cast<uint32_t>(n);
|
|
139
|
+
if (state_ != State::Closed && recv_ungranted_ >= recv_window_ / kCreditGrantFraction) {
|
|
140
|
+
carrier_->circuit_send_credit(id_, recv_ungranted_);
|
|
141
|
+
recv_ungranted_ = 0;
|
|
142
|
+
}
|
|
143
|
+
return {n, Link::Status::Ok};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Drained. Only now may the end of the stream be reported — data and
|
|
147
|
+
// end-of-stream are never delivered together.
|
|
148
|
+
if (state_ == State::Closed)
|
|
149
|
+
return {0, orderly_ ? Link::Status::Closed : Link::Status::Error};
|
|
150
|
+
return {0, Link::Status::WouldBlock};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
Link::IoResult Circuit::write(const ByteView* slices, size_t count) {
|
|
154
|
+
if (state_ == State::Closed) return {0, Link::Status::Error};
|
|
155
|
+
// Pending: opened but not yet accepted. Nothing may go out — and nothing tries
|
|
156
|
+
// to, since the Connection is still Connecting.
|
|
157
|
+
if (state_ != State::Open) return {0, Link::Status::WouldBlock};
|
|
158
|
+
if (blocked()) return {0, Link::Status::WouldBlock};
|
|
159
|
+
|
|
160
|
+
const size_t budget = (std::min)(static_cast<size_t>(send_credit_), kMaxDataChunk);
|
|
161
|
+
if (budget == 0) {
|
|
162
|
+
credit_blocked_ = true;
|
|
163
|
+
return {0, Link::Status::WouldBlock};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// One data message per call, gathered across as many of the caller's slices as
|
|
167
|
+
// fit. Connection::flush loops while a link keeps reporting Ok, so a backlog
|
|
168
|
+
// larger than one chunk simply comes back around — no loop is needed here, and
|
|
169
|
+
// each turn of that loop re-checks the credit and the carrier.
|
|
170
|
+
ByteView parts[kMaxParts];
|
|
171
|
+
size_t nparts = 0;
|
|
172
|
+
size_t taken = 0;
|
|
173
|
+
for (size_t i = 0; i < count && taken < budget && nparts < kMaxParts; ++i) {
|
|
174
|
+
if (slices[i].empty()) continue;
|
|
175
|
+
const size_t take = (std::min)(slices[i].size(), budget - taken);
|
|
176
|
+
parts[nparts++] = ByteView(slices[i].data(), take);
|
|
177
|
+
taken += take;
|
|
178
|
+
}
|
|
179
|
+
if (taken == 0) return {0, Link::Status::WouldBlock};
|
|
180
|
+
|
|
181
|
+
send_credit_ -= static_cast<uint32_t>(taken);
|
|
182
|
+
// The carrier queues what it is given either way; false is "stop and wait", so
|
|
183
|
+
// the bytes are ours to report as written and the block applies to the NEXT
|
|
184
|
+
// call (see CircuitCarrier::circuit_send_data).
|
|
185
|
+
if (!carrier_->circuit_send_data(id_, parts, nparts)) carrier_blocked_ = true;
|
|
186
|
+
return {taken, Link::Status::Ok};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
void Circuit::shutdown(CloseReason reason) {
|
|
190
|
+
if (!close_sent_) {
|
|
191
|
+
close_sent_ = true;
|
|
192
|
+
carrier_->circuit_send_close(id_, reason);
|
|
193
|
+
}
|
|
194
|
+
if (state_ != State::Closed) {
|
|
195
|
+
state_ = State::Closed;
|
|
196
|
+
reason_ = reason;
|
|
197
|
+
orderly_ = false;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
void Circuit::release() {
|
|
202
|
+
if (released_) return;
|
|
203
|
+
released_ = true;
|
|
204
|
+
state_ = State::Closed;
|
|
205
|
+
carrier_->circuit_released(id_);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
} // namespace librats
|