librats 1.0.2 → 2.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (313) hide show
  1. package/README.md +137 -332
  2. package/binding.gyp +16 -3
  3. package/lib/index.d.ts +271 -696
  4. package/lib/index.js +383 -44
  5. package/native-src/3rdparty/android/ifaddrs-android.c +1 -0
  6. package/native-src/3rdparty/android/ifaddrs-android.h +1 -0
  7. package/native-src/CMakeLists.txt +396 -179
  8. package/native-src/LICENSE +1 -1
  9. package/native-src/src/librats/bindings/rats.cpp +731 -0
  10. package/native-src/src/librats/bindings/rats.h +352 -0
  11. package/native-src/src/librats/bittorrent/bencode.cpp +437 -0
  12. package/native-src/src/librats/bittorrent/bencode.h +176 -0
  13. package/native-src/src/librats/bittorrent/bitfield.cpp +97 -0
  14. package/native-src/src/librats/bittorrent/bitfield.h +76 -0
  15. package/native-src/src/librats/bittorrent/byte_io.h +58 -0
  16. package/native-src/src/librats/bittorrent/choker.cpp +25 -0
  17. package/native-src/src/librats/bittorrent/choker.h +46 -0
  18. package/native-src/src/librats/bittorrent/client.cpp +413 -0
  19. package/native-src/src/librats/bittorrent/client.h +227 -0
  20. package/native-src/src/librats/bittorrent/disk_io.cpp +209 -0
  21. package/native-src/src/librats/bittorrent/disk_io.h +150 -0
  22. package/native-src/src/librats/bittorrent/extensions.cpp +191 -0
  23. package/native-src/src/librats/bittorrent/extensions.h +94 -0
  24. package/native-src/src/librats/bittorrent/file_storage.cpp +77 -0
  25. package/native-src/src/librats/bittorrent/file_storage.h +79 -0
  26. package/native-src/src/librats/bittorrent/log.h +42 -0
  27. package/native-src/src/librats/bittorrent/magnet_uri.cpp +98 -0
  28. package/native-src/src/librats/bittorrent/magnet_uri.h +35 -0
  29. package/native-src/src/librats/bittorrent/peer_connection.cpp +502 -0
  30. package/native-src/src/librats/bittorrent/peer_connection.h +194 -0
  31. package/native-src/src/librats/bittorrent/peer_list.cpp +68 -0
  32. package/native-src/src/librats/bittorrent/peer_list.h +75 -0
  33. package/native-src/src/librats/bittorrent/piece_picker.cpp +352 -0
  34. package/native-src/src/librats/bittorrent/piece_picker.h +201 -0
  35. package/native-src/src/librats/bittorrent/reactor.cpp +97 -0
  36. package/native-src/src/librats/bittorrent/reactor.h +89 -0
  37. package/native-src/src/librats/bittorrent/resume_data.cpp +72 -0
  38. package/native-src/src/librats/bittorrent/resume_data.h +41 -0
  39. package/native-src/src/librats/bittorrent/store_buffer.cpp +48 -0
  40. package/native-src/src/librats/bittorrent/store_buffer.h +47 -0
  41. package/native-src/src/librats/bittorrent/torrent.cpp +870 -0
  42. package/native-src/src/librats/bittorrent/torrent.h +260 -0
  43. package/native-src/src/librats/bittorrent/torrent_creator.cpp +129 -0
  44. package/native-src/src/librats/bittorrent/torrent_creator.h +58 -0
  45. package/native-src/src/librats/bittorrent/torrent_info.cpp +314 -0
  46. package/native-src/src/librats/bittorrent/torrent_info.h +118 -0
  47. package/native-src/src/librats/bittorrent/tracker.cpp +374 -0
  48. package/native-src/src/librats/bittorrent/tracker.h +108 -0
  49. package/native-src/src/librats/bittorrent/types.cpp +206 -0
  50. package/native-src/src/librats/bittorrent/types.h +86 -0
  51. package/native-src/src/librats/core/address.cpp +35 -0
  52. package/native-src/src/librats/core/address.h +78 -0
  53. package/native-src/src/librats/core/bytes.h +69 -0
  54. package/native-src/src/librats/core/chained_send_buffer.cpp +172 -0
  55. package/native-src/src/librats/core/chained_send_buffer.h +183 -0
  56. package/native-src/src/librats/core/endpoint_parse.cpp +41 -0
  57. package/native-src/src/librats/core/endpoint_parse.h +31 -0
  58. package/native-src/src/librats/core/event_bus.h +70 -0
  59. package/native-src/src/librats/core/host_endpoint.h +56 -0
  60. package/native-src/src/{io_poller.cpp → librats/core/io_poller.cpp} +520 -65
  61. package/native-src/src/{io_poller.h → librats/core/io_poller.h} +12 -6
  62. package/native-src/src/librats/core/ip_address.cpp +120 -0
  63. package/native-src/src/librats/core/ip_address.h +109 -0
  64. package/native-src/src/librats/core/mpsc_queue.h +47 -0
  65. package/native-src/src/librats/core/notifier.h +74 -0
  66. package/native-src/src/librats/core/receive_buffer.cpp +219 -0
  67. package/native-src/src/librats/core/receive_buffer.h +171 -0
  68. package/native-src/src/librats/core/service_registry.h +58 -0
  69. package/native-src/src/{socket.cpp → librats/core/socket.cpp} +625 -118
  70. package/native-src/src/librats/core/socket.h +496 -0
  71. package/native-src/src/librats/core/timer_queue.h +105 -0
  72. package/native-src/src/librats/core/types.cpp +42 -0
  73. package/native-src/src/librats/core/types.h +93 -0
  74. package/native-src/src/librats/core/wakeup_pipe.h +83 -0
  75. package/native-src/src/{crypto → librats/crypto}/blake2_endian.h +21 -23
  76. package/native-src/src/{crypto → librats/crypto}/blake2b.c +34 -33
  77. package/native-src/src/{crypto → librats/crypto}/blake2b.h +7 -6
  78. package/native-src/src/{crypto → librats/crypto}/blake2s.c +55 -54
  79. package/native-src/src/{crypto → librats/crypto}/blake2s.h +13 -12
  80. package/native-src/src/{crypto → librats/crypto}/chacha.c +22 -21
  81. package/native-src/src/{crypto → librats/crypto}/chacha.h +14 -13
  82. package/native-src/src/{crypto → librats/crypto}/chachapoly.c +56 -56
  83. package/native-src/src/{crypto → librats/crypto}/chachapoly.h +24 -17
  84. package/native-src/src/{crc32.cpp → librats/crypto/crc32.cpp} +1 -1
  85. package/native-src/src/{crc32.h → librats/crypto/crc32.h} +3 -1
  86. package/native-src/src/{crypto → librats/crypto}/curve25519.c +6 -4
  87. package/native-src/src/{crypto → librats/crypto}/curve25519.h +6 -3
  88. package/native-src/src/librats/crypto/hkdf.c +266 -0
  89. package/native-src/src/{crypto → librats/crypto}/hkdf.h +19 -19
  90. package/native-src/src/{noise.cpp → librats/crypto/noise.cpp} +84 -73
  91. package/native-src/src/{noise.h → librats/crypto/noise.h} +18 -8
  92. package/native-src/src/{crypto → librats/crypto}/poly1305.c +47 -46
  93. package/native-src/src/librats/crypto/poly1305.h +37 -0
  94. package/native-src/src/{sha1.cpp → librats/crypto/sha1.cpp} +33 -1
  95. package/native-src/src/{sha1.h → librats/crypto/sha1.h} +14 -6
  96. package/native-src/src/{crypto → librats/crypto}/sha256.c +15 -14
  97. package/native-src/src/{crypto → librats/crypto}/sha256.h +8 -7
  98. package/native-src/src/{crypto → librats/crypto}/sha512.c +15 -14
  99. package/native-src/src/{crypto → librats/crypto}/sha512.h +8 -7
  100. package/native-src/src/librats/dht/announce.cpp +37 -0
  101. package/native-src/src/librats/dht/announce.h +41 -0
  102. package/native-src/src/librats/dht/bep42.cpp +109 -0
  103. package/native-src/src/librats/dht/bep42.h +48 -0
  104. package/native-src/src/librats/dht/dht.cpp +501 -0
  105. package/native-src/src/librats/dht/dht.h +119 -0
  106. package/native-src/src/librats/dht/dht_runner.cpp +103 -0
  107. package/native-src/src/librats/dht/dht_runner.h +71 -0
  108. package/native-src/src/librats/dht/dos_blocker.cpp +42 -0
  109. package/native-src/src/librats/dht/dos_blocker.h +47 -0
  110. package/native-src/src/librats/dht/find_peers.cpp +52 -0
  111. package/native-src/src/librats/dht/find_peers.h +73 -0
  112. package/native-src/src/librats/dht/id.h +167 -0
  113. package/native-src/src/{krpc.cpp → librats/dht/krpc.cpp} +32 -81
  114. package/native-src/src/{krpc.h → librats/dht/krpc.h} +19 -23
  115. package/native-src/src/librats/dht/log.h +38 -0
  116. package/native-src/src/librats/dht/node.cpp +473 -0
  117. package/native-src/src/librats/dht/node.h +164 -0
  118. package/native-src/src/librats/dht/node_entry.h +81 -0
  119. package/native-src/src/librats/dht/observer.h +72 -0
  120. package/native-src/src/librats/dht/persistence.cpp +90 -0
  121. package/native-src/src/librats/dht/persistence.h +32 -0
  122. package/native-src/src/librats/dht/routing_table.cpp +559 -0
  123. package/native-src/src/librats/dht/routing_table.h +185 -0
  124. package/native-src/src/librats/dht/rpc_manager.cpp +127 -0
  125. package/native-src/src/librats/dht/rpc_manager.h +77 -0
  126. package/native-src/src/librats/dht/storage.cpp +92 -0
  127. package/native-src/src/librats/dht/storage.h +74 -0
  128. package/native-src/src/librats/dht/transport.h +27 -0
  129. package/native-src/src/librats/dht/traversal.cpp +326 -0
  130. package/native-src/src/librats/dht/traversal.h +120 -0
  131. package/native-src/src/librats/dht/udp_transport.cpp +49 -0
  132. package/native-src/src/librats/dht/udp_transport.h +51 -0
  133. package/native-src/src/librats/mdns/log.h +22 -0
  134. package/native-src/src/{mdns.cpp → librats/mdns/mdns.cpp} +75 -40
  135. package/native-src/src/{mdns.h → librats/mdns/mdns.h} +9 -8
  136. package/native-src/src/{natpmp.cpp → librats/nat/natpmp.cpp} +12 -9
  137. package/native-src/src/{natpmp.h → librats/nat/natpmp.h} +3 -3
  138. package/native-src/src/{port_mapping.h → librats/nat/port_mapping.h} +3 -2
  139. package/native-src/src/{stun.cpp → librats/nat/stun.cpp} +4 -4
  140. package/native-src/src/{stun.h → librats/nat/stun.h} +1 -1
  141. package/native-src/src/{upnp.cpp → librats/nat/upnp.cpp} +6 -6
  142. package/native-src/src/{upnp.h → librats/nat/upnp.h} +2 -2
  143. package/native-src/src/librats/node/config.h +110 -0
  144. package/native-src/src/librats/node/dial_service.h +54 -0
  145. package/native-src/src/librats/node/dialer.cpp +264 -0
  146. package/native-src/src/librats/node/dialer.h +188 -0
  147. package/native-src/src/librats/node/host_events.h +26 -0
  148. package/native-src/src/librats/node/identify.cpp +130 -0
  149. package/native-src/src/librats/node/identify.h +71 -0
  150. package/native-src/src/librats/node/nat_status.cpp +103 -0
  151. package/native-src/src/librats/node/nat_status.h +118 -0
  152. package/native-src/src/librats/node/node.cpp +826 -0
  153. package/native-src/src/librats/node/node.h +333 -0
  154. package/native-src/src/librats/node/node_context.h +33 -0
  155. package/native-src/src/librats/node/peer_network.h +91 -0
  156. package/native-src/src/librats/peer/peer.h +49 -0
  157. package/native-src/src/librats/peer/peer_book.cpp +181 -0
  158. package/native-src/src/librats/peer/peer_book.h +88 -0
  159. package/native-src/src/librats/peer/peer_id.cpp +72 -0
  160. package/native-src/src/librats/peer/peer_id.h +62 -0
  161. package/native-src/src/librats/peer/peer_info.h +37 -0
  162. package/native-src/src/librats/peer/peer_table.cpp +137 -0
  163. package/native-src/src/librats/peer/peer_table.h +148 -0
  164. package/native-src/src/librats/security/handshaker.h +66 -0
  165. package/native-src/src/librats/security/identity.h +43 -0
  166. package/native-src/src/librats/security/noise_security.cpp +122 -0
  167. package/native-src/src/librats/security/noise_security.h +37 -0
  168. package/native-src/src/librats/security/plaintext_security.h +106 -0
  169. package/native-src/src/librats/security/session.h +37 -0
  170. package/native-src/src/{storage.cpp → librats/storage/storage.cpp} +369 -522
  171. package/native-src/src/{storage.h → librats/storage/storage.h} +135 -299
  172. package/native-src/src/librats/subsystems/bittorrent.cpp +211 -0
  173. package/native-src/src/librats/subsystems/bittorrent.h +136 -0
  174. package/native-src/src/librats/subsystems/dht_discovery.cpp +202 -0
  175. package/native-src/src/librats/subsystems/dht_discovery.h +123 -0
  176. package/native-src/src/librats/subsystems/dht_service.h +36 -0
  177. package/native-src/src/librats/subsystems/file_transfer.cpp +972 -0
  178. package/native-src/src/librats/subsystems/file_transfer.h +367 -0
  179. package/native-src/src/librats/subsystems/hole_punch.cpp +532 -0
  180. package/native-src/src/librats/subsystems/hole_punch.h +266 -0
  181. package/native-src/src/librats/subsystems/hole_punch_service.h +38 -0
  182. package/native-src/src/librats/subsystems/mdns_discovery.cpp +66 -0
  183. package/native-src/src/librats/subsystems/mdns_discovery.h +55 -0
  184. package/native-src/src/librats/subsystems/message_json.cpp +112 -0
  185. package/native-src/src/librats/subsystems/message_json.h +88 -0
  186. package/native-src/src/librats/subsystems/peer_exchange.cpp +241 -0
  187. package/native-src/src/librats/subsystems/peer_exchange.h +136 -0
  188. package/native-src/src/librats/subsystems/ping_service.cpp +98 -0
  189. package/native-src/src/librats/subsystems/ping_service.h +66 -0
  190. package/native-src/src/librats/subsystems/port_mapping_service.cpp +192 -0
  191. package/native-src/src/librats/subsystems/port_mapping_service.h +84 -0
  192. package/native-src/src/librats/subsystems/pubsub.cpp +567 -0
  193. package/native-src/src/librats/subsystems/pubsub.h +175 -0
  194. package/native-src/src/librats/subsystems/reconnection.cpp +239 -0
  195. package/native-src/src/librats/subsystems/reconnection.h +126 -0
  196. package/native-src/src/librats/transport/connection.cpp +343 -0
  197. package/native-src/src/librats/transport/connection.h +283 -0
  198. package/native-src/src/librats/transport/link.h +96 -0
  199. package/native-src/src/librats/transport/reactor.cpp +540 -0
  200. package/native-src/src/librats/transport/reactor.h +224 -0
  201. package/native-src/src/librats/transport/reactor_pool.h +81 -0
  202. package/native-src/src/librats/transport/tcp_link.cpp +49 -0
  203. package/native-src/src/librats/transport/tcp_link.h +43 -0
  204. package/native-src/src/librats/transport/udp_mux.cpp +617 -0
  205. package/native-src/src/librats/transport/udp_mux.h +363 -0
  206. package/native-src/src/librats/transport/udp_packet.cpp +121 -0
  207. package/native-src/src/librats/transport/udp_packet.h +190 -0
  208. package/native-src/src/librats/transport/udp_stream.cpp +1194 -0
  209. package/native-src/src/librats/transport/udp_stream.h +614 -0
  210. package/native-src/src/librats/util/features.h.in +51 -0
  211. package/native-src/src/{fs.cpp → librats/util/fs.cpp} +51 -3
  212. package/native-src/src/librats/util/fs.h +136 -0
  213. package/native-src/src/librats/util/json.cpp +1002 -0
  214. package/native-src/src/librats/util/json.h +444 -0
  215. package/native-src/src/{logger.cpp → librats/util/logger.cpp} +1 -1
  216. package/native-src/src/{logger.h → librats/util/logger.h} +43 -31
  217. package/native-src/src/{network_monitor.cpp → librats/util/network_monitor.cpp} +12 -4
  218. package/native-src/src/{network_monitor.h → librats/util/network_monitor.h} +2 -1
  219. package/native-src/src/{network_utils.cpp → librats/util/network_utils.cpp} +38 -23
  220. package/native-src/src/{network_utils.h → librats/util/network_utils.h} +15 -8
  221. package/native-src/src/{os.cpp → librats/util/os.cpp} +48 -18
  222. package/native-src/src/librats/util/rats_export.h +69 -0
  223. package/native-src/src/{version.cpp → librats/util/version.cpp} +2 -2
  224. package/native-src/src/{version.h.in → librats/util/version.h.in} +1 -1
  225. package/native-src/src/librats/wire/frame.cpp +76 -0
  226. package/native-src/src/librats/wire/frame.h +110 -0
  227. package/native-src/src/librats/wire/message_router.cpp +45 -0
  228. package/native-src/src/librats/wire/message_router.h +47 -0
  229. package/package.json +5 -4
  230. package/scripts/build-librats.js +1 -0
  231. package/scripts/postinstall.js +3 -3
  232. package/scripts/prepare-package.js +4 -4
  233. package/scripts/verify-installation.js +63 -105
  234. package/src/librats_node.cpp +1042 -1324
  235. package/native-src/src/bencode.cpp +0 -485
  236. package/native-src/src/bencode.h +0 -145
  237. package/native-src/src/bittorrent.cpp +0 -14
  238. package/native-src/src/bittorrent.h +0 -74
  239. package/native-src/src/bt_bitfield.cpp +0 -372
  240. package/native-src/src/bt_bitfield.h +0 -316
  241. package/native-src/src/bt_choker.cpp +0 -228
  242. package/native-src/src/bt_choker.h +0 -147
  243. package/native-src/src/bt_client.cpp +0 -1047
  244. package/native-src/src/bt_client.h +0 -445
  245. package/native-src/src/bt_create_torrent.cpp +0 -677
  246. package/native-src/src/bt_create_torrent.h +0 -473
  247. package/native-src/src/bt_extension.cpp +0 -469
  248. package/native-src/src/bt_extension.h +0 -309
  249. package/native-src/src/bt_file_storage.cpp +0 -261
  250. package/native-src/src/bt_file_storage.h +0 -298
  251. package/native-src/src/bt_handshake.cpp +0 -134
  252. package/native-src/src/bt_handshake.h +0 -157
  253. package/native-src/src/bt_messages.cpp +0 -364
  254. package/native-src/src/bt_messages.h +0 -324
  255. package/native-src/src/bt_network.cpp +0 -1007
  256. package/native-src/src/bt_network.h +0 -417
  257. package/native-src/src/bt_peer_connection.cpp +0 -742
  258. package/native-src/src/bt_peer_connection.h +0 -592
  259. package/native-src/src/bt_piece_picker.cpp +0 -786
  260. package/native-src/src/bt_piece_picker.h +0 -473
  261. package/native-src/src/bt_resume_data.cpp +0 -410
  262. package/native-src/src/bt_resume_data.h +0 -249
  263. package/native-src/src/bt_torrent.cpp +0 -2120
  264. package/native-src/src/bt_torrent.h +0 -641
  265. package/native-src/src/bt_torrent_info.cpp +0 -659
  266. package/native-src/src/bt_torrent_info.h +0 -418
  267. package/native-src/src/bt_types.h +0 -621
  268. package/native-src/src/chained_send_buffer.cpp +0 -75
  269. package/native-src/src/chained_send_buffer.h +0 -137
  270. package/native-src/src/crypto/hkdf.c +0 -266
  271. package/native-src/src/crypto/poly1305.h +0 -36
  272. package/native-src/src/dht.cpp +0 -3311
  273. package/native-src/src/dht.h +0 -717
  274. package/native-src/src/disk_io.cpp +0 -632
  275. package/native-src/src/disk_io.h +0 -315
  276. package/native-src/src/file_transfer.cpp +0 -1415
  277. package/native-src/src/file_transfer.h +0 -286
  278. package/native-src/src/fs.h +0 -108
  279. package/native-src/src/gossipsub.cpp +0 -1139
  280. package/native-src/src/gossipsub.h +0 -403
  281. package/native-src/src/ice.cpp +0 -893
  282. package/native-src/src/ice.h +0 -559
  283. package/native-src/src/json.hpp +0 -25526
  284. package/native-src/src/librats.cpp +0 -2378
  285. package/native-src/src/librats.h +0 -2324
  286. package/native-src/src/librats_bittorrent.cpp +0 -601
  287. package/native-src/src/librats_c.cpp +0 -1557
  288. package/native-src/src/librats_c.h +0 -323
  289. package/native-src/src/librats_discovery.cpp +0 -402
  290. package/native-src/src/librats_encryption.cpp +0 -275
  291. package/native-src/src/librats_file_transfer.cpp +0 -144
  292. package/native-src/src/librats_gossipsub.cpp +0 -289
  293. package/native-src/src/librats_ice.cpp +0 -213
  294. package/native-src/src/librats_log_macros.h +0 -36
  295. package/native-src/src/librats_logging.cpp +0 -173
  296. package/native-src/src/librats_mdns.cpp +0 -166
  297. package/native-src/src/librats_persistence.cpp +0 -796
  298. package/native-src/src/librats_portmap.cpp +0 -419
  299. package/native-src/src/librats_reconnection.cpp +0 -218
  300. package/native-src/src/librats_statistic.cpp +0 -105
  301. package/native-src/src/librats_storage.cpp +0 -189
  302. package/native-src/src/rats_export.h +0 -17
  303. package/native-src/src/receive_buffer.cpp +0 -82
  304. package/native-src/src/receive_buffer.h +0 -127
  305. package/native-src/src/socket.h +0 -228
  306. package/native-src/src/threadmanager.cpp +0 -105
  307. package/native-src/src/threadmanager.h +0 -53
  308. package/native-src/src/tracker.cpp +0 -1264
  309. package/native-src/src/tracker.h +0 -319
  310. package/native-src/src/turn.cpp +0 -762
  311. package/native-src/src/turn.h +0 -460
  312. package/native-src/src/wakeup_pipe.h +0 -60
  313. /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