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.
Files changed (319) hide show
  1. package/README.md +145 -331
  2. package/binding.gyp +16 -3
  3. package/lib/index.d.ts +288 -696
  4. package/lib/index.js +407 -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 +404 -179
  8. package/native-src/LICENSE +1 -1
  9. package/native-src/src/librats/bindings/rats.cpp +762 -0
  10. package/native-src/src/librats/bindings/rats.h +380 -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 +43 -0
  73. package/native-src/src/librats/core/types.h +103 -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/circuit_service.h +84 -0
  144. package/native-src/src/librats/node/config.h +110 -0
  145. package/native-src/src/librats/node/dial_service.h +54 -0
  146. package/native-src/src/librats/node/dialer.cpp +264 -0
  147. package/native-src/src/librats/node/dialer.h +188 -0
  148. package/native-src/src/librats/node/host_events.h +26 -0
  149. package/native-src/src/librats/node/identify.cpp +130 -0
  150. package/native-src/src/librats/node/identify.h +71 -0
  151. package/native-src/src/librats/node/nat_status.cpp +103 -0
  152. package/native-src/src/librats/node/nat_status.h +118 -0
  153. package/native-src/src/librats/node/node.cpp +865 -0
  154. package/native-src/src/librats/node/node.h +344 -0
  155. package/native-src/src/librats/node/node_context.h +33 -0
  156. package/native-src/src/librats/node/peer_network.h +91 -0
  157. package/native-src/src/librats/peer/peer.h +49 -0
  158. package/native-src/src/librats/peer/peer_book.cpp +181 -0
  159. package/native-src/src/librats/peer/peer_book.h +88 -0
  160. package/native-src/src/librats/peer/peer_id.cpp +72 -0
  161. package/native-src/src/librats/peer/peer_id.h +62 -0
  162. package/native-src/src/librats/peer/peer_info.h +37 -0
  163. package/native-src/src/librats/peer/peer_table.cpp +170 -0
  164. package/native-src/src/librats/peer/peer_table.h +148 -0
  165. package/native-src/src/librats/security/handshaker.h +66 -0
  166. package/native-src/src/librats/security/identity.h +43 -0
  167. package/native-src/src/librats/security/noise_security.cpp +122 -0
  168. package/native-src/src/librats/security/noise_security.h +37 -0
  169. package/native-src/src/librats/security/plaintext_security.h +106 -0
  170. package/native-src/src/librats/security/session.h +37 -0
  171. package/native-src/src/{storage.cpp → librats/storage/storage.cpp} +369 -522
  172. package/native-src/src/{storage.h → librats/storage/storage.h} +135 -299
  173. package/native-src/src/librats/subsystems/bittorrent.cpp +211 -0
  174. package/native-src/src/librats/subsystems/bittorrent.h +136 -0
  175. package/native-src/src/librats/subsystems/dht_discovery.cpp +202 -0
  176. package/native-src/src/librats/subsystems/dht_discovery.h +123 -0
  177. package/native-src/src/librats/subsystems/dht_service.h +36 -0
  178. package/native-src/src/librats/subsystems/file_transfer.cpp +972 -0
  179. package/native-src/src/librats/subsystems/file_transfer.h +367 -0
  180. package/native-src/src/librats/subsystems/hole_punch.cpp +605 -0
  181. package/native-src/src/librats/subsystems/hole_punch.h +290 -0
  182. package/native-src/src/librats/subsystems/hole_punch_service.h +38 -0
  183. package/native-src/src/librats/subsystems/mdns_discovery.cpp +66 -0
  184. package/native-src/src/librats/subsystems/mdns_discovery.h +55 -0
  185. package/native-src/src/librats/subsystems/message_json.cpp +112 -0
  186. package/native-src/src/librats/subsystems/message_json.h +88 -0
  187. package/native-src/src/librats/subsystems/peer_exchange.cpp +241 -0
  188. package/native-src/src/librats/subsystems/peer_exchange.h +136 -0
  189. package/native-src/src/librats/subsystems/ping_service.cpp +98 -0
  190. package/native-src/src/librats/subsystems/ping_service.h +66 -0
  191. package/native-src/src/librats/subsystems/port_mapping_service.cpp +192 -0
  192. package/native-src/src/librats/subsystems/port_mapping_service.h +84 -0
  193. package/native-src/src/librats/subsystems/pubsub.cpp +567 -0
  194. package/native-src/src/librats/subsystems/pubsub.h +175 -0
  195. package/native-src/src/librats/subsystems/reconnection.cpp +239 -0
  196. package/native-src/src/librats/subsystems/reconnection.h +126 -0
  197. package/native-src/src/librats/subsystems/relay.cpp +1142 -0
  198. package/native-src/src/librats/subsystems/relay.h +211 -0
  199. package/native-src/src/librats/subsystems/relay_service.h +46 -0
  200. package/native-src/src/librats/transport/connection.cpp +343 -0
  201. package/native-src/src/librats/transport/connection.h +283 -0
  202. package/native-src/src/librats/transport/link.h +96 -0
  203. package/native-src/src/librats/transport/reactor.cpp +588 -0
  204. package/native-src/src/librats/transport/reactor.h +262 -0
  205. package/native-src/src/librats/transport/reactor_pool.h +81 -0
  206. package/native-src/src/librats/transport/relay_link.cpp +208 -0
  207. package/native-src/src/librats/transport/relay_link.h +303 -0
  208. package/native-src/src/librats/transport/tcp_link.cpp +49 -0
  209. package/native-src/src/librats/transport/tcp_link.h +43 -0
  210. package/native-src/src/librats/transport/udp_mux.cpp +617 -0
  211. package/native-src/src/librats/transport/udp_mux.h +363 -0
  212. package/native-src/src/librats/transport/udp_packet.cpp +121 -0
  213. package/native-src/src/librats/transport/udp_packet.h +190 -0
  214. package/native-src/src/librats/transport/udp_stream.cpp +1194 -0
  215. package/native-src/src/librats/transport/udp_stream.h +614 -0
  216. package/native-src/src/librats/util/features.h.in +51 -0
  217. package/native-src/src/{fs.cpp → librats/util/fs.cpp} +51 -3
  218. package/native-src/src/librats/util/fs.h +136 -0
  219. package/native-src/src/librats/util/json.cpp +1002 -0
  220. package/native-src/src/librats/util/json.h +444 -0
  221. package/native-src/src/{logger.cpp → librats/util/logger.cpp} +1 -1
  222. package/native-src/src/{logger.h → librats/util/logger.h} +43 -31
  223. package/native-src/src/{network_monitor.cpp → librats/util/network_monitor.cpp} +12 -4
  224. package/native-src/src/{network_monitor.h → librats/util/network_monitor.h} +2 -1
  225. package/native-src/src/{network_utils.cpp → librats/util/network_utils.cpp} +38 -23
  226. package/native-src/src/{network_utils.h → librats/util/network_utils.h} +15 -8
  227. package/native-src/src/{os.cpp → librats/util/os.cpp} +48 -18
  228. package/native-src/src/librats/util/rats_export.h +69 -0
  229. package/native-src/src/{version.cpp → librats/util/version.cpp} +2 -2
  230. package/native-src/src/{version.h.in → librats/util/version.h.in} +1 -1
  231. package/native-src/src/librats/wire/frame.cpp +76 -0
  232. package/native-src/src/librats/wire/frame.h +111 -0
  233. package/native-src/src/librats/wire/message_router.cpp +45 -0
  234. package/native-src/src/librats/wire/message_router.h +47 -0
  235. package/package.json +5 -4
  236. package/scripts/build-librats.js +1 -0
  237. package/scripts/postinstall.js +3 -3
  238. package/scripts/prepare-package.js +4 -4
  239. package/scripts/verify-installation.js +63 -105
  240. package/src/librats_node.cpp +1067 -1323
  241. package/native-src/src/bencode.cpp +0 -485
  242. package/native-src/src/bencode.h +0 -145
  243. package/native-src/src/bittorrent.cpp +0 -14
  244. package/native-src/src/bittorrent.h +0 -74
  245. package/native-src/src/bt_bitfield.cpp +0 -372
  246. package/native-src/src/bt_bitfield.h +0 -316
  247. package/native-src/src/bt_choker.cpp +0 -228
  248. package/native-src/src/bt_choker.h +0 -147
  249. package/native-src/src/bt_client.cpp +0 -1047
  250. package/native-src/src/bt_client.h +0 -445
  251. package/native-src/src/bt_create_torrent.cpp +0 -677
  252. package/native-src/src/bt_create_torrent.h +0 -473
  253. package/native-src/src/bt_extension.cpp +0 -469
  254. package/native-src/src/bt_extension.h +0 -309
  255. package/native-src/src/bt_file_storage.cpp +0 -261
  256. package/native-src/src/bt_file_storage.h +0 -298
  257. package/native-src/src/bt_handshake.cpp +0 -134
  258. package/native-src/src/bt_handshake.h +0 -157
  259. package/native-src/src/bt_messages.cpp +0 -364
  260. package/native-src/src/bt_messages.h +0 -324
  261. package/native-src/src/bt_network.cpp +0 -1007
  262. package/native-src/src/bt_network.h +0 -417
  263. package/native-src/src/bt_peer_connection.cpp +0 -742
  264. package/native-src/src/bt_peer_connection.h +0 -592
  265. package/native-src/src/bt_piece_picker.cpp +0 -786
  266. package/native-src/src/bt_piece_picker.h +0 -473
  267. package/native-src/src/bt_resume_data.cpp +0 -410
  268. package/native-src/src/bt_resume_data.h +0 -249
  269. package/native-src/src/bt_torrent.cpp +0 -2120
  270. package/native-src/src/bt_torrent.h +0 -641
  271. package/native-src/src/bt_torrent_info.cpp +0 -659
  272. package/native-src/src/bt_torrent_info.h +0 -418
  273. package/native-src/src/bt_types.h +0 -621
  274. package/native-src/src/chained_send_buffer.cpp +0 -75
  275. package/native-src/src/chained_send_buffer.h +0 -137
  276. package/native-src/src/crypto/hkdf.c +0 -266
  277. package/native-src/src/crypto/poly1305.h +0 -36
  278. package/native-src/src/dht.cpp +0 -3311
  279. package/native-src/src/dht.h +0 -717
  280. package/native-src/src/disk_io.cpp +0 -632
  281. package/native-src/src/disk_io.h +0 -315
  282. package/native-src/src/file_transfer.cpp +0 -1415
  283. package/native-src/src/file_transfer.h +0 -286
  284. package/native-src/src/fs.h +0 -108
  285. package/native-src/src/gossipsub.cpp +0 -1139
  286. package/native-src/src/gossipsub.h +0 -403
  287. package/native-src/src/ice.cpp +0 -893
  288. package/native-src/src/ice.h +0 -559
  289. package/native-src/src/json.hpp +0 -25526
  290. package/native-src/src/librats.cpp +0 -2378
  291. package/native-src/src/librats.h +0 -2324
  292. package/native-src/src/librats_bittorrent.cpp +0 -601
  293. package/native-src/src/librats_c.cpp +0 -1557
  294. package/native-src/src/librats_c.h +0 -323
  295. package/native-src/src/librats_discovery.cpp +0 -402
  296. package/native-src/src/librats_encryption.cpp +0 -275
  297. package/native-src/src/librats_file_transfer.cpp +0 -144
  298. package/native-src/src/librats_gossipsub.cpp +0 -289
  299. package/native-src/src/librats_ice.cpp +0 -213
  300. package/native-src/src/librats_log_macros.h +0 -36
  301. package/native-src/src/librats_logging.cpp +0 -173
  302. package/native-src/src/librats_mdns.cpp +0 -166
  303. package/native-src/src/librats_persistence.cpp +0 -796
  304. package/native-src/src/librats_portmap.cpp +0 -419
  305. package/native-src/src/librats_reconnection.cpp +0 -218
  306. package/native-src/src/librats_statistic.cpp +0 -105
  307. package/native-src/src/librats_storage.cpp +0 -189
  308. package/native-src/src/rats_export.h +0 -17
  309. package/native-src/src/receive_buffer.cpp +0 -82
  310. package/native-src/src/receive_buffer.h +0 -127
  311. package/native-src/src/socket.h +0 -228
  312. package/native-src/src/threadmanager.cpp +0 -105
  313. package/native-src/src/threadmanager.h +0 -53
  314. package/native-src/src/tracker.cpp +0 -1264
  315. package/native-src/src/tracker.h +0 -319
  316. package/native-src/src/turn.cpp +0 -762
  317. package/native-src/src/turn.h +0 -460
  318. package/native-src/src/wakeup_pipe.h +0 -60
  319. /package/native-src/src/{os.h → librats/util/os.h} +0 -0
