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,502 @@
|
|
|
1
|
+
#include "librats/bittorrent/peer_connection.h"
|
|
2
|
+
#include "librats/bittorrent/byte_io.h"
|
|
3
|
+
#include "librats/bittorrent/log.h"
|
|
4
|
+
|
|
5
|
+
#include <algorithm>
|
|
6
|
+
#include <cerrno>
|
|
7
|
+
#include <cstring>
|
|
8
|
+
|
|
9
|
+
namespace librats::bittorrent {
|
|
10
|
+
|
|
11
|
+
namespace {
|
|
12
|
+
|
|
13
|
+
#ifdef _WIN32
|
|
14
|
+
inline bool would_block() { return WSAGetLastError() == WSAEWOULDBLOCK; }
|
|
15
|
+
#else
|
|
16
|
+
inline bool would_block() { return errno == EAGAIN || errno == EWOULDBLOCK; }
|
|
17
|
+
#endif
|
|
18
|
+
|
|
19
|
+
/// Largest message we will accept. A bitfield for ~16M pieces fits; a piece
|
|
20
|
+
/// message is ~16 KiB. Anything larger is treated as a protocol violation.
|
|
21
|
+
constexpr std::uint32_t kMaxMessageLen = 2 * 1024 * 1024;
|
|
22
|
+
|
|
23
|
+
constexpr std::size_t kRecvChunk = 64 * 1024;
|
|
24
|
+
|
|
25
|
+
/// Ceiling on how far rx_ may be grown on the strength of a *declared* message length
|
|
26
|
+
/// alone. Sizing the buffer for the whole message up front (as libtorrent does, growing
|
|
27
|
+
/// straight to packet_size) turns a large message into one allocation instead of a
|
|
28
|
+
/// 1.5x-at-a-time climb. But the length is the peer's word, so past this we fall back
|
|
29
|
+
/// to geometric growth and the allocation only ever tracks bytes that really arrived.
|
|
30
|
+
/// Comfortably above anything real (a piece is 16 KiB + 9; the largest honest message
|
|
31
|
+
/// is a bitfield, ~1 byte per 8 pieces).
|
|
32
|
+
constexpr std::size_t kMaxEagerReserve = 1024 * 1024;
|
|
33
|
+
|
|
34
|
+
/// Send-buffer high-water mark. If a peer stops draining its socket and our
|
|
35
|
+
/// unsent backlog blows past this, the peer is a slow consumer — drop it rather
|
|
36
|
+
/// than buffer without bound. Matches librats' own kDefaultSendHighWater (8 MiB);
|
|
37
|
+
/// far above any single legitimate message (a piece is <=32 KiB, a bitfield for a
|
|
38
|
+
/// realistic torrent well under a MiB).
|
|
39
|
+
constexpr std::size_t kSendHighWater = 8 * 1024 * 1024;
|
|
40
|
+
|
|
41
|
+
// ---- connection timeouts (enforced by tick()) ----
|
|
42
|
+
/// Drop an incoming peer that hasn't completed the handshake in this long — stops
|
|
43
|
+
/// idle half-open sockets from accumulating (a cheap DoS).
|
|
44
|
+
constexpr auto kHandshakeTimeout = std::chrono::seconds(30);
|
|
45
|
+
/// Drop a connected peer that has sent us nothing for this long.
|
|
46
|
+
constexpr auto kIdleTimeout = std::chrono::seconds(120);
|
|
47
|
+
/// Send a keep-alive if we haven't sent anything for this long (kept below a
|
|
48
|
+
/// typical peer's ~120 s idle timeout so we don't get dropped).
|
|
49
|
+
constexpr auto kKeepAliveInterval = std::chrono::seconds(100);
|
|
50
|
+
/// How often tick() runs. Coarse: it only checks the deadlines above.
|
|
51
|
+
constexpr auto kTickInterval = std::chrono::seconds(10);
|
|
52
|
+
|
|
53
|
+
} // namespace
|
|
54
|
+
|
|
55
|
+
PeerConnection::PeerConnection(Reactor& reactor, socket_t sock, bool outgoing,
|
|
56
|
+
const InfoHash& info_hash, const PeerId& our_peer_id,
|
|
57
|
+
std::uint32_t num_pieces, Observer* observer,
|
|
58
|
+
std::string remote_ip, std::uint16_t remote_port)
|
|
59
|
+
: reactor_(reactor)
|
|
60
|
+
, sock_(sock)
|
|
61
|
+
, outgoing_(outgoing)
|
|
62
|
+
, info_hash_(info_hash)
|
|
63
|
+
, our_peer_id_(our_peer_id)
|
|
64
|
+
, num_pieces_(num_pieces)
|
|
65
|
+
, obs_(observer)
|
|
66
|
+
, bound_(true)
|
|
67
|
+
, remote_ip_(std::move(remote_ip))
|
|
68
|
+
, remote_port_(remote_port)
|
|
69
|
+
, peer_have_(num_pieces, false) {}
|
|
70
|
+
|
|
71
|
+
PeerConnection::PeerConnection(Reactor& reactor, socket_t sock, const PeerId& our_peer_id,
|
|
72
|
+
Resolver resolver, std::string remote_ip, std::uint16_t remote_port)
|
|
73
|
+
: reactor_(reactor)
|
|
74
|
+
, sock_(sock)
|
|
75
|
+
, outgoing_(false)
|
|
76
|
+
, info_hash_{}
|
|
77
|
+
, our_peer_id_(our_peer_id)
|
|
78
|
+
, num_pieces_(0)
|
|
79
|
+
, obs_(nullptr)
|
|
80
|
+
, resolver_(std::move(resolver))
|
|
81
|
+
, bound_(false)
|
|
82
|
+
, remote_ip_(std::move(remote_ip))
|
|
83
|
+
, remote_port_(remote_port) {}
|
|
84
|
+
|
|
85
|
+
PeerConnection::~PeerConnection() {
|
|
86
|
+
// Cancel the tick before we die so its captured `this` can never fire on freed
|
|
87
|
+
// memory. Same reactor thread owns both the timer and this destructor.
|
|
88
|
+
if (tick_timer_ != kInvalidTimerId) { reactor_.cancel(tick_timer_); tick_timer_ = kInvalidTimerId; }
|
|
89
|
+
if (!closed_ && is_valid_socket(sock_)) {
|
|
90
|
+
reactor_.remove(sock_);
|
|
91
|
+
close_socket(sock_);
|
|
92
|
+
sock_ = RATS_INVALID_SOCKET;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
void PeerConnection::start() {
|
|
97
|
+
if (started_) return;
|
|
98
|
+
started_ = true;
|
|
99
|
+
set_socket_nonblocking(sock_);
|
|
100
|
+
reactor_.add(sock_, PollIn, [this](std::uint32_t ev) { on_io(ev); });
|
|
101
|
+
|
|
102
|
+
const auto now = std::chrono::steady_clock::now();
|
|
103
|
+
created_ = last_recv_ = last_sent_ = now;
|
|
104
|
+
tick_timer_ = reactor_.schedule(kTickInterval, [this] { tick(); });
|
|
105
|
+
|
|
106
|
+
// An outgoing peer sends its handshake immediately; an incoming one waits to
|
|
107
|
+
// learn the info-hash, then replies (see parse_handshake()).
|
|
108
|
+
if (outgoing_) {
|
|
109
|
+
LOG_DEBUG("bt.peer", remote_ip_ << ':' << remote_port_ << " → handshake sent ("
|
|
110
|
+
<< short_hash(info_hash_) << ')');
|
|
111
|
+
send_handshake();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
void PeerConnection::send_handshake() {
|
|
116
|
+
std::uint8_t hs[kHandshakeSize];
|
|
117
|
+
hs[0] = std::uint8_t(kProtocolStringLen);
|
|
118
|
+
std::memcpy(hs + 1, kProtocolString, kProtocolStringLen);
|
|
119
|
+
ReservedBytes reserved{};
|
|
120
|
+
reserved::enable_dht(reserved);
|
|
121
|
+
// NOTE: we deliberately do NOT advertise the Fast Extension (BEP 6). We do not
|
|
122
|
+
// implement its messages (Have All / Have None / Suggest / Reject / Allowed
|
|
123
|
+
// Fast), and a peer that sees the Fast bit and also supports it would replace
|
|
124
|
+
// its initial `bitfield` with a `Have All` / `Have None` we'd silently ignore —
|
|
125
|
+
// making fast-capable seeds (i.e. most of the modern swarm) look like they hold
|
|
126
|
+
// nothing, so we'd never download from them. Re-enable only once BEP 6 is
|
|
127
|
+
// actually implemented in dispatch().
|
|
128
|
+
reserved::enable_extensions(reserved);
|
|
129
|
+
std::memcpy(hs + 20, reserved.data(), 8);
|
|
130
|
+
std::memcpy(hs + 28, info_hash_.data(), 20);
|
|
131
|
+
std::memcpy(hs + 48, our_peer_id_.data(), 20);
|
|
132
|
+
handshake_sent_ = true;
|
|
133
|
+
queue(ByteView(hs, kHandshakeSize));
|
|
134
|
+
flush();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
void PeerConnection::close(const std::string& reason) {
|
|
138
|
+
if (closed_) return;
|
|
139
|
+
closed_ = true;
|
|
140
|
+
// Single choke point for every teardown path (protocol error, timeout, slow
|
|
141
|
+
// consumer, remote close, torrent stop): one greppable line per disconnect.
|
|
142
|
+
LOG_DEBUG("bt.peer", remote_ip_ << ':' << remote_port_ << " disconnect: " << reason);
|
|
143
|
+
if (tick_timer_ != kInvalidTimerId) { reactor_.cancel(tick_timer_); tick_timer_ = kInvalidTimerId; }
|
|
144
|
+
if (is_valid_socket(sock_)) {
|
|
145
|
+
reactor_.remove(sock_);
|
|
146
|
+
close_socket(sock_);
|
|
147
|
+
sock_ = RATS_INVALID_SOCKET;
|
|
148
|
+
}
|
|
149
|
+
// Drop the send backlog; rx_ is deliberately left alone — close() can be called
|
|
150
|
+
// from inside a message handler that still holds a ByteView into it.
|
|
151
|
+
tx_.clear();
|
|
152
|
+
if (obs_) obs_->on_closed(*this, reason);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ---- I/O ----
|
|
156
|
+
|
|
157
|
+
void PeerConnection::on_io(std::uint32_t events) {
|
|
158
|
+
if (closed_) return;
|
|
159
|
+
if (events & PollOut) flush();
|
|
160
|
+
if (closed_) return;
|
|
161
|
+
if (events & PollIn) do_read();
|
|
162
|
+
if (closed_) return;
|
|
163
|
+
if (events & (PollErr | PollHup)) close("socket error");
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
void PeerConnection::do_read() {
|
|
167
|
+
// Drain the kernel buffer, parsing after every read. Parsing inside the loop
|
|
168
|
+
// (rather than once the socket is empty) is what bounds rx_ to a single in-flight
|
|
169
|
+
// message — otherwise a seed pushing pieces at line rate would grow it to
|
|
170
|
+
// however much it managed to deliver before we caught up.
|
|
171
|
+
//
|
|
172
|
+
// The loop must run to would-block or to a short read (kernel buffer now empty):
|
|
173
|
+
// the kqueue backend is edge-triggered, so leaving unread data behind would stall
|
|
174
|
+
// the connection until the peer happens to send more.
|
|
175
|
+
for (;;) {
|
|
176
|
+
const ByteSpan into = rx_.prepare(read_size());
|
|
177
|
+
|
|
178
|
+
const int n = ::recv(sock_, reinterpret_cast<char*>(into.data()),
|
|
179
|
+
static_cast<int>(into.size()), 0);
|
|
180
|
+
if (n == 0) { close("peer closed connection"); return; }
|
|
181
|
+
if (n < 0) {
|
|
182
|
+
if (would_block()) return;
|
|
183
|
+
close("recv error");
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
last_recv_ = std::chrono::steady_clock::now();
|
|
188
|
+
rx_.commit(std::size_t(n));
|
|
189
|
+
|
|
190
|
+
parse();
|
|
191
|
+
if (closed_) return;
|
|
192
|
+
|
|
193
|
+
if (std::size_t(n) < into.size()) return; // kernel buffer drained
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
std::size_t PeerConnection::read_size() const {
|
|
198
|
+
// Ask for the rest of the message we are mid-way through, so a big one (a bitfield,
|
|
199
|
+
// a metadata piece) lands in a single allocation rather than a series of 1.5x growth
|
|
200
|
+
// steps that memmove everything received so far at each one. Bounded by
|
|
201
|
+
// kMaxEagerReserve — rx_need_ is a length the *peer* declared.
|
|
202
|
+
//
|
|
203
|
+
// Note this asks for the remainder even when it is *smaller* than kRecvChunk. It is
|
|
204
|
+
// tempting to floor it at kRecvChunk ("read as much as we can anyway"), but
|
|
205
|
+
// prepare(n) is a demand for n bytes of free tail, and a buffer sized exactly for
|
|
206
|
+
// the message has no kRecvChunk of tail left near the end of it — so the floor would
|
|
207
|
+
// force one last 1.5x grow (memcpy'ing the whole message that already arrived) for
|
|
208
|
+
// room the message does not need. Nothing is lost by asking for less: prepare()
|
|
209
|
+
// hands back the *entire* free tail regardless, so the recv() is just as big.
|
|
210
|
+
if (rx_need_ > rx_.size()) return (std::min)(rx_need_ - rx_.size(), kMaxEagerReserve);
|
|
211
|
+
return kRecvChunk;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
void PeerConnection::parse() {
|
|
215
|
+
rx_need_ = 0; // recomputed below: 0 == no message is mid-flight
|
|
216
|
+
|
|
217
|
+
if (!handshake_received_) {
|
|
218
|
+
if (rx_.size() < kHandshakeSize) { rx_need_ = kHandshakeSize; return; }
|
|
219
|
+
if (!parse_handshake()) return; // consumed 68 bytes (or closed)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
while (!closed_ && rx_.size() >= 4) {
|
|
223
|
+
const std::uint32_t len = read_u32_be(rx_.data());
|
|
224
|
+
if (len > kMaxMessageLen) { close("oversize message"); return; }
|
|
225
|
+
if (rx_.size() < std::size_t(4) + len) {
|
|
226
|
+
rx_need_ = std::size_t(4) + len; // size rx_ for the whole message
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
if (len == 0) { rx_.consume(4); continue; } // keep-alive
|
|
230
|
+
|
|
231
|
+
const std::uint8_t id = rx_.data()[4];
|
|
232
|
+
const std::uint8_t* payload = rx_.data() + 5;
|
|
233
|
+
const std::uint32_t plen = len - 1;
|
|
234
|
+
dispatch(MessageId(id), payload, plen);
|
|
235
|
+
if (closed_) return;
|
|
236
|
+
rx_.consume(std::size_t(4) + len);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
bool PeerConnection::parse_handshake() {
|
|
241
|
+
const std::uint8_t* d = rx_.data();
|
|
242
|
+
if (d[0] != kProtocolStringLen || std::memcmp(d + 1, kProtocolString, kProtocolStringLen) != 0) {
|
|
243
|
+
close("bad protocol header");
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
std::memcpy(peer_reserved_.data(), d + 20, 8);
|
|
247
|
+
|
|
248
|
+
InfoHash their_info{};
|
|
249
|
+
std::memcpy(their_info.data(), d + 28, 20);
|
|
250
|
+
|
|
251
|
+
if (bound_) {
|
|
252
|
+
// Outgoing: the info-hash must be the one we dialed for.
|
|
253
|
+
if (their_info != info_hash_) { close("info-hash mismatch"); return false; }
|
|
254
|
+
} else {
|
|
255
|
+
// Incoming: resolve which torrent this is for and late-bind to it.
|
|
256
|
+
Binding b;
|
|
257
|
+
if (!resolver_ || !resolver_(their_info, b) || !b.observer) {
|
|
258
|
+
close("unknown torrent");
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
info_hash_ = their_info;
|
|
262
|
+
obs_ = b.observer;
|
|
263
|
+
num_pieces_ = b.num_pieces;
|
|
264
|
+
peer_have_ = Bitfield(num_pieces_, false);
|
|
265
|
+
bound_ = true;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
std::memcpy(peer_id_.data(), d + 48, 20);
|
|
269
|
+
rx_.consume(kHandshakeSize);
|
|
270
|
+
handshake_received_ = true;
|
|
271
|
+
|
|
272
|
+
// An incoming connection now knows which torrent it is for and replies.
|
|
273
|
+
if (!outgoing_ && !handshake_sent_) send_handshake();
|
|
274
|
+
|
|
275
|
+
LOG_DEBUG("bt.peer", remote_ip_ << ':' << remote_port_ << " ← handshake "
|
|
276
|
+
<< identify_client(peer_id_) << " (" << short_hash(info_hash_) << ')');
|
|
277
|
+
if (obs_) obs_->on_handshake(*this, info_hash_, peer_id_);
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
void PeerConnection::dispatch(MessageId id, const std::uint8_t* payload, std::uint32_t len) {
|
|
282
|
+
auto bad = [&] { close("malformed message"); };
|
|
283
|
+
|
|
284
|
+
switch (id) {
|
|
285
|
+
case MessageId::Choke:
|
|
286
|
+
if (len != 0) return bad();
|
|
287
|
+
peer_choking_ = true;
|
|
288
|
+
if (obs_) obs_->on_choke(*this, true);
|
|
289
|
+
break;
|
|
290
|
+
case MessageId::Unchoke:
|
|
291
|
+
if (len != 0) return bad();
|
|
292
|
+
peer_choking_ = false;
|
|
293
|
+
if (obs_) obs_->on_choke(*this, false);
|
|
294
|
+
break;
|
|
295
|
+
case MessageId::Interested:
|
|
296
|
+
if (len != 0) return bad();
|
|
297
|
+
peer_interested_ = true;
|
|
298
|
+
if (obs_) obs_->on_interest(*this, true);
|
|
299
|
+
break;
|
|
300
|
+
case MessageId::NotInterested:
|
|
301
|
+
if (len != 0) return bad();
|
|
302
|
+
peer_interested_ = false;
|
|
303
|
+
if (obs_) obs_->on_interest(*this, false);
|
|
304
|
+
break;
|
|
305
|
+
case MessageId::Have: {
|
|
306
|
+
if (len != 4) return bad();
|
|
307
|
+
piece_state_begun_ = true; // a HAVE begins the piece-state flow; a later bitfield is invalid
|
|
308
|
+
const std::uint32_t piece = read_u32_be(payload);
|
|
309
|
+
// Only act on a HAVE that actually flips a bit. A redundant HAVE (a piece
|
|
310
|
+
// the peer already advertised, via bitfield or an earlier HAVE) is legal on
|
|
311
|
+
// the wire, but re-notifying the observer would inc availability a second
|
|
312
|
+
// time in the picker while disconnect only decrements once per set bit —
|
|
313
|
+
// leaving the count permanently skewed and corrupting rarest-first. An
|
|
314
|
+
// out-of-range index simply flips nothing and is ignored.
|
|
315
|
+
if (piece < peer_have_.size() && !peer_have_.get(piece)) {
|
|
316
|
+
peer_have_.set(piece);
|
|
317
|
+
if (obs_) obs_->on_have(*this, piece);
|
|
318
|
+
}
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
321
|
+
case MessageId::Bitfield: {
|
|
322
|
+
// BEP 3: a bitfield is valid only as the peer's first piece-state
|
|
323
|
+
// message. A second bitfield (or one after any HAVE) would re-add the
|
|
324
|
+
// peer's whole availability in the picker with no matching decrement —
|
|
325
|
+
// a permanent skew. And when we know the piece count, the payload must
|
|
326
|
+
// be exactly ceil(num_pieces/8) bytes. Reject either as a violation.
|
|
327
|
+
if (piece_state_begun_) return bad();
|
|
328
|
+
if (num_pieces_ != 0 && len != (num_pieces_ + 7) / 8) return bad();
|
|
329
|
+
piece_state_begun_ = true;
|
|
330
|
+
const std::uint32_t bits = num_pieces_ ? num_pieces_ : len * 8;
|
|
331
|
+
peer_have_.assign(payload, len, bits);
|
|
332
|
+
if (obs_) obs_->on_bitfield(*this, peer_have_);
|
|
333
|
+
break;
|
|
334
|
+
}
|
|
335
|
+
case MessageId::Request: {
|
|
336
|
+
if (len != 12) return bad();
|
|
337
|
+
if (obs_) obs_->on_request(*this, read_u32_be(payload), read_u32_be(payload + 4),
|
|
338
|
+
read_u32_be(payload + 8));
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
case MessageId::Piece: {
|
|
342
|
+
if (len < 8) return bad();
|
|
343
|
+
if (obs_) obs_->on_piece(*this, read_u32_be(payload), read_u32_be(payload + 4),
|
|
344
|
+
ByteView(payload + 8, len - 8));
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
case MessageId::Cancel: {
|
|
348
|
+
if (len != 12) return bad();
|
|
349
|
+
if (obs_) obs_->on_cancel(*this, read_u32_be(payload), read_u32_be(payload + 4),
|
|
350
|
+
read_u32_be(payload + 8));
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
case MessageId::Port:
|
|
354
|
+
if (len != 2) return bad();
|
|
355
|
+
if (obs_) obs_->on_port(*this, read_u16_be(payload));
|
|
356
|
+
break;
|
|
357
|
+
case MessageId::Extended:
|
|
358
|
+
if (len < 1) return bad();
|
|
359
|
+
if (obs_) obs_->on_extended(*this, payload[0], ByteView(payload + 1, len - 1));
|
|
360
|
+
break;
|
|
361
|
+
default:
|
|
362
|
+
break; // unknown id — ignore for forward compatibility
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// ---- send ----
|
|
367
|
+
|
|
368
|
+
void PeerConnection::send_message(MessageId id, const std::uint8_t* payload, std::uint32_t len) {
|
|
369
|
+
std::uint8_t header[5];
|
|
370
|
+
write_u32_be(header, len + 1);
|
|
371
|
+
header[4] = std::uint8_t(id);
|
|
372
|
+
queue(ByteView(header, 5));
|
|
373
|
+
if (len) queue(ByteView(payload, len));
|
|
374
|
+
flush();
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
void PeerConnection::send_keepalive() {
|
|
378
|
+
const std::uint8_t z[4] = {0, 0, 0, 0};
|
|
379
|
+
queue(ByteView(z, 4));
|
|
380
|
+
flush();
|
|
381
|
+
}
|
|
382
|
+
void PeerConnection::send_choke() { am_choking_ = true; send_message(MessageId::Choke, nullptr, 0); }
|
|
383
|
+
void PeerConnection::send_unchoke() { am_choking_ = false; send_message(MessageId::Unchoke, nullptr, 0); }
|
|
384
|
+
void PeerConnection::send_interested() { am_interested_ = true; send_message(MessageId::Interested, nullptr, 0); }
|
|
385
|
+
void PeerConnection::send_not_interested(){ am_interested_ = false;send_message(MessageId::NotInterested, nullptr, 0); }
|
|
386
|
+
|
|
387
|
+
void PeerConnection::send_have(std::uint32_t piece) {
|
|
388
|
+
std::uint8_t p[4];
|
|
389
|
+
write_u32_be(p, piece);
|
|
390
|
+
send_message(MessageId::Have, p, 4);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
void PeerConnection::send_bitfield(const Bitfield& bitfield) {
|
|
394
|
+
send_message(MessageId::Bitfield, bitfield.data(), std::uint32_t(bitfield.data_size()));
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
void PeerConnection::send_request(std::uint32_t piece, std::uint32_t offset, std::uint32_t length) {
|
|
398
|
+
std::uint8_t p[12];
|
|
399
|
+
write_u32_be(p, piece);
|
|
400
|
+
write_u32_be(p + 4, offset);
|
|
401
|
+
write_u32_be(p + 8, length);
|
|
402
|
+
send_message(MessageId::Request, p, 12);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
void PeerConnection::send_piece(std::uint32_t piece, std::uint32_t offset, Bytes data) {
|
|
406
|
+
// The 13-byte header packs into the send queue's scratch chunk; the block is moved
|
|
407
|
+
// in as its own chunk, so the buffer the disk read filled is never copied. Gather
|
|
408
|
+
// I/O reunites the two, so this is still one send().
|
|
409
|
+
std::uint8_t header[13];
|
|
410
|
+
write_u32_be(header, std::uint32_t(9 + data.size()));
|
|
411
|
+
header[4] = std::uint8_t(MessageId::Piece);
|
|
412
|
+
write_u32_be(header + 5, piece);
|
|
413
|
+
write_u32_be(header + 9, offset);
|
|
414
|
+
queue(ByteView(header, sizeof(header)));
|
|
415
|
+
if (!data.empty()) queue(std::move(data));
|
|
416
|
+
flush();
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
void PeerConnection::send_cancel(std::uint32_t piece, std::uint32_t offset, std::uint32_t length) {
|
|
420
|
+
std::uint8_t p[12];
|
|
421
|
+
write_u32_be(p, piece);
|
|
422
|
+
write_u32_be(p + 4, offset);
|
|
423
|
+
write_u32_be(p + 8, length);
|
|
424
|
+
send_message(MessageId::Cancel, p, 12);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
void PeerConnection::send_port(std::uint16_t port) {
|
|
428
|
+
std::uint8_t p[2];
|
|
429
|
+
write_u16_be(p, port);
|
|
430
|
+
send_message(MessageId::Port, p, 2);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
void PeerConnection::send_extended(std::uint8_t ext_id, ByteView payload) {
|
|
434
|
+
std::uint8_t header[6];
|
|
435
|
+
write_u32_be(header, std::uint32_t(2 + payload.size()));
|
|
436
|
+
header[4] = std::uint8_t(MessageId::Extended);
|
|
437
|
+
header[5] = ext_id;
|
|
438
|
+
queue(ByteView(header, sizeof(header)));
|
|
439
|
+
if (!payload.empty()) queue(payload);
|
|
440
|
+
flush();
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
void PeerConnection::flush() {
|
|
444
|
+
if (closed_) return;
|
|
445
|
+
|
|
446
|
+
while (!tx_.empty()) {
|
|
447
|
+
// One syscall for the whole backlog, however many chunks it spans.
|
|
448
|
+
ByteView slices[kMaxSendSlices];
|
|
449
|
+
const std::size_t count = tx_.gather(slices, kMaxSendSlices);
|
|
450
|
+
|
|
451
|
+
const std::ptrdiff_t n = send_vectored(sock_, slices, count);
|
|
452
|
+
if (n > 0) {
|
|
453
|
+
last_sent_ = std::chrono::steady_clock::now();
|
|
454
|
+
tx_.pop_front(std::size_t(n));
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
if (n == 0) break; // nothing accepted; retry on PollOut
|
|
458
|
+
if (would_block()) break; // congested; the backlog waits for PollOut
|
|
459
|
+
close("send error");
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
want_write(!tx_.empty());
|
|
464
|
+
|
|
465
|
+
// Backpressure: allocated() is the memory the queue actually holds (a partially
|
|
466
|
+
// sent chunk keeps its whole allocation). Past the high-water mark the peer isn't
|
|
467
|
+
// draining us — drop it rather than buffer without bound.
|
|
468
|
+
if (tx_.allocated() > kSendHighWater) close("slow consumer: send buffer overflow");
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
void PeerConnection::want_write(bool on) {
|
|
472
|
+
if (on == want_write_ || closed_) return;
|
|
473
|
+
want_write_ = on;
|
|
474
|
+
reactor_.modify(sock_, PollIn | (on ? PollOut : PollNone));
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
void PeerConnection::tick() {
|
|
478
|
+
if (closed_) return;
|
|
479
|
+
const auto now = std::chrono::steady_clock::now();
|
|
480
|
+
|
|
481
|
+
if (!handshake_done()) {
|
|
482
|
+
// A peer that connects but never completes the handshake is dropped so it
|
|
483
|
+
// can't occupy a socket indefinitely.
|
|
484
|
+
if (now - created_ > kHandshakeTimeout) { close("handshake timeout"); return; }
|
|
485
|
+
} else {
|
|
486
|
+
if (now - last_recv_ > kIdleTimeout) { close("idle timeout"); return; }
|
|
487
|
+
// Keep the link alive if we've been quiet, so the peer doesn't drop us.
|
|
488
|
+
if (now - last_sent_ > kKeepAliveInterval) send_keepalive(); // updates last_sent_ via flush
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (closed_) return; // send_keepalive() may have hit a send error and closed us
|
|
492
|
+
|
|
493
|
+
// Hand back the receive buffer of a peer that has gone quiet. Without this a peer
|
|
494
|
+
// that sent one big message (a bitfield, a metadata piece) and then went idle would
|
|
495
|
+
// sit on that allocation for as long as the connection lives — the traffic-driven
|
|
496
|
+
// shrink in ReceiveBuffer only ever samples when a message actually arrives.
|
|
497
|
+
rx_.decay();
|
|
498
|
+
|
|
499
|
+
tick_timer_ = reactor_.schedule(kTickInterval, [this] { tick(); });
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
} // namespace librats::bittorrent
|