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,367 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file file_transfer.h
|
|
5
|
+
* @brief Stream a file or a whole directory tree to a peer, with integrity,
|
|
6
|
+
* backpressure, pause/resume/cancel, idle-timeout and crash-safe writes.
|
|
7
|
+
*
|
|
8
|
+
* Push model: the sender offers a file/directory; the receiver accepts (choosing
|
|
9
|
+
* a destination) or rejects; the sender streams the data; the receiver verifies a
|
|
10
|
+
* whole-file SHA-256 before moving each temp file into place. All control + data
|
|
11
|
+
* ride on MessageType::FileChunk as compact binary opcodes (no JSON), implemented
|
|
12
|
+
* on the Node/Subsystem plugin model.
|
|
13
|
+
*
|
|
14
|
+
* Integrity: every file ends with its SHA-256, verified end-to-end before the temp
|
|
15
|
+
* file is moved into place; a mismatch (or a disk-write failure) fails the whole
|
|
16
|
+
* transfer. In transit the Noise session already AEAD-authenticates every byte, so
|
|
17
|
+
* no redundant per-chunk checksum rides the wire.
|
|
18
|
+
*
|
|
19
|
+
* Backpressure: the sender keeps at most `window_bytes` un-acked; the receiver
|
|
20
|
+
* acks cumulative progress at least twice per window, so the sender never stalls.
|
|
21
|
+
*
|
|
22
|
+
* Safety: temp file names are derived from the sender's PeerId + the transfer id
|
|
23
|
+
* (never the peer's self-declared name) — the id alone is only unique per sender,
|
|
24
|
+
* so two peers' first transfers would otherwise share a temp file. Every
|
|
25
|
+
* peer-supplied relative path in a directory manifest is validated against path
|
|
26
|
+
* traversal before use, and a manifest file count is checked against the payload
|
|
27
|
+
* size before it is used to size an allocation.
|
|
28
|
+
*
|
|
29
|
+
* Threading: a worker pool runs the blocking send loop (one transfer per worker);
|
|
30
|
+
* receiving + all control handling run on the reactor thread; a maintenance
|
|
31
|
+
* thread reaps idle/timed-out transfers. A finished transfer is erased as it
|
|
32
|
+
* completes, so there is no retention window. Each transfer has its own
|
|
33
|
+
* mutex+condvar; the maps are guarded by mutex_.
|
|
34
|
+
*
|
|
35
|
+
* Wire (MessageType::FileChunk payload, big-endian):
|
|
36
|
+
* OFFER [1][id:u64][flags:u8][total:u64][name_len:u16][name][file_count:u32]
|
|
37
|
+
* { [path_len:u16][path][size:u64] } × file_count
|
|
38
|
+
* RESPONSE [2][id:u64][accept:u8]
|
|
39
|
+
* CHUNK [3][id:u64][file_index:u32][offset:u64][data]
|
|
40
|
+
* FILE_END [4][id:u64][file_index:u32][sha256:32]
|
|
41
|
+
* PROGRESS [5][id:u64][received:u64] (cumulative across all files)
|
|
42
|
+
* COMPLETE [6][id:u64][ok:u8]
|
|
43
|
+
* CANCEL [7][id:u64]
|
|
44
|
+
* PAUSE [8][id:u64]
|
|
45
|
+
* RESUME [9][id:u64]
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
#include "librats/util/rats_export.h"
|
|
49
|
+
#include "librats/node/peer_network.h"
|
|
50
|
+
#include "librats/peer/peer.h"
|
|
51
|
+
#include "librats/core/bytes.h"
|
|
52
|
+
#include "librats/peer/peer_id.h"
|
|
53
|
+
#include "librats/util/fs.h"
|
|
54
|
+
|
|
55
|
+
extern "C" {
|
|
56
|
+
#include "librats/crypto/sha256.h"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
#include <atomic>
|
|
60
|
+
#include <chrono>
|
|
61
|
+
#include <condition_variable>
|
|
62
|
+
#include <cstdint>
|
|
63
|
+
#include <functional>
|
|
64
|
+
#include <memory>
|
|
65
|
+
#include <mutex>
|
|
66
|
+
#include <queue>
|
|
67
|
+
#include <string>
|
|
68
|
+
#include <thread>
|
|
69
|
+
#include <unordered_map>
|
|
70
|
+
#include <vector>
|
|
71
|
+
|
|
72
|
+
namespace librats {
|
|
73
|
+
|
|
74
|
+
class RATS_API FileTransfer final : public Subsystem {
|
|
75
|
+
public:
|
|
76
|
+
struct Config {
|
|
77
|
+
uint32_t chunk_size = 64 * 1024; ///< payload bytes per chunk
|
|
78
|
+
uint32_t window_bytes = 4 * 1024 * 1024; ///< max un-acked bytes in flight
|
|
79
|
+
uint32_t progress_interval = 256 * 1024; ///< receiver acks every N bytes
|
|
80
|
+
uint32_t transfer_timeout_secs = 60; ///< abort a transfer idle this long
|
|
81
|
+
uint32_t worker_threads = 4; ///< concurrent outgoing transfers
|
|
82
|
+
uint32_t disk_threads = 4; ///< receive-side disk-writer pool size
|
|
83
|
+
bool verify_integrity = true; ///< whole-file SHA-256 end-to-end check
|
|
84
|
+
std::string temp_directory = "."; ///< holds in-progress downloads
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
enum class Status { Pending, Active, Paused, Completed, Failed, Cancelled };
|
|
88
|
+
enum class Direction { Sending, Receiving };
|
|
89
|
+
|
|
90
|
+
/// One file inside a transfer (a single-file transfer has exactly one).
|
|
91
|
+
struct FileEntry {
|
|
92
|
+
std::string relative_path; ///< POSIX path relative to the transfer root
|
|
93
|
+
uint64_t size = 0;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/// Delivered to the offer callback so the app can accept() or reject().
|
|
97
|
+
struct Offer {
|
|
98
|
+
PeerId from;
|
|
99
|
+
uint64_t id = 0;
|
|
100
|
+
std::string name; ///< file or directory name
|
|
101
|
+
uint64_t size = 0; ///< total size across all files
|
|
102
|
+
bool is_directory = false;
|
|
103
|
+
std::vector<FileEntry> files; ///< full manifest
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/// Snapshot passed to the progress callback (both directions).
|
|
107
|
+
struct Progress {
|
|
108
|
+
uint64_t id = 0;
|
|
109
|
+
PeerId peer;
|
|
110
|
+
Direction direction = Direction::Sending;
|
|
111
|
+
Status status = Status::Pending;
|
|
112
|
+
uint64_t bytes_transferred = 0;
|
|
113
|
+
uint64_t total_bytes = 0;
|
|
114
|
+
uint32_t files_completed = 0;
|
|
115
|
+
uint32_t total_files = 0;
|
|
116
|
+
|
|
117
|
+
double transfer_rate_bps = 0.0; ///< recent (smoothed) throughput, bytes/sec
|
|
118
|
+
double average_rate_bps = 0.0; ///< mean throughput since the transfer went live
|
|
119
|
+
std::chrono::milliseconds elapsed{0}; ///< time since it went live
|
|
120
|
+
std::chrono::milliseconds estimated_time_remaining{0}; ///< ETA at the recent rate (0 = unknown)
|
|
121
|
+
|
|
122
|
+
/// Completion in [0, 100].
|
|
123
|
+
double percent() const {
|
|
124
|
+
if (total_bytes == 0) return status == Status::Completed ? 100.0 : 0.0;
|
|
125
|
+
return static_cast<double>(bytes_transferred) / static_cast<double>(total_bytes) * 100.0;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/// Aggregate counters.
|
|
130
|
+
struct Stats {
|
|
131
|
+
uint64_t bytes_sent = 0, bytes_received = 0;
|
|
132
|
+
uint64_t completed = 0, failed = 0;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
using OfferHandler = std::function<void(const Offer&)>;
|
|
136
|
+
using ProgressHandler = std::function<void(const Progress&)>;
|
|
137
|
+
using CompleteHandler = std::function<void(uint64_t id, bool success, const std::string& path)>;
|
|
138
|
+
|
|
139
|
+
explicit FileTransfer(std::string temp_dir = ".");
|
|
140
|
+
explicit FileTransfer(Config config);
|
|
141
|
+
~FileTransfer() override;
|
|
142
|
+
|
|
143
|
+
void on_offer(OfferHandler handler) { offer_handler_ = std::move(handler); }
|
|
144
|
+
void on_progress(ProgressHandler handler) { progress_handler_ = std::move(handler); }
|
|
145
|
+
void on_complete(CompleteHandler handler) { complete_handler_ = std::move(handler); }
|
|
146
|
+
|
|
147
|
+
/// Offer a single file. Returns the transfer id (0 if the file is unusable).
|
|
148
|
+
uint64_t send_file(const PeerId& to, const std::string& path);
|
|
149
|
+
/// Offer a directory tree. Returns the transfer id (0 if the dir is unusable).
|
|
150
|
+
uint64_t send_directory(const PeerId& to, const std::string& dir_path);
|
|
151
|
+
|
|
152
|
+
/// Accept an offered transfer. For a single file, dest_path is the file path;
|
|
153
|
+
/// for a directory, it is the destination directory. (from, id) names the offer.
|
|
154
|
+
void accept(const PeerId& from, uint64_t id, const std::string& dest_path);
|
|
155
|
+
void reject(const PeerId& from, uint64_t id);
|
|
156
|
+
|
|
157
|
+
/// Control a live transfer (works from either side); (peer, id) names it.
|
|
158
|
+
bool cancel(const PeerId& peer, uint64_t id);
|
|
159
|
+
bool pause(const PeerId& peer, uint64_t id);
|
|
160
|
+
bool resume(const PeerId& peer, uint64_t id);
|
|
161
|
+
|
|
162
|
+
Stats stats() const;
|
|
163
|
+
|
|
164
|
+
/// A relative path from a peer's directory manifest is safe only if it stays
|
|
165
|
+
/// inside the destination: non-empty, not absolute, no drive letter, and no
|
|
166
|
+
/// "."/".." component. Public + static so it can be unit-tested directly.
|
|
167
|
+
static bool is_safe_relative_path(const std::string& p);
|
|
168
|
+
|
|
169
|
+
void attach(NodeContext& ctx) override;
|
|
170
|
+
void start() override;
|
|
171
|
+
void stop() override;
|
|
172
|
+
|
|
173
|
+
private:
|
|
174
|
+
// Smoothed throughput + elapsed/ETA tracking for one transfer. Accessed only
|
|
175
|
+
// under the owning transfer's mutex. The clock starts lazily on the first
|
|
176
|
+
// sample (first byte activity); a long gap (pause/idle) is treated as a
|
|
177
|
+
// discontinuity so it doesn't register as a throughput dip.
|
|
178
|
+
struct RateTracker {
|
|
179
|
+
using clock = std::chrono::steady_clock;
|
|
180
|
+
clock::time_point start{}; ///< first byte activity (transfer went live)
|
|
181
|
+
clock::time_point mark{}; ///< last rate-sample instant
|
|
182
|
+
uint64_t mark_bytes = 0;
|
|
183
|
+
double rate_bps = 0.0; ///< EWMA of recent throughput
|
|
184
|
+
|
|
185
|
+
void sample(uint64_t bytes, clock::time_point now) {
|
|
186
|
+
constexpr int64_t kMinMs = 250, kMaxMs = 2000;
|
|
187
|
+
constexpr double kAlpha = 0.4;
|
|
188
|
+
if (start == clock::time_point{}) { start = mark = now; mark_bytes = bytes; return; }
|
|
189
|
+
const int64_t dt = std::chrono::duration_cast<std::chrono::milliseconds>(now - mark).count();
|
|
190
|
+
if (dt < kMinMs) return; // too soon: hold the prior rate
|
|
191
|
+
if (dt > kMaxMs) { mark = now; mark_bytes = bytes; return; } // gap: reset, don't pollute
|
|
192
|
+
const double inst = static_cast<double>(bytes - mark_bytes) * 1000.0 / static_cast<double>(dt);
|
|
193
|
+
rate_bps = rate_bps <= 0.0 ? inst : kAlpha * inst + (1.0 - kAlpha) * rate_bps;
|
|
194
|
+
mark = now; mark_bytes = bytes;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
void fill(Progress& p, uint64_t bytes, uint64_t total, clock::time_point now) const {
|
|
198
|
+
if (start == clock::time_point{}) return; // not live yet
|
|
199
|
+
const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);
|
|
200
|
+
p.elapsed = ms;
|
|
201
|
+
p.transfer_rate_bps = rate_bps;
|
|
202
|
+
if (ms.count() > 0)
|
|
203
|
+
p.average_rate_bps = static_cast<double>(bytes) * 1000.0 / static_cast<double>(ms.count());
|
|
204
|
+
if (rate_bps > 1.0 && total > bytes)
|
|
205
|
+
p.estimated_time_remaining = std::chrono::milliseconds(
|
|
206
|
+
static_cast<int64_t>(static_cast<double>(total - bytes) / rate_bps * 1000.0));
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// ── Per-transfer state (held via shared_ptr so workers/handlers keep it
|
|
211
|
+
// alive past map removal) ──────────────────────────────────────────────
|
|
212
|
+
struct Outgoing {
|
|
213
|
+
uint64_t id = 0;
|
|
214
|
+
PeerId peer;
|
|
215
|
+
std::string name;
|
|
216
|
+
std::string root; ///< local source path/dir
|
|
217
|
+
bool is_directory = false;
|
|
218
|
+
std::vector<FileEntry> files; ///< relative_path + size
|
|
219
|
+
std::vector<std::string> sources; ///< absolute source path per file
|
|
220
|
+
uint64_t total_bytes = 0;
|
|
221
|
+
|
|
222
|
+
std::mutex mtx;
|
|
223
|
+
std::condition_variable cv;
|
|
224
|
+
size_t cur_file = 0;
|
|
225
|
+
uint64_t cur_offset = 0;
|
|
226
|
+
uint64_t bytes_done = 0;
|
|
227
|
+
uint64_t acked = 0;
|
|
228
|
+
uint32_t files_done = 0;
|
|
229
|
+
Status status = Status::Pending;
|
|
230
|
+
bool worker_active = false;
|
|
231
|
+
bool finished = false;
|
|
232
|
+
rats_sha256_context_t hash{};
|
|
233
|
+
std::chrono::steady_clock::time_point last_activity{};
|
|
234
|
+
RateTracker rate;
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
struct IncomingFile {
|
|
238
|
+
std::string relative_path;
|
|
239
|
+
uint64_t size = 0;
|
|
240
|
+
std::string final_path;
|
|
241
|
+
std::string temp_path;
|
|
242
|
+
uint64_t enqueued = 0; ///< reactor: bytes handed to the disk writer
|
|
243
|
+
uint64_t received = 0; ///< writer: bytes actually written to disk
|
|
244
|
+
bool temp_created = false;
|
|
245
|
+
bool finalized = false;
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
// A unit of disk work queued by the reactor for the writer pool. A pure data
|
|
249
|
+
// job carries chunk bytes; a file-end job carries the sender's SHA-256 so the
|
|
250
|
+
// writer can verify + finalize once all of the file's data is on disk.
|
|
251
|
+
struct WriteJob {
|
|
252
|
+
uint32_t fidx = 0;
|
|
253
|
+
uint64_t offset = 0;
|
|
254
|
+
Bytes data; ///< chunk bytes (empty for a file-end job)
|
|
255
|
+
bool is_file_end = false;
|
|
256
|
+
uint8_t sha[RATS_SHA256_HASH_SIZE]{};
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
struct Incoming {
|
|
260
|
+
uint64_t id = 0;
|
|
261
|
+
PeerId peer;
|
|
262
|
+
std::string name;
|
|
263
|
+
bool is_directory = false;
|
|
264
|
+
std::string dest_root;
|
|
265
|
+
std::vector<IncomingFile> files;
|
|
266
|
+
|
|
267
|
+
std::mutex mtx;
|
|
268
|
+
size_t recv_file = 0; ///< reactor cursor: file currently accepting chunks
|
|
269
|
+
uint64_t bytes_done = 0; ///< writer: total bytes on disk (drives acks/progress)
|
|
270
|
+
uint64_t last_ack = 0;
|
|
271
|
+
uint32_t files_done = 0;
|
|
272
|
+
Status status = Status::Pending;
|
|
273
|
+
bool finished = false;
|
|
274
|
+
std::chrono::steady_clock::time_point last_activity{};
|
|
275
|
+
RateTracker rate;
|
|
276
|
+
|
|
277
|
+
// ── async disk writer (all fields below the queue are writer-thread only) ─
|
|
278
|
+
std::queue<WriteJob> wq; ///< pending disk jobs (guarded by mtx)
|
|
279
|
+
uint64_t queued_bytes = 0;///< bytes sitting in wq (backpressure)
|
|
280
|
+
bool scheduled = false;///< queued in / owned by the writer pool
|
|
281
|
+
FileStream out; ///< currently open temp file
|
|
282
|
+
size_t out_idx = SIZE_MAX;
|
|
283
|
+
int hashing_file = -1;///< which file `hash` currently covers
|
|
284
|
+
rats_sha256_context_t hash{};
|
|
285
|
+
|
|
286
|
+
~Incoming(); ///< closes `out` and reclaims any un-finalized temp files
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
// ── message handling (reactor thread) ─────────────────────────────────────
|
|
290
|
+
void on_message(const Peer& peer, ByteView payload);
|
|
291
|
+
void handle_offer(const PeerId& from, uint64_t id, bool is_dir, uint64_t total,
|
|
292
|
+
std::string name, std::vector<FileEntry> files);
|
|
293
|
+
void handle_chunk(const PeerId& from, uint64_t id, uint32_t fidx, uint64_t offset,
|
|
294
|
+
ByteView data);
|
|
295
|
+
void handle_file_end(const PeerId& from, uint64_t id, uint32_t fidx, const uint8_t* sha);
|
|
296
|
+
|
|
297
|
+
// ── sending ──────────────────────────────────────────────────────────────
|
|
298
|
+
uint64_t start_send(std::shared_ptr<Outgoing> t);
|
|
299
|
+
void queue_send(uint64_t id);
|
|
300
|
+
void worker_loop();
|
|
301
|
+
void run_send(const std::shared_ptr<Outgoing>& t);
|
|
302
|
+
|
|
303
|
+
// ── receiving ────────────────────────────────────────────────────────────
|
|
304
|
+
// The reactor thread only validates + copies a chunk into the transfer's write
|
|
305
|
+
// queue; a disk-writer thread does the blocking write, hashing and finalize off
|
|
306
|
+
// the reactor. `schedule_writer` hands a transfer to the pool (call with the
|
|
307
|
+
// transfer's mutex NOT held — pool lock is always taken after the transfer's).
|
|
308
|
+
void schedule_writer(const std::shared_ptr<Incoming>& t);
|
|
309
|
+
void disk_worker_loop();
|
|
310
|
+
void drain_writes(const std::shared_ptr<Incoming>& t);
|
|
311
|
+
void process_data(const std::shared_ptr<Incoming>& t, WriteJob& job);
|
|
312
|
+
void process_file_end(const std::shared_ptr<Incoming>& t, WriteJob& job);
|
|
313
|
+
|
|
314
|
+
// ── lifecycle helpers ─────────────────────────────────────────────────────
|
|
315
|
+
void maintenance_loop();
|
|
316
|
+
void finish_outgoing(const std::shared_ptr<Outgoing>& t, bool success);
|
|
317
|
+
void finish_incoming(const std::shared_ptr<Incoming>& t, bool success, const std::string& error);
|
|
318
|
+
void emit_progress(const std::shared_ptr<Outgoing>& t);
|
|
319
|
+
void emit_progress(const std::shared_ptr<Incoming>& t);
|
|
320
|
+
|
|
321
|
+
std::shared_ptr<Outgoing> find_outgoing(uint64_t id) const;
|
|
322
|
+
std::shared_ptr<Incoming> find_incoming(const PeerId& peer, uint64_t id) const;
|
|
323
|
+
|
|
324
|
+
void send_to(const PeerId& peer, const Bytes& msg) {
|
|
325
|
+
if (network_) network_->send(peer, MessageType::FileChunk, ByteView(msg));
|
|
326
|
+
}
|
|
327
|
+
void send_simple(const PeerId& peer, uint8_t op, uint64_t id);
|
|
328
|
+
void send_complete(const PeerId& peer, uint64_t id, bool ok);
|
|
329
|
+
|
|
330
|
+
PeerNetwork* network_ = nullptr;
|
|
331
|
+
Config config_;
|
|
332
|
+
std::atomic<uint64_t> next_id_{1};
|
|
333
|
+
std::atomic<bool> running_{false};
|
|
334
|
+
|
|
335
|
+
OfferHandler offer_handler_;
|
|
336
|
+
ProgressHandler progress_handler_;
|
|
337
|
+
CompleteHandler complete_handler_;
|
|
338
|
+
|
|
339
|
+
mutable std::mutex mutex_; ///< guards the two maps below
|
|
340
|
+
std::unordered_map<uint64_t, std::shared_ptr<Outgoing>> outgoing_;
|
|
341
|
+
std::unordered_map<PeerId, std::unordered_map<uint64_t, std::shared_ptr<Incoming>>,
|
|
342
|
+
PeerId::Hash> incoming_;
|
|
343
|
+
|
|
344
|
+
// send-side worker pool + send queue
|
|
345
|
+
std::vector<std::thread> workers_;
|
|
346
|
+
std::mutex queue_mutex_;
|
|
347
|
+
std::condition_variable queue_cv_;
|
|
348
|
+
std::queue<uint64_t> send_queue_;
|
|
349
|
+
|
|
350
|
+
// receive-side disk-writer pool + ready queue. A transfer is pushed here when it
|
|
351
|
+
// has pending write jobs; its `scheduled` flag keeps it single-owner so exactly
|
|
352
|
+
// one worker drains a given transfer at a time (preserving chunk order).
|
|
353
|
+
std::vector<std::thread> disk_workers_;
|
|
354
|
+
std::mutex disk_mutex_;
|
|
355
|
+
std::condition_variable disk_cv_;
|
|
356
|
+
std::queue<std::shared_ptr<Incoming>> disk_ready_;
|
|
357
|
+
|
|
358
|
+
// maintenance (idle timeout / purge)
|
|
359
|
+
std::thread maintenance_thread_;
|
|
360
|
+
std::mutex maintenance_mutex_;
|
|
361
|
+
std::condition_variable maintenance_cv_;
|
|
362
|
+
|
|
363
|
+
mutable std::mutex stats_mutex_;
|
|
364
|
+
Stats stats_;
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
} // namespace librats
|