@@ -0,0 +1,588 @@
1
+ #include "librats/transport/reactor.h"
2
+ #include "librats/transport/tcp_link.h"
3
+ #include "librats/core/ip_address.h"
4
+ #include "librats/util/logger.h"
5
+ #include "librats/util/network_utils.h"
6
+
7
+ #include <cstring>
8
+ #include <utility>
9
+
10
+ namespace librats {
11
+
12
+ namespace {
13
+
14
+ /// Turn a dial target into the numeric endpoint the datagram transport needs.
15
+ /// A literal is used as-is; a hostname is resolved in the order this socket can
16
+ /// actually use.
17
+ ///
18
+ /// The order matters as much as the result. One mux socket serves every peer and
19
+ /// is bound in one family, which the default configuration makes IPv4 — so asking
20
+ /// for AAAA first, as TCP can afford to, would keep handing back addresses this
21
+ /// socket cannot send to while the same host advertises a perfectly dialable A
22
+ /// record. Answering with a reachable address is the whole job here.
23
+ std::optional<Address> resolve_dial_target(const std::string& host, int port,
24
+ AddressFamily af) {
25
+ if (auto ip = IpAddress::parse(host))
26
+ return Address{*ip, static_cast<uint16_t>(port)};
27
+
28
+ if (af != AddressFamily::IPv4) {
29
+ if (auto ip = IpAddress::parse(network_utils::resolve_hostname_v6(host)))
30
+ return Address{*ip, static_cast<uint16_t>(port)};
31
+ }
32
+ if (af != AddressFamily::IPv6) {
33
+ if (auto ip = IpAddress::parse(network_utils::resolve_hostname(host)))
34
+ return Address{*ip, static_cast<uint16_t>(port)};
35
+ }
36
+ return std::nullopt;
37
+ }
38
+
39
+ } // namespace
40
+
41
+ Reactor::Reactor(uint8_t index, ConnectionDelegate& delegate, SecurityProvider& security)
42
+ : index_(index), delegate_(delegate), security_(security), poller_(IOPoller::create()) {}
43
+
44
+ Reactor::~Reactor() {
45
+ stop();
46
+ }
47
+
48
+ // ── Lifecycle ───────────────────────────────────────────────────────────────
49
+
50
+ void Reactor::listen(socket_t server_socket) {
51
+ server_socket_ = server_socket;
52
+ }
53
+
54
+ void Reactor::listen_udp(socket_t udp_socket, AddressFamily family) {
55
+ mux_ = std::make_unique<UdpMux>(udp_socket, family, *this);
56
+ }
57
+
58
+ void Reactor::start() {
59
+ if (running_.exchange(true)) return;
60
+ thread_ = std::thread(&Reactor::run, this);
61
+ }
62
+
63
+ void Reactor::stop() {
64
+ if (!running_.exchange(false)) {
65
+ if (thread_.joinable()) thread_.join();
66
+ return;
67
+ }
68
+ wakeup_.signal(); // break the poll wait
69
+ if (thread_.joinable()) thread_.join();
70
+ }
71
+
72
+ // ── Work submission ─────────────────────────────────────────────────────────
73
+
74
+ bool Reactor::on_reactor_thread() const noexcept {
75
+ return std::this_thread::get_id() == thread_id_.load(std::memory_order_acquire);
76
+ }
77
+
78
+ void Reactor::post(Task task) {
79
+ // After stop() the loop has exited and (apart from the brief stopping window
80
+ // caught by the final drain in run()) the task will never run. Enqueue anyway
81
+ // so its captures are released at destruction, but flag the dropped work.
82
+ if (!running_.load(std::memory_order_acquire))
83
+ LOG_DEBUG("reactor", "Reactor " << static_cast<int>(index_)
84
+ << " post() after stop; task may not run");
85
+ tasks_.push(std::move(task));
86
+ wakeup_.signal();
87
+ }
88
+
89
+ void Reactor::execute(Task task) {
90
+ if (on_reactor_thread()) task();
91
+ else post(std::move(task));
92
+ }
93
+
94
+ ConnId Reactor::connect(std::string host, int port, TransportKind kind, DialProfile profile) {
95
+ if (kind == TransportKind::Udp && !mux_) {
96
+ LOG_DEBUG("reactor", "Reactor " << static_cast<int>(index_)
97
+ << " has no datagram transport; refusing UDP dial to " << host << ":" << port);
98
+ return kInvalidConnId;
99
+ }
100
+
101
+ // Reserve the id here, on the calling thread, rather than inside the task. The
102
+ // caller can then cancel this exact attempt (close()) before the task has even
103
+ // run — which is what makes racing one transport against the other cancellable
104
+ // instead of a matter of luck.
105
+ const ConnId id = next_conn_id_.fetch_add(1, std::memory_order_relaxed);
106
+ post([this, host = std::move(host), port, kind, profile, id] {
107
+ start_dial(id, host, port, kind, profile);
108
+ });
109
+ return id;
110
+ }
111
+
112
+ void Reactor::start_dial(ConnId id, const std::string& host, int port, TransportKind kind,
113
+ DialProfile profile) {
114
+ if (kind == TransportKind::Udp) {
115
+ const auto target = resolve_dial_target(host, port, mux_->family());
116
+ if (!target) {
117
+ LOG_DEBUG("reactor", "Cannot resolve " << host << " for a UDP dial");
118
+ abort_dial(id, host, port);
119
+ return;
120
+ }
121
+ // The one mux socket cannot leave its own address family, and that is
122
+ // knowable here rather than after the fact. Saying so now costs nothing —
123
+ // the dialer brings the other transport in at once. Opening the stream
124
+ // anyway would spend the Syn's whole give-up on datagrams the kernel
125
+ // refuses one by one, and refusals at that layer are per-packet and
126
+ // silent: a dial that never had a chance would be indistinguishable from
127
+ // a peer that is merely slow to answer.
128
+ if (!family_can_reach(mux_->family(), target->ip.is_v6())) {
129
+ LOG_DEBUG("reactor", "Datagram socket cannot reach " << target->to_string()
130
+ << "; leaving " << host << ":" << port << " to the other transport");
131
+ abort_dial(id, host, port);
132
+ return;
133
+ }
134
+ std::unique_ptr<Link> link = mux_->connect(*target, profile);
135
+ if (!link) { abort_dial(id, host, port); return; }
136
+
137
+ Connection* conn = adopt(std::move(link), ConnRole::Outbound, id);
138
+ if (conn) conn->set_dial_address(host, static_cast<uint16_t>(port));
139
+ return;
140
+ }
141
+
142
+ socket_t sock = tcp_connect_start(host, port);
143
+ if (!is_valid_socket(sock)) {
144
+ LOG_DEBUG("reactor", "Outbound connect to " << host << ":" << port << " failed to start");
145
+ abort_dial(id, host, port);
146
+ return;
147
+ }
148
+ Connection* conn = adopt(std::make_unique<TcpLink>(sock, *this), ConnRole::Outbound, id);
149
+ if (!conn) { close_socket(sock); return; } // the dial was cancelled while queued
150
+ conn->set_dial_address(host, static_cast<uint16_t>(port));
151
+ }
152
+
153
+ void Reactor::adopt_link(ConnId id, std::unique_ptr<Link> link, ConnRole role, bool connected) {
154
+ // A unique_ptr cannot be captured by a std::function, which is what the task
155
+ // queue holds — so the link travels in a shared holder it is moved out of on
156
+ // arrival. If the task is never run (a reactor stopped under us), the holder
157
+ // dies with it and takes the link with it, which is the right outcome.
158
+ auto holder = std::make_shared<std::unique_ptr<Link>>(std::move(link));
159
+ execute([this, id, holder, role, connected] {
160
+ Connection* conn = adopt(std::move(*holder), role, id);
161
+ if (!conn) return;
162
+ // Already-connected links skip the Connecting state entirely, exactly as an
163
+ // accepted socket does; the rest wait for the PollOut that wake() will bring
164
+ // once the far end answers.
165
+ if (connected) conn->start_handshake();
166
+ });
167
+ }
168
+
169
+ void Reactor::wake(ConnId id, uint32_t events) {
170
+ if (events == 0) return;
171
+ execute([this, id, events] { dispatch_events(id, events); });
172
+ }
173
+
174
+ void Reactor::abort_dial(ConnId id, const std::string& host, int port) {
175
+ resolve_dial(id); // the id is spent either way; release any cancellation slot
176
+ delegate_.on_dial_aborted(index_, id, host, static_cast<uint16_t>(port));
177
+ }
178
+
179
+ void Reactor::close(ConnId id, CloseReason reason) {
180
+ close_conn(id, reason, /*spare_established=*/false);
181
+ }
182
+
183
+ void Reactor::cancel_dial(ConnId id, CloseReason reason) {
184
+ close_conn(id, reason, /*spare_established=*/true);
185
+ }
186
+
187
+ void Reactor::close_conn(ConnId id, CloseReason reason, bool spare_established) {
188
+ execute([this, id, reason, spare_established] {
189
+ auto it = conns_.find(id);
190
+ if (it != conns_.end()) {
191
+ // It got there first: the dial it was is over, and what it left behind
192
+ // is a connection to a peer. Whether that one or another link survives
193
+ // is decided by the peer table on both ends alike — see peer_table.cpp.
194
+ if (spare_established && it->second->state() == ConnState::Established) return;
195
+ mark_for_close(id, reason);
196
+ return;
197
+ }
198
+ // Not adopted yet: its connect task is still queued behind this one. Leave
199
+ // a note so adopt() drops it on arrival. Ids at or below the watermark have
200
+ // already come and gone, so they need no slot (and cannot leak one).
201
+ if (id != kInvalidConnId && id > resolved_watermark_) cancelled_dials_.insert(id);
202
+ });
203
+ }
204
+
205
+ void Reactor::broadcast(FrameHeader header, std::shared_ptr<const Bytes> payload) {
206
+ execute([this, header, payload = std::move(payload)] {
207
+ for (auto& [id, conn] : conns_) {
208
+ if (conn->state() == ConnState::Established)
209
+ conn->send(header, ByteView(*payload));
210
+ }
211
+ });
212
+ }
213
+
214
+ // ── Timers ──────────────────────────────────────────────────────────────────
215
+
216
+ TimerId Reactor::schedule(std::chrono::milliseconds delay, Task on_fire) {
217
+ return timers_.schedule(delay, std::move(on_fire));
218
+ }
219
+
220
+ void Reactor::cancel(TimerId id) {
221
+ timers_.cancel(id);
222
+ }
223
+
224
+ // ── Lookups / interest ──────────────────────────────────────────────────────
225
+
226
+ Connection* Reactor::find(ConnId id) noexcept {
227
+ auto it = conns_.find(id);
228
+ return it == conns_.end() ? nullptr : it->second.get();
229
+ }
230
+
231
+ void Reactor::set_interest(socket_t sock, uint32_t events) {
232
+ poller_->modify(sock, events);
233
+ }
234
+
235
+ // ── Reactor loop ────────────────────────────────────────────────────────────
236
+
237
+ void Reactor::run() {
238
+ thread_id_ = std::this_thread::get_id();
239
+ LOG_INFO("reactor", "Reactor " << static_cast<int>(index_)
240
+ << " started (backend: " << poller_->name() << ")");
241
+
242
+ set_socket_nonblocking(wakeup_.fd());
243
+ poller_->add(wakeup_.fd(), PollIn);
244
+ if (is_valid_socket(server_socket_)) {
245
+ set_socket_nonblocking(server_socket_);
246
+ poller_->add(server_socket_, PollIn);
247
+ }
248
+ if (mux_) {
249
+ set_socket_nonblocking(mux_->socket());
250
+ poller_->add(mux_->socket(), PollIn);
251
+ }
252
+
253
+ PollResult events[kMaxEvents];
254
+ std::vector<Task> task_batch;
255
+
256
+ schedule_maintenance();
257
+
258
+ while (running_.load(std::memory_order_relaxed)) {
259
+ int timeout = timers_.next_timeout_ms(kMaxPollMs);
260
+ // The datagram transport keeps its own deadlines — retransmission timeouts,
261
+ // delayed acks, keep-alives — rather than being swept on a fixed cadence, so
262
+ // the wait has to cover the earliest of them too. With no stream due it
263
+ // contributes nothing, and an idle mux costs no wake-ups at all.
264
+ if (mux_) timeout = mux_->next_timeout_ms(timeout);
265
+ const int n = poller_->wait(events, kMaxEvents, timeout);
266
+
267
+ // Empty the wakeup pipe BEFORE taking the task snapshot, never after.
268
+ //
269
+ // post() pushes a task and then writes a byte, and those two arrive here as
270
+ // two separate facts. Draining last would let a byte written *while this
271
+ // turn was running its tasks* be thrown away along with the ones that
272
+ // brought us here — and the task it belonged to was pushed after the
273
+ // snapshot, so it does not run either. It would then sit in the queue with
274
+ // nothing left to announce it, until the next poll timeout woke the loop
275
+ // for some other reason: up to kMaxPollMs of latency, at random.
276
+ //
277
+ // Draining first inverts that. A byte written any time after this line is
278
+ // still in the pipe when the loop comes back round, so wait() returns at
279
+ // once — whether or not its task made this turn's snapshot. The cost is one
280
+ // recv per turn, which is nothing against the work a turn does.
281
+ drain_wakeup();
282
+ drain_tasks(task_batch); // connect/close/send-arm
283
+ for (int i = 0; i < n; ++i) handle_event(events[i]);
284
+ timers_.run_due();
285
+ // Service whatever datagram deadlines have come due. Deliberately not a
286
+ // TimerQueue entry: those deadlines move on nearly every packet, and
287
+ // re-scheduling a timer that often would cost far more than the one
288
+ // comparison this is when nothing is due. Before process_pending_close(),
289
+ // so a stream that dies here is torn down in the same turn.
290
+ if (mux_) mux_->tick();
291
+ // Everything queued by send() during this turn goes to the wire here, in
292
+ // one write per connection rather than one per frame. Deliberately after
293
+ // the mux tick — a handler it dispatched may well have sent something —
294
+ // and before the close pass, so a connection that dies on the write is
295
+ // torn down in the same turn.
296
+ flush_dirty();
297
+ process_pending_close();
298
+ // The mux batches its datagrams; the ones it collected inside a readable
299
+ // event or a tick have already gone out, but an application send reaches a
300
+ // UDP stream from a task, outside any batch of its own. Flushing once here
301
+ // covers those without giving any of them a timer to wait on.
302
+ if (mux_) mux_->flush_output();
303
+ }
304
+
305
+ // Graceful drain: run whatever was queued before/around stop() so in-flight
306
+ // sends flush and explicit closes settle, then tear everything down. (post()
307
+ // may still race in after this; such tasks are dropped — see post().)
308
+ drain_tasks(task_batch);
309
+ flush_dirty(); // in-flight sends leave before the door closes
310
+ process_pending_close();
311
+ shutdown_connections();
312
+ if (mux_) mux_->shutdown(); // after the connections, so no Link outlives it
313
+
314
+ // Timers outlive the loop that scheduled them unless the heap goes with it,
315
+ // and schedule_maintenance() re-arms itself: an entry left behind would start
316
+ // a second self-perpetuating chain on the next run(), so N restarts would cost
317
+ // N+1 sweeps per interval and N+1 timed wake-ups, for as long as the node
318
+ // lives. Whatever else is still pending — establish deadlines, backoffs —
319
+ // belongs to connections that no longer exist.
320
+ timers_.clear();
321
+
322
+ // Hand back the sockets this loop registered above. A registration belongs to a
323
+ // descriptor, and a stopped reactor can be started again on freshly opened ones
324
+ // — which the kernel is free to hand the very same descriptor numbers, and on
325
+ // the BSDs, which allocate the lowest free one, very nearly always does. The
326
+ // backends that keep a registration table of their own (kqueue, poll, IOCP)
327
+ // would then refuse to add a descriptor they believe is already watched, while
328
+ // the kernel forgot it the moment it was closed — leaving a restarted node
329
+ // polling nothing at all: no accepts, no datagrams, every dial to it timing out.
330
+ // epoll drops a closed descriptor by itself, which is why this only ever showed
331
+ // on macOS.
332
+ //
333
+ // The listen sockets themselves are the node's to close, and it does so after
334
+ // joining this thread — so they are still open here, which is exactly when a
335
+ // registration can still be withdrawn cleanly.
336
+ poller_->remove(wakeup_.fd());
337
+ if (is_valid_socket(server_socket_)) {
338
+ poller_->remove(server_socket_);
339
+ // Forgotten as well as unwatched: the next run() must not re-add a
340
+ // descriptor the node closed in between, which by then may name something
341
+ // else entirely. listen() hands over the new one before every start.
342
+ server_socket_ = RATS_INVALID_SOCKET;
343
+ }
344
+ if (mux_) {
345
+ poller_->remove(mux_->socket());
346
+ mux_.reset();
347
+ }
348
+
349
+ LOG_INFO("reactor", "Reactor " << static_cast<int>(index_) << " stopped");
350
+ }
351
+
352
+ void Reactor::drain_tasks(std::vector<Task>& scratch) {
353
+ tasks_.drain(scratch);
354
+ for (auto& task : scratch) task();
355
+ }
356
+
357
+ void Reactor::drain_wakeup() {
358
+ wakeup_.drain();
359
+ }
360
+
361
+ void Reactor::handle_event(const PollResult& ev) {
362
+ const socket_t fd = ev.fd;
363
+
364
+ // Already emptied at the top of the turn — see run(). Nothing to do but
365
+ // recognise it, so it is not mistaken for a connection's socket.
366
+ if (fd == wakeup_.fd()) return;
367
+ if (fd == server_socket_) { if (ev.events & PollIn) do_accept(); return; }
368
+ if (mux_ && fd == mux_->socket()) {
369
+ // Any event, not just PollIn. An error on a datagram socket belongs to one
370
+ // datagram — Windows reports an ICMP complaint about an earlier send on the
371
+ // *next* receive — and not to the socket, which stays usable; the cure is
372
+ // therefore to read, which surfaces or clears it and drains whatever queued
373
+ // up behind it. Treating PollErr as "nothing to do" would strand every
374
+ // datagram after it, since nothing else here ever re-arms this socket.
375
+ //
376
+ // Stopped short of draining: come straight back rather than waiting for the
377
+ // next datagram to re-arm an edge-triggered poller.
378
+ if (mux_->on_readable()) wakeup_.signal();
379
+ return;
380
+ }
381
+
382
+ auto it = fd_to_conn_.find(fd);
383
+ if (it == fd_to_conn_.end()) return;
384
+ dispatch_events(it->second, ev.events);
385
+ }
386
+
387
+ void Reactor::dispatch_events(ConnId id, uint32_t events) {
388
+ Connection* conn = find(id);
389
+ if (!conn) return;
390
+ if (conn->state() == ConnState::Closing || conn->state() == ConnState::Closed) return;
391
+ // Condemned earlier this turn, just not torn down yet — process_pending_close()
392
+ // runs at the end of the turn, so events polled alongside the one that closed it
393
+ // would otherwise still be delivered, and the connection could change state
394
+ // *after* the decision to close it was taken against the state it had.
395
+ //
396
+ // Which is not merely wasted work on bytes about to be dropped. cancel_dial()
397
+ // spares an attempt that has established; let one finish its handshake here,
398
+ // just after that check found it still handshaking, and it reaches the peer
399
+ // table as a duplicate and supersedes the sibling that beat it — while itself
400
+ // still standing condemned. Both are then gone at the end of the turn and the
401
+ // peer is lost, which is the very failure the cancel/close split exists to
402
+ // prevent. The map is empty on every turn that closes nothing, nearly all of them.
403
+ if (!pending_close_.empty() && pending_close_.count(id)) return;
404
+
405
+ bool keep = true;
406
+ if (events & (PollErr | PollHup)) {
407
+ keep = conn->on_error();
408
+ } else {
409
+ if (keep && (events & PollIn)) keep = conn->on_readable();
410
+ if (keep && (events & PollOut)) keep = conn->on_writable();
411
+ }
412
+
413
+ if (!keep) mark_for_close(id, conn->close_reason());
414
+ }
415
+
416
+ void Reactor::dispatch_link_events(ConnId id, uint32_t events) {
417
+ dispatch_events(id, events);
418
+ }
419
+
420
+ void Reactor::schedule_maintenance() {
421
+ timers_.schedule(kMaintenanceInterval, [this] {
422
+ // Connections being torn down this tick are still in conns_; the sweep is
423
+ // read-only with respect to the map, and on_maintenance_tick() never closes.
424
+ for (auto& [id, conn] : conns_) conn->on_maintenance_tick();
425
+ schedule_maintenance();
426
+ });
427
+ }
428
+
429
+ void Reactor::do_accept() {
430
+ // Level-triggered: drain all pending connections this tick. Use a raw,
431
+ // non-logging accept — accept_client() logs an error on every EWOULDBLOCK,
432
+ // which is the normal "no more pending" signal here.
433
+ while (true) {
434
+ socket_t client = ::accept(server_socket_, nullptr, nullptr);
435
+ if (!is_valid_socket(client)) break; // EWOULDBLOCK / error → done this tick
436
+ suppress_sigpipe(client); // not inherited from the listener; raw accept()
437
+
438
+ // Admission control: refuse new inbound peers when at capacity, before
439
+ // paying any handshake cost. Keep draining the backlog so the listener
440
+ // doesn't stay readable. The precise per-peer cap is enforced again at
441
+ // on_established (this coarse gate can't yet know the remote's identity).
442
+ if (!delegate_.admit_inbound()) {
443
+ close_socket(client);
444
+ continue;
445
+ }
446
+
447
+ Connection* conn = adopt(std::make_unique<TcpLink>(client, *this), ConnRole::Inbound,
448
+ next_conn_id_.fetch_add(1, std::memory_order_relaxed));
449
+ if (!conn) { close_socket(client); continue; }
450
+ conn->start_handshake(); // accepted sockets are already connected
451
+ }
452
+ }
453
+
454
+ ConnId Reactor::adopt_inbound_link(std::unique_ptr<Link> link) {
455
+ // Same coarse admission gate the TCP listener applies, for the same reason:
456
+ // turn a flood away before any handshake work is done for it.
457
+ if (!delegate_.admit_inbound()) return kInvalidConnId;
458
+
459
+ const ConnId id = next_conn_id_.fetch_add(1, std::memory_order_relaxed);
460
+ Connection* conn = adopt(std::move(link), ConnRole::Inbound, id);
461
+ if (!conn) return kInvalidConnId;
462
+ conn->start_handshake(); // an accepted stream is already connected
463
+ return id;
464
+ }
465
+
466
+ bool Reactor::resolve_dial(ConnId id) {
467
+ if (id > resolved_watermark_) resolved_watermark_ = id;
468
+ return cancelled_dials_.erase(id) > 0;
469
+ }
470
+
471
+ Connection* Reactor::adopt(std::unique_ptr<Link> link, ConnRole role, ConnId id) {
472
+ // A dial cancelled while its task sat in the queue never becomes a connection;
473
+ // dropping `link` here is what actually calls the attempt off.
474
+ if (resolve_dial(id)) return nullptr;
475
+
476
+ const socket_t fd = link->fd();
477
+ if (is_valid_socket(fd)) {
478
+ set_socket_nonblocking(fd);
479
+ // Only a TCP link has a socket of its own, so this reaches exactly the
480
+ // sockets Nagle applies to. Safe to do here and nowhere else because the
481
+ // send queue now aggregates on our side — see set_tcp_nodelay().
482
+ set_tcp_nodelay(fd);
483
+ }
484
+
485
+ auto conn = std::make_unique<Connection>(id, std::move(link), role, *this, delegate_);
486
+ Connection* raw = conn.get();
487
+ raw->link().attach(id);
488
+
489
+ conns_.emplace(id, std::move(conn));
490
+ conn_count_.fetch_add(1, std::memory_order_relaxed);
491
+
492
+ if (is_valid_socket(fd)) {
493
+ fd_to_conn_.emplace(fd, id);
494
+ // Inbound sockets are connected: watch for readable. Outbound sockets are
495
+ // still connecting: watch for writable, which signals connect completion.
496
+ poller_->add(fd, role == ConnRole::Inbound ? PollIn : PollOut);
497
+ }
498
+
499
+ // Reap connections that never reach Established (stuck connect or handshake).
500
+ TimerId timer = timers_.schedule(kEstablishTimeout, [this, id] {
501
+ auto it = conns_.find(id);
502
+ if (it == conns_.end()) return;
503
+ const ConnState st = it->second->state();
504
+ if (st != ConnState::Established && st != ConnState::Closing && st != ConnState::Closed) {
505
+ mark_for_close(id, st == ConnState::Connecting ? CloseReason::ConnectFailed
506
+ : CloseReason::HandshakeFailed);
507
+ }
508
+ });
509
+ raw->set_establish_timer(timer);
510
+ return raw;
511
+ }
512
+
513
+ // ── Teardown ────────────────────────────────────────────────────────────────
514
+
515
+ void Reactor::mark_for_close(ConnId id, CloseReason reason) {
516
+ pending_close_.emplace(id, reason); // first reason wins
517
+ }
518
+
519
+ void Reactor::flush_dirty() {
520
+ // Swapped out first: a flush can close a connection, and a close can queue
521
+ // further work — none of which should extend the batch being walked.
522
+ //
523
+ // Looped, because a flush can also *book another one*: writing a relayed
524
+ // circuit hands its bytes to the connection carrying it (see
525
+ // transport/relay_link.h), which books its own flush from inside this one. A
526
+ // single pass would leave those bytes queued until the next turn of the loop —
527
+ // i.e. behind a poll wait — adding tens of milliseconds to every hop of a
528
+ // relayed path. The bound is a backstop against a pathological chain, not an
529
+ // expected limit: honest work needs two passes at most.
530
+ std::vector<ConnId> batch;
531
+ for (int pass = 0; pass < kMaxFlushPasses && !dirty_.empty(); ++pass) {
532
+ batch.clear();
533
+ batch.swap(dirty_);
534
+ for (const ConnId id : batch) {
535
+ Connection* conn = find(id);
536
+ if (!conn) continue; // gone since it booked
537
+ if (conn->state() != ConnState::Established) continue; // closing; nothing to owe
538
+ if (!conn->flush_pending()) mark_for_close(id, conn->close_reason());
539
+ }
540
+ }
541
+ }
542
+
543
+ void Reactor::process_pending_close() {
544
+ if (pending_close_.empty()) return;
545
+ auto batch = std::move(pending_close_);
546
+ pending_close_.clear();
547
+ for (const auto& [id, reason] : batch) remove(id, reason);
548
+ }
549
+
550
+ void Reactor::remove(ConnId id, CloseReason reason) {
551
+ auto it = conns_.find(id);
552
+ if (it == conns_.end()) return;
553
+
554
+ std::unique_ptr<Connection> conn = std::move(it->second);
555
+ conns_.erase(it);
556
+
557
+ const socket_t fd = conn->socket();
558
+ if (is_valid_socket(fd)) {
559
+ fd_to_conn_.erase(fd);
560
+ poller_->remove(fd);
561
+ }
562
+ conn->cancel_establish_timer(); // stop the reaper before this fd can be reused
563
+ conn_count_.fetch_sub(1, std::memory_order_relaxed);
564
+
565
+ LOG_DEBUG("reactor", "Connection " << id << " closed (" << to_string(reason) << ")");
566
+ conn->shutdown_link(reason); // let the transport say goodbye / flush
567
+ delegate_.on_closed(*conn, reason);
568
+ if (is_valid_socket(fd)) close_socket(fd);
569
+ // ~Connection here releases the Link, which is what hands a UDP stream back to
570
+ // the mux (to linger briefly or go straight away).
571
+ }
572
+
573
+ void Reactor::shutdown_connections() {
574
+ for (auto& [id, conn] : conns_) {
575
+ const socket_t fd = conn->socket();
576
+ if (is_valid_socket(fd)) poller_->remove(fd);
577
+ conn->shutdown_link(CloseReason::ReactorShutdown);
578
+ delegate_.on_closed(*conn, CloseReason::ReactorShutdown);
579
+ if (is_valid_socket(fd)) close_socket(fd);
580
+ }
581
+ conns_.clear();
582
+ fd_to_conn_.clear();
583
+ pending_close_.clear();
584
+ cancelled_dials_.clear();
585
+ conn_count_.store(0, std::memory_order_relaxed);
586
+ }
587
+
588
+ } // namespace librats