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