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,1194 @@
1
+ #include "librats/transport/udp_stream.h"
2
+ #include "librats/core/io_poller.h" // PollIn / PollOut / PollErr
3
+ #include "librats/util/logger.h"
4
+
5
+ #include <algorithm>
6
+ #include <cstring>
7
+
8
+ namespace librats {
9
+
10
+ namespace {
11
+
12
+ using Clock = UdpStream::Clock;
13
+
14
+ /// Sentinel for "no delayed ack is armed".
15
+ constexpr Clock::time_point kNoDeadline{};
16
+
17
+ template <typename A, typename B>
18
+ Clock::duration clamp_duration(Clock::duration v, A lo, B hi) {
19
+ const auto low = std::chrono::duration_cast<Clock::duration>(lo);
20
+ const auto high = std::chrono::duration_cast<Clock::duration>(hi);
21
+ return v < low ? low : (v > high ? high : v);
22
+ }
23
+
24
+ } // namespace
25
+
26
+ UdpStream::UdpStream(UdpStreamHost& host, const Address& remote, uint32_t recv_id,
27
+ uint32_t send_id, ConnRole role, Clock::time_point now,
28
+ DialProfile profile)
29
+ : host_(host), remote_(remote), recv_id_(recv_id), send_id_(send_id), role_(role),
30
+ state_(role == ConnRole::Outbound ? State::SynSent : State::Connected),
31
+ last_recv_(now), last_send_(now) {
32
+ pace_last_ = now;
33
+ pace_tokens_ = kPaceMinBurst; // nothing to pace against until the first RTT sample
34
+ // Seeded rather than left at the epoch: a sentinel there would collide with a
35
+ // clock whose zero is a real instant, which is exactly what a test driving
36
+ // virtual time hands us.
37
+ last_data_send_ = now;
38
+ hystart_reset();
39
+
40
+ if (role_ != ConnRole::Outbound) return;
41
+
42
+ // The dial's own retry shape (see DialProfile). Clamped rather than trusted:
43
+ // zero attempts would be a stream that dies on its first timeout with nothing
44
+ // ever sent twice, and an interval outside the RTO bounds would either spin the
45
+ // timer or stall the dial past the establish deadline above it.
46
+ syn_attempts_ = (std::max)(1, profile.syn_attempts);
47
+ syn_backoff_ = profile.syn_backoff;
48
+ rto_ = clamp_duration(std::chrono::milliseconds(profile.syn_rto_ms), kMinRto, kMaxRto);
49
+
50
+ // The dial itself is just the first packet of the stream: a Syn occupies a
51
+ // sequence number like any other, so the ordinary retransmission machinery
52
+ // covers a lost dial with no special case — only a tighter attempt cap (see
53
+ // kSynMaxAttempts), so a UDP-blocked path gives up quickly enough for the
54
+ // dialer to fall back to TCP.
55
+ OutPacket syn = new_packet(rudp::PacketType::Syn);
56
+ syn.seq = next_seq_++;
57
+ sent_.push_back(std::move(syn));
58
+ transmit(sent_.back(), now);
59
+ }
60
+
61
+ // ── Outbound ────────────────────────────────────────────────────────────────
62
+
63
+ UdpStream::OutPacket UdpStream::new_packet(rudp::PacketType type) {
64
+ OutPacket pkt;
65
+ pkt.type = type;
66
+ if (!spare_.empty()) {
67
+ pkt.buf = std::move(spare_.back());
68
+ spare_.pop_back();
69
+ pkt.buf.clear(); // capacity survives; the bytes do not
70
+ } else {
71
+ pkt.buf.reserve(rudp::kMaxDatagram); // headroom + a full payload, allocated once
72
+ }
73
+ pkt.buf.resize(rudp::kMaxHeaderSize); // reserve the headroom transmit() writes into
74
+ return pkt;
75
+ }
76
+
77
+ void UdpStream::recycle(OutPacket& pkt) {
78
+ if (spare_.size() >= kMaxSpareBuffers) return;
79
+ pkt.buf.clear();
80
+ spare_.push_back(std::move(pkt.buf));
81
+ }
82
+
83
+ uint16_t UdpStream::advertised_window() const noexcept {
84
+ // What we are still willing to buffer: the reorder slots a gap is holding,
85
+ // plus whatever the connection has not read out of the in-order buffer yet.
86
+ const size_t used = reorder_.size() + inbox_.size() / rudp::kMaxPayload;
87
+ if (used >= rudp::kMaxWindowPackets) return 0;
88
+ return static_cast<uint16_t>(rudp::kMaxWindowPackets - used);
89
+ }
90
+
91
+ void UdpStream::fill_common(rudp::Packet& p) const {
92
+ p.conn_id = send_id_;
93
+ p.window = advertised_window();
94
+ // Cumulative: the highest sequence number received with no gap before it.
95
+ // recv_next_ is the first one still missing, so the ack is the one before it
96
+ // (0 while nothing has arrived — sequence numbers start at 1).
97
+ p.ack = recv_next_ - 1;
98
+
99
+ if (reorder_.empty()) return;
100
+
101
+ const uint32_t bits = sack_bitmap();
102
+ if (bits != 0) {
103
+ p.flags |= rudp::FlagSack;
104
+ p.sack = bits;
105
+ }
106
+ }
107
+
108
+ uint32_t UdpStream::sack_bitmap() const noexcept {
109
+ if (!sack_dirty_) return sack_bits_;
110
+
111
+ // Selective ack: bit i covers ack+2+i, i.e. the 32 packets that follow the
112
+ // hole at recv_next_. Derived from the reorder buffer and recv_next_ alone, so
113
+ // it is rebuilt only when one of those moves — not on every packet sent.
114
+ uint32_t bits = 0;
115
+ for (uint32_t i = 0; i < rudp::kSackBits; ++i)
116
+ if (reorder_.count(recv_next_ + 1 + i)) bits |= (1u << i);
117
+
118
+ sack_bits_ = bits;
119
+ sack_dirty_ = false;
120
+ return bits;
121
+ }
122
+
123
+ void UdpStream::transmit(OutPacket& pkt, Clock::time_point now) {
124
+ rudp::Packet p;
125
+ p.type = pkt.type;
126
+ fill_common(p); // may raise FlagSack, which is what decides the header length
127
+ p.seq = pkt.seq;
128
+
129
+ // The payload is already sitting in pkt.buf behind kMaxHeaderSize bytes of
130
+ // headroom, so the header goes in immediately ahead of it and the datagram
131
+ // leaves as one contiguous range. Nothing is copied here — not on the first
132
+ // transmission, and not on any retransmission.
133
+ const size_t hdr = rudp::header_size(p);
134
+ uint8_t* const start = pkt.buf.data() + (rudp::kMaxHeaderSize - hdr);
135
+ rudp::encode_header(p, start);
136
+
137
+ host_.send_datagram(remote_, start, hdr + pkt.size());
138
+
139
+ // Everything that reaches the wire is metered, retransmissions included: a
140
+ // repair loads the bottleneck exactly as new data does, and a pacer that
141
+ // ignored repairs would let a recovering stream burst precisely when the path
142
+ // has just proved it cannot take one. Only *new* data is gated by the pacer
143
+ // though (see can_transmit) — holding a retransmission back would stall the
144
+ // recovery the timeout just started.
145
+ const uint64_t on_wire = hdr + pkt.size();
146
+ pace_tokens_ = pace_tokens_ > on_wire ? pace_tokens_ - on_wire : 0;
147
+
148
+ // These bytes are on the path now. A retransmission of a packet that was never
149
+ // given up on adds nothing — it is the same bytes travelling again, not more of
150
+ // them — which is why this is a transition rather than an addition.
151
+ if (!pkt.in_flight) {
152
+ pkt.in_flight = true;
153
+ flight_bytes_ += pkt.size();
154
+ }
155
+ // RFC 6298 (5.1): something is outstanding, so the timer has to be running —
156
+ // set to whichever of the probe and the timeout comes first (see loss_timeout).
157
+ if (rto_deadline_ == kNoDeadline) rto_deadline_ = now + loss_timeout();
158
+
159
+ pkt.sends++;
160
+ pkt.sent_at = now;
161
+ last_send_ = now;
162
+ // Data, not a bare acknowledgement — this is the clock the idle-restart rule
163
+ // reads, and the keep-alive must not be allowed to keep resetting it.
164
+ last_data_send_ = now;
165
+
166
+ // Every packet carries the ack field, so sending one settles whatever
167
+ // acknowledgement was owed — the delayed-ack timer exists precisely to give
168
+ // this a chance to happen.
169
+ need_ack_ = false;
170
+ unacked_packets_ = 0;
171
+ ack_due_ = kNoDeadline;
172
+ }
173
+
174
+ void UdpStream::send_control(rudp::PacketType type, Clock::time_point now) {
175
+ uint8_t buf[rudp::kMaxDatagram];
176
+
177
+ rudp::Packet p;
178
+ p.type = type;
179
+ fill_common(p);
180
+ // A control packet consumes no sequence number: it carries the next one we
181
+ // *will* use, purely so a peer can see where the stream stands. Nothing
182
+ // retransmits it — a lost ack is repaired by the next one.
183
+ p.seq = next_seq_;
184
+
185
+ host_.send_datagram(remote_, buf, rudp::encode(p, buf));
186
+
187
+ last_send_ = now;
188
+ need_ack_ = false;
189
+ unacked_packets_ = 0;
190
+ ack_due_ = kNoDeadline;
191
+ }
192
+
193
+ bool UdpStream::cwnd_allows(size_t bytes) const noexcept {
194
+ // Always allow one packet out when the path is idle. This keeps a stream from
195
+ // deadlocking when the congestion window shrinks below a single packet — a
196
+ // window is an estimate of what the path will carry, and an estimate that has
197
+ // collapsed to nothing still has to be able to take a sample. It says nothing
198
+ // about the *receiver's* window, which is not an estimate at all and is
199
+ // enforced ahead of this, in window_allows().
200
+ if (flight_bytes_ == 0) return true;
201
+ return flight_bytes_ + bytes <= cwnd_;
202
+ }
203
+
204
+ bool UdpStream::window_allows() const noexcept {
205
+ if (state_ != State::Connected) return false; // nothing may overtake the Syn
206
+ if (unsent_.empty()) return false;
207
+
208
+ // A receiver that advertises zero has said it will buffer nothing more, and
209
+ // that is absolute: it is the only bound on the memory one peer can make
210
+ // another spend on it, so an empty pipe is no licence to send anyway. It would
211
+ // not stay a single probe if it were — the packet is ordinary in-order data,
212
+ // the peer acknowledges it and holds it, the pipe empties, and the next one
213
+ // follows on its heels. What looks like a persist probe is then a steady
214
+ // trickle that fills the receiver far past the window it advertised, bounded
215
+ // in the end by the sender's own queue rather than by anything the receiver
216
+ // said.
217
+ //
218
+ // Nothing is needed from this side to get going again: a receiver whose reader
219
+ // drains a full buffer announces the re-opened window at once and unprompted
220
+ // (see read(), and UdpStreamLink::read for the wake-up that carries it), and
221
+ // its keep-alive carries the window if that announcement is lost.
222
+ if (peer_window_ == 0) return false;
223
+
224
+ if (sent_.empty()) return true;
225
+ if (sent_.size() >= peer_window_) return false;
226
+ if (sent_.size() >= rudp::kMaxWindowPackets) return false;
227
+ return cwnd_allows(unsent_.front().size());
228
+ }
229
+
230
+ bool UdpStream::can_transmit() const noexcept {
231
+ if (!window_allows()) return false;
232
+ return pace_allows(unsent_.front().size());
233
+ }
234
+
235
+ // ── Pacing ──────────────────────────────────────────────────────────────────
236
+
237
+ uint64_t UdpStream::pace_window() const noexcept {
238
+ // No round-trip estimate, no rate: there is nothing to derive a pace from,
239
+ // and the window at that point is four packets, which cannot hurt anyone.
240
+ if (!have_rtt_ || srtt_ <= Clock::duration::zero()) return 0;
241
+
242
+ // The cautious phase grows at a quarter of slow start, so it does not need
243
+ // slow start's doubling headroom.
244
+ const bool doubling = in_slow_start() && !css_;
245
+ const uint32_t num = doubling ? kPaceGainSlowStartNum : kPaceGainSteadyNum;
246
+ const uint32_t den = doubling ? kPaceGainSlowStartDen : kPaceGainSteadyDen;
247
+ return static_cast<uint64_t>(cwnd_) * num / den;
248
+ }
249
+
250
+ uint64_t UdpStream::pace_bytes_over(Clock::duration dt) const noexcept {
251
+ const uint64_t window = pace_window();
252
+ if (window == 0 || dt <= Clock::duration::zero()) return 0;
253
+
254
+ // window * dt / srtt. The caller clamps dt to a second and a window is at
255
+ // most ~1.2 MB, so the product stays four orders of magnitude inside 64 bits.
256
+ const auto dt_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(dt).count();
257
+ const auto srtt_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(srtt_).count();
258
+ if (srtt_ns <= 0) return 0;
259
+ return window * static_cast<uint64_t>(dt_ns) / static_cast<uint64_t>(srtt_ns);
260
+ }
261
+
262
+ void UdpStream::pace_accrue(Clock::time_point now) {
263
+ auto dt = now - pace_last_;
264
+ if (dt < Clock::duration::zero()) dt = Clock::duration::zero();
265
+ // The bucket is capped a few lines below, so integrating over a long silence
266
+ // buys nothing — and the clamp is what keeps the multiplication finite.
267
+ const auto cap = std::chrono::duration_cast<Clock::duration>(std::chrono::seconds(1));
268
+ if (dt > cap) dt = cap;
269
+ pace_last_ = now;
270
+
271
+ if (pace_window() == 0) {
272
+ // Not pacing yet. Keep the bucket at the floor rather than at zero, so
273
+ // the first packet after the first round-trip sample is not held back by
274
+ // an empty bucket the stream never had a chance to fill.
275
+ pace_tokens_ = kPaceMinBurst;
276
+ return;
277
+ }
278
+
279
+ pace_tokens_ += pace_bytes_over(dt);
280
+ const uint64_t burst = (std::max)(static_cast<uint64_t>(kPaceMinBurst),
281
+ pace_bytes_over(kPaceQuantum));
282
+ if (pace_tokens_ > burst) pace_tokens_ = burst;
283
+ }
284
+
285
+ bool UdpStream::pace_allows(size_t bytes) const noexcept {
286
+ if (pace_window() == 0) return true; // no estimate to pace against
287
+ // Never hold back a stream with an empty pipe. That covers the lone packet a
288
+ // recovering stream is allowed and the first packet after an idle period —
289
+ // neither of which can congest anything, and both of which would deadlock if a
290
+ // rate derived from an empty pipe were allowed to refuse them.
291
+ if (flight_bytes_ == 0) return true;
292
+ return pace_tokens_ >= bytes;
293
+ }
294
+
295
+ UdpStream::Clock::duration UdpStream::pace_wait(size_t bytes) const noexcept {
296
+ const uint64_t window = pace_window();
297
+ if (window == 0 || pace_tokens_ >= bytes) return Clock::duration::zero();
298
+
299
+ const uint64_t deficit = static_cast<uint64_t>(bytes) - pace_tokens_;
300
+ const auto srtt_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(srtt_).count();
301
+ if (srtt_ns <= 0) return Clock::duration::zero();
302
+
303
+ // The inverse of pace_bytes_over(): how long this many bytes take to accrue.
304
+ const uint64_t wait_ns = deficit * static_cast<uint64_t>(srtt_ns) / window;
305
+ return std::chrono::duration_cast<Clock::duration>(std::chrono::nanoseconds(wait_ns));
306
+ }
307
+
308
+ void UdpStream::restart_after_idle(Clock::time_point now) {
309
+ // Only with the pipe genuinely empty. While anything is outstanding the
310
+ // window describes a path we are actively measuring, and is not stale.
311
+ if (flight_bytes_ != 0) return;
312
+ if (cwnd_ <= kInitialCwnd) return; // nothing to give back
313
+
314
+ const auto idle = now - last_data_send_;
315
+ if (idle < rto_) return;
316
+
317
+ // RFC 2861. A congestion window is a measurement, and one nobody has
318
+ // validated for a round trip is a guess about a path we stopped watching. The
319
+ // shape of P2P traffic makes this the common case rather than the corner one
320
+ // — silence, a burst of gossip, silence — and without this the burst goes out
321
+ // at a rate justified by what the path looked like ten seconds ago.
322
+ //
323
+ // Halve once per timeout of silence, down to the window a fresh stream would
324
+ // start with. ssthresh is deliberately untouched: it records where this path
325
+ // congested, and going quiet does not make that wrong — keeping it is what
326
+ // lets the stream climb back in slow start instead of re-running the whole
327
+ // discovery from scratch.
328
+ const auto rto_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(rto_).count();
329
+ const auto idle_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(idle).count();
330
+ const uint64_t halvings = rto_ns > 0 ? static_cast<uint64_t>(idle_ns / rto_ns) : 32;
331
+
332
+ cwnd_ = halvings >= 32 ? kInitialCwnd
333
+ : (std::max)(cwnd_ >> halvings, kInitialCwnd);
334
+
335
+ // Consume the silence that was just paid for. Without this the *same* elapsed
336
+ // time is re-read on every tick and halves an already-halved window again, so
337
+ // a window would decay per tick rather than per timeout — far faster than
338
+ // intended, and enough to undo a healthy window during an ordinary lull.
339
+ last_data_send_ += Clock::duration(rto_ * static_cast<Clock::rep>(halvings));
340
+
341
+ // The bucket describes a rate that no longer applies either. Zeroing it (and
342
+ // the clock behind it) means the stream releases the one packet an empty pipe
343
+ // always allows and then paces the rest at the restarted rate, instead of
344
+ // spending a burst allowance earned while it was silent.
345
+ pace_tokens_ = 0;
346
+ pace_last_ = now;
347
+ }
348
+
349
+ void UdpStream::retransmit_lost(Clock::time_point now) {
350
+ // The flag keeps this off the hot path entirely: without it the scan below
351
+ // would walk the whole retransmission queue on every acknowledgement of a
352
+ // healthy transfer — a window's worth of packets, every time — to discover
353
+ // that there is nothing to repair.
354
+ if (!have_lost_) return;
355
+
356
+ // Packets a timeout has given up on: still owed to the peer, no longer counted
357
+ // against the window. They are re-sent from the front, under whatever the
358
+ // congestion window currently allows — so the pipe refills at the rate the
359
+ // recovering window dictates instead of all at once, and a hole is always
360
+ // repaired before anything queued behind it is sent.
361
+ for (OutPacket& pkt : sent_) {
362
+ if (pkt.acked || pkt.in_flight) continue; // the peer has it, or it is already back out
363
+ if (pkt.sends == 0) continue; // never sent; pump() owns it
364
+ if (!cwnd_allows(pkt.size())) return; // more still owed: come back with a bigger window
365
+ transmit(pkt, now);
366
+ ++retransmits_;
367
+ }
368
+ have_lost_ = false; // the scan reached the end, so nothing is waiting to go back out
369
+ }
370
+
371
+ void UdpStream::pump(Clock::time_point now) {
372
+ // A window that has gone unused for a round trip is stale before anything
373
+ // else here reads it, so this comes first.
374
+ restart_after_idle(now);
375
+ pace_accrue(now);
376
+
377
+ // Repairs first: what the peer is missing blocks everything queued behind it,
378
+ // so spending the window on new data before the hole is filled would only grow
379
+ // the peer's reorder buffer.
380
+ retransmit_lost(now);
381
+
382
+ while (can_transmit()) {
383
+ OutPacket pkt = std::move(unsent_.front());
384
+ unsent_.pop_front();
385
+ pkt.seq = next_seq_++;
386
+
387
+ sent_.push_back(std::move(pkt));
388
+ transmit(sent_.back(), now); // this is what puts it in flight
389
+ }
390
+
391
+ // If the loop stopped and every window would still have allowed the next
392
+ // packet, the pacer is the only thing holding it — and a pacer is released by
393
+ // time, not by an acknowledgement, so it needs a deadline of its own. When a
394
+ // window is what stopped us there is deliberately no timer: the ack that
395
+ // opens it is what wakes the stream, and arming one here would spin.
396
+ pace_due_ = Clock::time_point{};
397
+ if (window_allows()) {
398
+ const auto wait = pace_wait(unsent_.front().size());
399
+ if (wait > Clock::duration::zero()) pace_due_ = now + wait;
400
+ }
401
+ }
402
+
403
+ size_t UdpStream::write(const ByteView* slices, size_t count, Clock::time_point now) {
404
+ if (state_ != State::Connected || fin_queued_) return 0;
405
+
406
+ size_t budget = queued_bytes_ >= kSendQueueLimit ? 0 : kSendQueueLimit - queued_bytes_;
407
+ if (budget == 0) return 0;
408
+
409
+ size_t taken = 0;
410
+ for (size_t i = 0; i < count && budget > 0; ++i) {
411
+ const uint8_t* src = slices[i].data();
412
+ size_t left = slices[i].size();
413
+ while (left > 0 && budget > 0) {
414
+ // Pack into the tail packet while it has room, so a burst of small
415
+ // frames leaves as one datagram instead of one datagram each. Only a
416
+ // Data packet can be topped up — a queued Fin closes the stream and
417
+ // must stay the last thing in the queue.
418
+ if (unsent_.empty() || unsent_.back().type != rudp::PacketType::Data ||
419
+ unsent_.back().space() == 0) {
420
+ unsent_.push_back(new_packet(rudp::PacketType::Data));
421
+ }
422
+ OutPacket& tail = unsent_.back();
423
+
424
+ const size_t n = (std::min)({left, tail.space(), budget});
425
+ tail.buf.insert(tail.buf.end(), src, src + n);
426
+ src += n;
427
+ left -= n;
428
+ budget -= n;
429
+ taken += n;
430
+ queued_bytes_ += n;
431
+ }
432
+ }
433
+
434
+ pump(now);
435
+ return taken;
436
+ }
437
+
438
+ void UdpStream::begin_close(Clock::time_point now) {
439
+ if (state_ != State::Connected || fin_queued_) return;
440
+ fin_queued_ = true;
441
+
442
+ unsent_.push_back(new_packet(rudp::PacketType::Fin));
443
+ pump(now);
444
+ }
445
+
446
+ void UdpStream::abort(Clock::time_point now) {
447
+ if (state_ != State::Dead) send_control(rudp::PacketType::Reset, now);
448
+ die(CloseReason::LocalClose);
449
+ events_ = 0; // the connection asked for this; it does not need telling
450
+ }
451
+
452
+ // ── Inbound ─────────────────────────────────────────────────────────────────
453
+
454
+ void UdpStream::on_packet(const rudp::Packet& p, Clock::time_point now) {
455
+ if (state_ == State::Dead) return;
456
+
457
+ last_recv_ = now;
458
+
459
+ if (p.type == rudp::PacketType::Reset) {
460
+ LOG_DEBUG("udp", "Stream " << recv_id_ << " reset by " << remote_.to_string());
461
+ die(CloseReason::PeerReset);
462
+ flush_events();
463
+ return;
464
+ }
465
+
466
+ // Handled before anything else reads the header: a Retry comes from a responder
467
+ // that is holding no state for us at all, so its window and sequence number
468
+ // describe nothing and must not be folded into what we believe about the peer.
469
+ if (p.type == rudp::PacketType::Retry) {
470
+ handle_retry(p, now);
471
+ flush_events();
472
+ return;
473
+ }
474
+
475
+ bool ack_now = (p.type == rudp::PacketType::Syn || p.type == rudp::PacketType::Fin);
476
+
477
+ handle_ack(p, now);
478
+
479
+ // The Syn is the first entry in the retransmission queue, so the moment it is
480
+ // no longer there the dial has been answered and the stream is up.
481
+ if (state_ == State::SynSent &&
482
+ (sent_.empty() || sent_.front().type != rudp::PacketType::Syn)) {
483
+ state_ = State::Connected;
484
+ raise(PollOut);
485
+ LOG_DEBUG("udp", "Stream " << recv_id_ << " connected to " << remote_.to_string());
486
+ }
487
+
488
+ if (p.type == rudp::PacketType::Syn || p.type == rudp::PacketType::Data ||
489
+ p.type == rudp::PacketType::Fin) {
490
+ // The peer is sending again, so it is not stopped on a window we re-opened
491
+ // and there is nothing left to announce. A bare acknowledgement deliberately
492
+ // does not count: a sender stuck on a stale zero window still keep-alives,
493
+ // and taking that as proof would call off the very repeats meant for it.
494
+ window_announces_ = 0;
495
+
496
+ const uint32_t before = recv_next_;
497
+ handle_sequenced(p);
498
+ // A packet that did not fill the gap it was expected to means the peer is
499
+ // missing something: say so at once rather than waiting out the delayed
500
+ // ack, since that ack is what triggers its fast retransmit.
501
+ if (recv_next_ == before) ack_now = true;
502
+ }
503
+
504
+ pump(now);
505
+
506
+ // The connection asked to be told when it could write again, and an ack just
507
+ // freed queue space.
508
+ if (want_write_ && state_ == State::Connected && queued_bytes_ < kSendQueueLimit)
509
+ raise(PollOut);
510
+
511
+ if (need_ack_) {
512
+ // Acknowledge every second packet even without a hole (the classic
513
+ // ack-every-other-segment rule), so a bulk sender's window keeps opening
514
+ // without a round trip's worth of delay per packet.
515
+ if (ack_now || unacked_packets_ >= 2) send_control(rudp::PacketType::Ack, now);
516
+ else if (ack_due_ == kNoDeadline) ack_due_ = now + kDelayedAck;
517
+ }
518
+
519
+ flush_events();
520
+ }
521
+
522
+ void UdpStream::handle_ack(const rudp::Packet& p, Clock::time_point now) {
523
+ // An ack past the highest sequence number we have ever assigned is nonsense;
524
+ // honouring it would retire packets that were never sent.
525
+ if (rudp::seq_less(next_seq_ - 1, p.ack)) return;
526
+
527
+ // What the receiver will still buffer — taken only from a packet that is not
528
+ // from the past. A path that duplicates or reorders a datagram hands back a
529
+ // window from before the one we are already acting on, and latching that would
530
+ // stop a sender the receiver has since made room for. With no probe left to
531
+ // discover the mistake (see window_allows) the stream would then sit until the
532
+ // peer's next keep-alive, ten seconds of silence bought by one stale packet.
533
+ //
534
+ // The cumulative acknowledgement is what dates them: it never moves backwards
535
+ // at the peer, so one that has moved backwards arrived out of order. Only
536
+ // *strictly* older is refused — a retransmission carries a header built when it
537
+ // was sent, so its window is current even though the packet is not, and TCP's
538
+ // stricter reading of this (SND.WL1) would throw that away. Two packets sharing
539
+ // an ack are genuinely indistinguishable here, which is what the receiver's
540
+ // repeated announcement covers from the other end (see read()).
541
+ //
542
+ // Deliberately below the check above rather than in on_packet(): a forged or
543
+ // corrupt ack from the future must not be allowed to set the mark, or every
544
+ // legitimate update after it would look stale and the window would freeze for
545
+ // the life of the stream.
546
+ if (!rudp::seq_less(p.ack, window_ack_)) {
547
+ peer_window_ = p.window;
548
+ window_ack_ = p.ack;
549
+ }
550
+
551
+ size_t newly_acked = 0;
552
+ size_t retired = 0; ///< packets the cumulative ack removed from the queue
553
+ while (!sent_.empty() && rudp::seq_le(sent_.front().seq, p.ack)) {
554
+ OutPacket& front = sent_.front();
555
+ // Karn's rule: a packet that was retransmitted cannot say which copy this
556
+ // ack answers, so it contributes no round-trip sample. Nor does one that a
557
+ // selective ack already retired — its real round trip was over when that
558
+ // SACK arrived, and measuring it against this later cumulative ack would
559
+ // inflate the estimate by however long the hole in front of it took to
560
+ // fill, which is exactly when a *tight* RTO matters most.
561
+ if (front.sends == 1 && !front.acked) sample_rtt(now - front.sent_at);
562
+
563
+ if (front.in_flight) {
564
+ flight_bytes_ -= front.size();
565
+ front.in_flight = false;
566
+ }
567
+ // A packet a selective ack already retired left the queue's accounting then;
568
+ // one a timeout gave up on left only the *flight* accounting, and still owes
569
+ // its bytes to queued_bytes_ — which is why the two are settled separately.
570
+ if (!front.acked) {
571
+ queued_bytes_ -= front.size();
572
+ newly_acked += front.size();
573
+ }
574
+ recycle(front);
575
+ sent_.pop_front();
576
+ ++retired;
577
+ }
578
+
579
+ // The episode ends once everything that was outstanding when the loss was
580
+ // detected has been acknowledged (the NewReno recovery point). Until then the
581
+ // window has already been reduced for it and must not be reduced again.
582
+ if (in_recovery_ && rudp::seq_le(recover_seq_, p.ack)) in_recovery_ = false;
583
+
584
+ if (p.has_sack() && !sent_.empty()) {
585
+ // Every packet in the queue occupies exactly one sequence number, so the
586
+ // packet a bit refers to is found by subtraction rather than by search.
587
+ const uint32_t base = p.ack + 2;
588
+ for (uint32_t i = 0; i < rudp::kSackBits; ++i) {
589
+ if ((p.sack & (1u << i)) == 0) continue;
590
+ const int32_t idx = rudp::seq_diff(base + i, sent_.front().seq);
591
+ if (idx < 0 || static_cast<size_t>(idx) >= sent_.size()) continue;
592
+ OutPacket& pkt = sent_[static_cast<size_t>(idx)];
593
+ if (pkt.acked) continue;
594
+ pkt.acked = true;
595
+ if (pkt.in_flight) {
596
+ flight_bytes_ -= pkt.size();
597
+ pkt.in_flight = false;
598
+ }
599
+ queued_bytes_ -= pkt.size();
600
+ }
601
+ }
602
+
603
+ // Selective acks name what got through, which by elimination names what did
604
+ // not. Repairing those now is the difference between recovering a lossy
605
+ // window in one round trip and unpicking it one duplicate-ack at a time.
606
+ if (p.has_sack()) repair_sacked_holes(now);
607
+
608
+ if (newly_acked > 0) {
609
+ dup_acks_ = 0;
610
+ tail_probes_ = 0; // the peer answered; this silence is over
611
+ last_ack_recv_ = p.ack;
612
+ grow_window(newly_acked);
613
+ // After the window moves, not before: the round-trip rise HyStart++ acts
614
+ // on is only meaningful against the window that produced it.
615
+ hystart_on_ack(p.ack);
616
+ // Progress means the path is alive: drop back to the estimated RTO,
617
+ // undoing any doubling a previous timeout applied.
618
+ if (have_rtt_) rto_ = clamp_duration(srtt_ + 4 * rttvar_, kMinRto, kMaxRto);
619
+ } else if (p.type == rudp::PacketType::Ack && p.ack == last_ack_recv_ && p.ack != 0 &&
620
+ !sent_.empty()) {
621
+ // A *pure* ack whose cumulative number stood still: the peer is receiving
622
+ // packets past a hole and re-reporting the same edge. Three of those is the
623
+ // classic loss signal, and repairing it now saves a whole retransmission
624
+ // timeout.
625
+ //
626
+ // The type check is what makes this a loss signal rather than a coincidence,
627
+ // and RFC 5681 defines a duplicate ack that way for exactly this reason. Every
628
+ // packet here carries the ack field, so on a two-way stream the peer's own
629
+ // Data rides over the same number until our next packet reaches it — three of
630
+ // *those* say nothing about loss, they only say the peer had something of its
631
+ // own to send. Counting them halved the window and re-sent a packet that was
632
+ // merely still in flight, on a stream where nothing had been dropped at all —
633
+ // which on a peer-to-peer link (gossip during a transfer, any request while a
634
+ // response streams back) is the normal case rather than the corner one.
635
+ //
636
+ // Nothing is lost by being strict: a hole makes the receiver acknowledge at
637
+ // once (see on_packet), so a one-way flow still produces the pure acks this
638
+ // counts, and a two-way one is covered by repair_sacked_holes() above, which
639
+ // names the missing packets outright instead of inferring them.
640
+ if (++dup_acks_ == 3) {
641
+ enter_recovery();
642
+ transmit(sent_.front(), now);
643
+ ++retransmits_;
644
+ }
645
+ }
646
+
647
+ // RFC 6298 (5.2/5.3): the timer restarts whenever the cumulative ack retires
648
+ // something, and stops once nothing is outstanding. Counted in packets rather
649
+ // than bytes, because a Syn and a Fin each occupy a sequence number while
650
+ // carrying no payload — a byte count would leave the timer running on the
651
+ // deadline the *handshake* set, which is neither the one the first data packet
652
+ // deserves nor one anything else will correct.
653
+ if (retired > 0) rto_deadline_ = sent_.empty() ? kNoDeadline : now + loss_timeout();
654
+ }
655
+
656
+ void UdpStream::handle_retry(const rudp::Packet& p, Clock::time_point now) {
657
+ // "Not until you prove you are really at that address." A responder under load
658
+ // answers a dial with a cookie instead of a stream, and will not spend a byte of
659
+ // memory on us until it comes back. Only a dial that has not been answered yet
660
+ // can be retried, and only once — see retried_.
661
+ if (state_ != State::SynSent || retried_) return;
662
+ if (p.payload.size() != rudp::kCookieSize) return;
663
+ if (sent_.empty() || sent_.front().type != rudp::PacketType::Syn) return;
664
+
665
+ retried_ = true;
666
+ OutPacket& syn = sent_.front();
667
+
668
+ // The same Syn, same sequence number, now carrying the cookie. Its payload was
669
+ // empty until now, and the send accounting has to learn about the bytes: the
670
+ // cumulative ack that eventually retires this packet subtracts size() from both
671
+ // counters, so anything that grows a queued packet must add to them first.
672
+ syn.buf.resize(rudp::kMaxHeaderSize);
673
+ syn.buf.insert(syn.buf.end(), p.payload.begin(), p.payload.end());
674
+ queued_bytes_ += rudp::kCookieSize;
675
+ // Only if the packet is currently counted as in flight: if a timeout has just
676
+ // given up on it, transmit() below will count the whole grown packet afresh,
677
+ // and adding the difference here as well would count the cookie twice.
678
+ if (syn.in_flight) flight_bytes_ += rudp::kCookieSize;
679
+
680
+ // The round trip we just spent proving our address is not a lost packet, so it
681
+ // does not count against the dial's attempt budget — and the dial gets a fresh
682
+ // timeout to answer in, rather than what was left of the first one.
683
+ syn.sends = 0;
684
+ rto_deadline_ = now + rto_;
685
+ transmit(syn, now);
686
+
687
+ LOG_DEBUG("udp", "Stream " << recv_id_ << " re-dialing " << remote_.to_string()
688
+ << " with an address-validation cookie");
689
+ }
690
+
691
+ void UdpStream::repair_sacked_holes(Clock::time_point now) {
692
+ // Everything before the highest selectively acknowledged packet has had its
693
+ // chance: the peer received something sent *after* it, so it is not merely
694
+ // late. Three packets of margin is the usual allowance for reordering — the
695
+ // same threshold the duplicate-ack rule uses, for the same reason.
696
+ //
697
+ // The search is bounded by the reach of a selective ack rather than by the
698
+ // length of the queue. A bit can only ever mark the kSackBits packets after
699
+ // the hole — handle_ack() resolves bit i to index (p.ack + 2 + i) - front,
700
+ // and the cumulative retire above leaves front at exactly p.ack + 1, so the
701
+ // highest index it can set is kSackBits — and an index only ever moves *down*
702
+ // as the queue drains from the front. So nothing past that window can carry
703
+ // the flag, and walking the whole queue to discover it (a full window of
704
+ // packets, on every acknowledgement of a loss episode — which is when they
705
+ // are at their most frequent) was searching where the answer cannot be.
706
+ // Backwards inside that window, so the common case returns on the first hit.
707
+ const size_t limit = (std::min)(sent_.size(), size_t{rudp::kSackBits} + 1);
708
+ int highest_acked = -1;
709
+ for (size_t i = limit; i-- > 0;) {
710
+ if (sent_[i].acked) { highest_acked = static_cast<int>(i); break; }
711
+ }
712
+ if (highest_acked < 3) return;
713
+
714
+ const auto spacing = (std::max)(std::chrono::duration_cast<Clock::duration>(kMinRepairSpacing),
715
+ srtt_);
716
+
717
+ int repaired = 0;
718
+ for (int i = 0; i + 3 <= highest_acked && repaired < kMaxRepairsPerAck; ++i) {
719
+ OutPacket& pkt = sent_[static_cast<size_t>(i)];
720
+ if (pkt.acked) continue;
721
+ if (now - pkt.sent_at < spacing) continue; // already re-sent very recently
722
+ transmit(pkt, now);
723
+ ++retransmits_;
724
+ ++repaired;
725
+ }
726
+ // One window reduction for the whole episode — not one per packet repaired,
727
+ // and not one per ack that repairs something. Several selective acks arrive
728
+ // per round trip and recovery spans several round trips, so halving on each
729
+ // of them would drive the window to the floor over a loss TCP would have
730
+ // ridden out with a single halving. enter_recovery() enforces that.
731
+ if (repaired > 0) enter_recovery();
732
+ }
733
+
734
+ void UdpStream::handle_sequenced(const rudp::Packet& p) {
735
+ need_ack_ = true;
736
+
737
+ if (rudp::seq_less(p.seq, recv_next_)) return; // already delivered; just re-ack
738
+
739
+ // Only Data carries stream content. A Syn occupies a sequence number like any
740
+ // other packet, but what it carries is the address-validation cookie the mux
741
+ // has already checked — delivering that as stream bytes would splice four bytes
742
+ // of nonsense into the front of the peer's handshake.
743
+ const ByteView body = (p.type == rudp::PacketType::Data) ? p.payload : ByteView{};
744
+
745
+ if (p.seq == recv_next_) {
746
+ ++unacked_packets_;
747
+ deliver(body, p.type == rudp::PacketType::Fin);
748
+ ++recv_next_;
749
+ sack_dirty_ = true; // the bitmap is relative to recv_next_, which just moved
750
+ drain_reorder();
751
+ return;
752
+ }
753
+
754
+ // Past the gap: hold it, but only within the window we advertised — anything
755
+ // beyond that is a peer ignoring flow control, and buffering it would let one
756
+ // peer decide how much memory we spend.
757
+ const int32_t ahead = rudp::seq_diff(p.seq, recv_next_);
758
+ if (ahead <= 0 || ahead >= rudp::kMaxWindowPackets) return;
759
+ if (reorder_.count(p.seq)) return;
760
+
761
+ InPacket held;
762
+ held.payload = body.to_bytes();
763
+ held.fin = (p.type == rudp::PacketType::Fin);
764
+ reorder_.emplace(p.seq, std::move(held));
765
+ sack_dirty_ = true;
766
+ }
767
+
768
+ void UdpStream::deliver(ByteView payload, bool fin) {
769
+ if (peer_fin_) return; // nothing follows a Fin
770
+
771
+ if (!payload.empty()) {
772
+ const ByteSpan into = inbox_.prepare(payload.size());
773
+ std::memcpy(into.data(), payload.data(), payload.size());
774
+ inbox_.commit(payload.size());
775
+ raise(PollIn);
776
+ }
777
+ if (fin) {
778
+ peer_fin_ = true;
779
+ raise(PollIn); // the reader has to see the end of stream
780
+ }
781
+ }
782
+
783
+ void UdpStream::drain_reorder() {
784
+ for (;;) {
785
+ auto it = reorder_.find(recv_next_);
786
+ if (it == reorder_.end()) break;
787
+ deliver(ByteView(it->second.payload), it->second.fin);
788
+ reorder_.erase(it);
789
+ ++recv_next_;
790
+ sack_dirty_ = true;
791
+ }
792
+ }
793
+
794
+ size_t UdpStream::read(uint8_t* into, size_t len) {
795
+ const size_t n = (std::min)(len, inbox_.size());
796
+ if (n == 0) return 0;
797
+
798
+ std::memcpy(into, inbox_.data(), n);
799
+ const uint16_t before = advertised_window();
800
+ inbox_.consume(n);
801
+ // Draining the buffer may have re-opened a window we had advertised as full.
802
+ // The peer is waiting on that number, so it has to be told without waiting for
803
+ // traffic that will never come while it is stopped — hence an owed ack with no
804
+ // deadline attached, which the next tick() sends outright rather than holding
805
+ // for company (see there). Leaving ack_due_ unset is the *signal*, not an
806
+ // omission: everything else that owes an ack has a packet of its own to wait for.
807
+ //
808
+ // And again after that, a few times, if the peer stays quiet. Nothing
809
+ // retransmits a bare acknowledgement, and the sender this one is for is stopped
810
+ // — so were it dropped, the only thing left to restart the transfer would be
811
+ // the keep-alive ten seconds out. The repeats also cover the one case the
812
+ // sender cannot: two packets carrying the same acknowledgement and different
813
+ // windows, which it has no way to tell apart (see handle_ack). Cleared as soon
814
+ // as the peer sends anything sequenced, which is proof it heard us.
815
+ if (before == 0 && advertised_window() > 0) {
816
+ need_ack_ = true;
817
+ window_announces_ = kMaxWindowAnnounces;
818
+ window_due_ = kNoDeadline; // the first one goes at once
819
+ }
820
+ return n;
821
+ }
822
+
823
+ // ── Timing, congestion control, lifecycle ───────────────────────────────────
824
+
825
+ void UdpStream::sample_rtt(Clock::duration rtt) {
826
+ if (rtt < Clock::duration::zero()) return;
827
+
828
+ hystart_sample(rtt);
829
+
830
+ if (!have_rtt_) {
831
+ srtt_ = rtt;
832
+ rttvar_ = rtt / 2;
833
+ have_rtt_ = true;
834
+ } else {
835
+ // RFC 6298: rttvar = 3/4 rttvar + 1/4 |srtt - r| ; srtt = 7/8 srtt + 1/8 r.
836
+ const auto err = srtt_ > rtt ? srtt_ - rtt : rtt - srtt_;
837
+ rttvar_ = (rttvar_ * 3 + err) / 4;
838
+ srtt_ = (srtt_ * 7 + rtt) / 8;
839
+ }
840
+ rto_ = clamp_duration(srtt_ + 4 * rttvar_, kMinRto, kMaxRto);
841
+ }
842
+
843
+ void UdpStream::enter_recovery() {
844
+ if (in_recovery_) return; // already paid for this episode
845
+ in_recovery_ = true;
846
+ // Everything assigned a sequence number so far is what has to be acknowledged
847
+ // before the episode is over. next_seq_ is the number the *next* packet will
848
+ // take, so the highest one outstanding is one below it.
849
+ recover_seq_ = next_seq_ - 1;
850
+ on_loss(false);
851
+ }
852
+
853
+ void UdpStream::on_loss(bool timeout) {
854
+ ++window_reductions_;
855
+ // A loss settles the question the cautious phase was asking, and settles it
856
+ // the expensive way. Whatever slow start is entered from here starts its
857
+ // round-trip comparison afresh rather than against a queue that has since
858
+ // drained.
859
+ css_ = false;
860
+ if (timeout) {
861
+ // A timeout says the path is congested enough to have dropped everything
862
+ // in flight: back down to one packet and re-probe from there.
863
+ ssthresh_ = (std::max)(static_cast<uint32_t>(flight_bytes_ / 2), kMinCwnd);
864
+ cwnd_ = rudp::kMaxPayload;
865
+ } else {
866
+ // A fast retransmit means packets are still flowing, so halve rather than
867
+ // collapse.
868
+ ssthresh_ = (std::max)(cwnd_ / 2, kMinCwnd);
869
+ cwnd_ = ssthresh_;
870
+ }
871
+ }
872
+
873
+ UdpStream::Clock::duration UdpStream::probe_timeout() const noexcept {
874
+ if (!have_rtt_) return kInitialRto;
875
+
876
+ // RFC 9002's PTO: a round trip, the variance allowance, and the time the peer
877
+ // is entitled to hold an acknowledgement back for. The last term is what stops
878
+ // the probe racing our own delayed-ack rule and calling a slow peer a lost one.
879
+ const auto granularity = std::chrono::duration_cast<Clock::duration>(kMinProbeTimeout) / 8;
880
+ auto pto = srtt_ + (std::max)(4 * rttvar_, granularity)
881
+ + std::chrono::duration_cast<Clock::duration>(kDelayedAck);
882
+
883
+ // Doubled per consecutive probe: if the first went unanswered the path is
884
+ // worse than the estimate said, and asking again at the same spacing would
885
+ // just be asking twice.
886
+ for (int i = 0; i < tail_probes_ && pto < std::chrono::duration_cast<Clock::duration>(kMaxRto); ++i)
887
+ pto *= 2;
888
+
889
+ return clamp_duration(pto, kMinProbeTimeout, kMaxRto);
890
+ }
891
+
892
+ UdpStream::Clock::duration UdpStream::loss_timeout() const noexcept {
893
+ if (state_ != State::Connected || tail_probes_ >= kMaxTailProbes) return rto_;
894
+
895
+ // Never later than the timeout it stands in front of. A probe exists to ask
896
+ // the question *sooner* and more cheaply than the retransmission timeout would
897
+ // — on a path whose round trip is long enough that the probe interval exceeds
898
+ // the timeout, waiting for the probe would be pure added delay. Capped, the
899
+ // worst case is that it fires exactly when the timeout would have, and the
900
+ // stream still keeps its window instead of collapsing it.
901
+ return (std::min)(probe_timeout(), rto_);
902
+ }
903
+
904
+ void UdpStream::on_rto(Clock::time_point now) {
905
+ // Find the oldest packet the peer has not confirmed. A selectively acknowledged
906
+ // one at the front would mean the peer already has it, so it is not what the
907
+ // timeout is about.
908
+ OutPacket* oldest = nullptr;
909
+ for (OutPacket& pkt : sent_) {
910
+ if (!pkt.acked) { oldest = &pkt; break; }
911
+ }
912
+ if (!oldest || oldest->sends == 0) {
913
+ rto_deadline_ = kNoDeadline; // nothing outstanding; the timer has no work
914
+ return;
915
+ }
916
+
917
+ const int cap = (state_ == State::SynSent) ? syn_attempts_ : kMaxRetransmits;
918
+ if (oldest->sends >= cap) {
919
+ die(state_ == State::SynSent ? CloseReason::ConnectFailed : CloseReason::PeerReset);
920
+ return;
921
+ }
922
+
923
+ // Tail loss probe, before any of the collapse below is believed. The peer has
924
+ // gone quiet, which on a stream whose last packet was lost is indistinguishable
925
+ // from a peer that is merely slow — and the two call for opposite responses.
926
+ // So ask first: re-send something it has not acknowledged, change nothing else,
927
+ // and let the answer decide. A probe that was unnecessary costs one packet;
928
+ // the collapse below, taken wrongly, costs the whole window.
929
+ //
930
+ // Deliberately not for a dial: a Syn has its own, tighter attempt budget that
931
+ // the transport race depends on (see kSynMaxAttempts).
932
+ if (state_ == State::Connected && tail_probes_ < kMaxTailProbes) {
933
+ // The LAST unacknowledged packet, not the first (RFC 8985 §7.2). This is
934
+ // the whole mechanism, not a detail of it: the probe's acknowledgement has
935
+ // to land *past* every hole in front of it, so the receiver holds it out of
936
+ // order and the selective ack that comes back names all of them at once —
937
+ // which is what lets one round trip repair a whole lost burst.
938
+ //
939
+ // Probing the front instead produces an acknowledgement that advances the
940
+ // cumulative number by exactly one and names nothing. Each probe would then
941
+ // recover a single packet, and because that acknowledgement is new data it
942
+ // resets tail_probes_ — so the escalation below is never reached, the rest
943
+ // of the burst stays counted in flight with the window shut behind it, and
944
+ // grow_window() walks the window *up* through what is in fact a total loss.
945
+ // For a single lost packet the two are the same packet and the difference
946
+ // does not show; for a lost burst it is the difference between repairing in
947
+ // one round trip and crawling out one packet per probe.
948
+ OutPacket* probe = oldest;
949
+ for (auto it = sent_.rbegin(); it != sent_.rend(); ++it) {
950
+ if (!it->acked) { probe = &*it; break; }
951
+ }
952
+
953
+ ++tail_probes_;
954
+ transmit(*probe, now);
955
+ ++retransmits_;
956
+ rto_deadline_ = now + loss_timeout(); // backed off, still capped by the RTO
957
+ return;
958
+ }
959
+
960
+ // A timeout is the stronger signal and always collapses the window, even
961
+ // mid-recovery — but it also restarts the episode, so the selective acks that
962
+ // come back as the pipe refills do not each take another halving out of a
963
+ // window that is already down to one packet. (on_loss reads flight_bytes_, so
964
+ // it has to run before the accounting below is undone.)
965
+ on_loss(true);
966
+ in_recovery_ = true;
967
+ recover_seq_ = next_seq_ - 1;
968
+
969
+ // Everything outstanding has had a full retransmission timeout to arrive and
970
+ // nothing acknowledged it, so it is presumed lost and stops occupying the path.
971
+ //
972
+ // This step is what makes recovery possible at all. Leaving the bytes counted
973
+ // would leave flight_bytes_ holding a whole window while cwnd_ is back down to
974
+ // one packet, and cwnd_allows() — the gate every transmission goes through —
975
+ // would refuse for as long as those packets sat in the queue. The sender would
976
+ // then crawl forward one packet per timeout, unable to grow the window or
977
+ // repair the rest, until the transfer effectively stopped.
978
+ for (OutPacket& pkt : sent_) {
979
+ if (!pkt.in_flight) continue;
980
+ pkt.in_flight = false;
981
+ flight_bytes_ -= pkt.size();
982
+ have_lost_ = true;
983
+ }
984
+
985
+ // Exponential backoff, so a path that is down is probed ever more cheaply
986
+ // instead of being hammered. The deadline is set from it before anything goes
987
+ // out, so the retransmissions below do not each restart the timer.
988
+ //
989
+ // A punching dial is the one case that opts out (see DialProfile): there the
990
+ // peer's NAT is expected to swallow the early Syns, so backing off would spread
991
+ // the few attempts we get across seconds and leave the window in which both
992
+ // sides are actually probing barely covered. Only the dial can ask for this —
993
+ // an established stream always backs off.
994
+ if (syn_backoff_ || state_ != State::SynSent)
995
+ rto_ = clamp_duration(rto_ * 2, kMinRto, kMaxRto);
996
+ rto_deadline_ = now + rto_;
997
+
998
+ retransmit_lost(now);
999
+ }
1000
+
1001
+ void UdpStream::hystart_reset() {
1002
+ css_ = false;
1003
+ css_rounds_ = 0;
1004
+ css_baseline_rtt_ = (Clock::duration::max)();
1005
+ round_min_rtt_ = (Clock::duration::max)();
1006
+ prev_round_min_rtt_ = (Clock::duration::max)();
1007
+ round_samples_ = 0;
1008
+ round_end_ = next_seq_ - 1;
1009
+ }
1010
+
1011
+ void UdpStream::hystart_sample(Clock::duration rtt) {
1012
+ // The *minimum* of the round, not the average: a queue building in front of
1013
+ // the bottleneck lifts even the luckiest packet's round trip, where an
1014
+ // average moves just as readily for one straggler that had nothing to do
1015
+ // with congestion.
1016
+ if (rtt < round_min_rtt_) round_min_rtt_ = rtt;
1017
+ ++round_samples_;
1018
+ }
1019
+
1020
+ void UdpStream::hystart_on_ack(uint32_t ack) {
1021
+ // Only slow start is in question. Congestion avoidance has already found its
1022
+ // ceiling and climbs a packet per round trip, which no queue signal improves.
1023
+ if (!in_slow_start()) { css_ = false; return; }
1024
+
1025
+ // Entry: this round's best round trip has risen clear of the previous round's
1026
+ // by more than the threshold, which means a queue is forming ahead of us.
1027
+ // Leave doubling for the cautious phase rather than exiting outright — one
1028
+ // round's rise may be noise, and CSS is how that gets settled without
1029
+ // throwing the remaining growth away.
1030
+ if (!css_ && round_samples_ >= kHyRttSamples &&
1031
+ prev_round_min_rtt_ != (Clock::duration::max)() &&
1032
+ round_min_rtt_ != (Clock::duration::max)()) {
1033
+ const auto thresh = clamp_duration(prev_round_min_rtt_ / 8,
1034
+ kHyMinRttThresh, kHyMaxRttThresh);
1035
+ if (round_min_rtt_ >= prev_round_min_rtt_ + thresh) {
1036
+ css_ = true;
1037
+ css_rounds_ = 0;
1038
+ css_baseline_rtt_ = round_min_rtt_;
1039
+ }
1040
+ }
1041
+
1042
+ if (!rudp::seq_le(round_end_, ack)) return; // the round is still running
1043
+
1044
+ const auto ended_min = round_min_rtt_;
1045
+ prev_round_min_rtt_ = ended_min;
1046
+ round_min_rtt_ = (Clock::duration::max)();
1047
+ round_samples_ = 0;
1048
+ round_end_ = next_seq_ - 1;
1049
+
1050
+ if (!css_) return;
1051
+
1052
+ // The rise did not hold — it was one round's noise. Take the caution back and
1053
+ // let slow start carry on, which is the whole reason CSS exists.
1054
+ if (ended_min != (Clock::duration::max)() && ended_min < css_baseline_rtt_) {
1055
+ css_ = false;
1056
+ return;
1057
+ }
1058
+
1059
+ // It held long enough to believe. Stop doubling *here*, at a window the path
1060
+ // demonstrably carries — this is the point of the exercise: the ceiling is
1061
+ // found without having had to lose a window to find it.
1062
+ if (++css_rounds_ >= kHyCssRounds) {
1063
+ ssthresh_ = cwnd_;
1064
+ css_ = false;
1065
+ }
1066
+ }
1067
+
1068
+ void UdpStream::grow_window(size_t acked_bytes) {
1069
+ if (cwnd_ < ssthresh_) {
1070
+ uint32_t inc = static_cast<uint32_t>((std::min)(acked_bytes, size_t{rudp::kMaxPayload} * 2));
1071
+ // The cautious phase probes at a quarter of slow start: still upward, so a
1072
+ // spurious signal costs almost nothing, but no longer doubling while it is
1073
+ // being decided whether the round-trip rise was a real queue.
1074
+ if (css_) inc /= kHyCssGrowthDivisor;
1075
+ cwnd_ += inc;
1076
+ } else {
1077
+ // Additive increase: one packet per window, spread over the acks that
1078
+ // make up that window.
1079
+ const uint64_t inc = static_cast<uint64_t>(rudp::kMaxPayload) * acked_bytes / cwnd_;
1080
+ cwnd_ += static_cast<uint32_t>(inc > 0 ? inc : 1);
1081
+ }
1082
+ cwnd_ = (std::min)(cwnd_, kMaxCwnd);
1083
+ }
1084
+
1085
+ std::optional<Clock::time_point> UdpStream::next_deadline() const noexcept {
1086
+ // A dead stream has no timers left to run; the mux collects it instead of
1087
+ // servicing it, so it asks for no wake-up at all.
1088
+ if (state_ == State::Dead) return std::nullopt;
1089
+
1090
+ // The idle deadline is the one thing always armed: silence from the peer ends
1091
+ // the stream whatever else is or is not outstanding.
1092
+ Clock::time_point due = last_recv_ + kIdleTimeout;
1093
+
1094
+ // An owed acknowledgement. The epoch here is not "no deadline" but "at once" —
1095
+ // read() leaves it that way to ask for a window update, and a peer stopped on a
1096
+ // zero window sends nothing for the ack to ride on. Returning the epoch makes
1097
+ // that the earliest possible deadline, which is exactly what it means.
1098
+ if (need_ack_) due = (std::min)(due, ack_due_);
1099
+
1100
+ // A re-opened window still waiting to be heard back on. Same convention as the
1101
+ // owed ack above — the count is what arms this, so the epoch means "at once".
1102
+ if (window_announces_ > 0) due = (std::min)(due, window_due_);
1103
+
1104
+ if (rto_deadline_ != kNoDeadline) due = (std::min)(due, rto_deadline_);
1105
+
1106
+ // A packet the pacer is holding back. Unlike everything else here this one is
1107
+ // released by the clock alone — no acknowledgement is coming to free it — so
1108
+ // without this deadline a paced stream would sit until the keep-alive.
1109
+ if (pace_due_ != kNoDeadline) due = (std::min)(due, pace_due_);
1110
+
1111
+ // Keep-alive: says we are still here, and keeps a NAT's mapping for this port
1112
+ // open. Only meaningful once the stream is up — a Syn in flight is covered by
1113
+ // the retransmission timeout above.
1114
+ if (state_ == State::Connected) due = (std::min)(due, last_send_ + kKeepAlive);
1115
+
1116
+ return due;
1117
+ }
1118
+
1119
+ void UdpStream::tick(Clock::time_point now) {
1120
+ if (state_ == State::Dead) return;
1121
+
1122
+ if (now - last_recv_ >= kIdleTimeout) {
1123
+ LOG_DEBUG("udp", "Stream " << recv_id_ << " to " << remote_.to_string()
1124
+ << " idle for " << kIdleTimeout.count() << "s; closing");
1125
+ die(CloseReason::IdleTimeout);
1126
+ flush_events();
1127
+ return;
1128
+ }
1129
+
1130
+ if (rto_deadline_ != kNoDeadline && now >= rto_deadline_) {
1131
+ on_rto(now);
1132
+ if (state_ == State::Dead) { flush_events(); return; }
1133
+ }
1134
+
1135
+ // A delayed acknowledgement that has come due — or one carrying no deadline at
1136
+ // all, which is how read() asks for a window update. That case deliberately has
1137
+ // no deadline to wait out: the peer is stopped on a window of zero and will send
1138
+ // nothing for an acknowledgement to ride on, so an ack sent from here is the
1139
+ // only thing that can tell it to start again.
1140
+ // A window we re-opened that the peer has not answered. It is stopped, so it
1141
+ // will not produce a packet for the update to ride on however long we wait —
1142
+ // the announcement has to be volunteered, and volunteered again if it is lost.
1143
+ if (window_announces_ > 0 && now >= window_due_) {
1144
+ need_ack_ = true;
1145
+ ack_due_ = kNoDeadline; // at once, for the same reason read() leaves it so
1146
+ }
1147
+
1148
+ if (need_ack_ && (ack_due_ == kNoDeadline || now >= ack_due_)) {
1149
+ send_control(rudp::PacketType::Ack, now);
1150
+ // That one carried the window. Space the rest by the retransmission
1151
+ // timeout: it is this side's own estimate of how long an answer would take
1152
+ // to come back, and asking again sooner would only ask twice.
1153
+ if (window_announces_ > 0) {
1154
+ --window_announces_;
1155
+ window_due_ = now + rto_;
1156
+ }
1157
+ } else if (state_ == State::Connected && now - last_send_ >= kKeepAlive) {
1158
+ // Say something now and then: it keeps the peer's idle timer from firing
1159
+ // and, just as importantly, keeps a NAT's mapping for this port alive.
1160
+ send_control(rudp::PacketType::Ack, now);
1161
+ }
1162
+
1163
+ pump(now);
1164
+ flush_events();
1165
+ }
1166
+
1167
+ void UdpStream::die(CloseReason reason) {
1168
+ if (state_ == State::Dead) return;
1169
+ state_ = State::Dead;
1170
+ close_reason_ = reason;
1171
+ sent_.clear();
1172
+ unsent_.clear();
1173
+ reorder_.clear();
1174
+ spare_.clear();
1175
+ sack_bits_ = 0;
1176
+ sack_dirty_ = false;
1177
+ flight_bytes_ = 0;
1178
+ queued_bytes_ = 0;
1179
+ rto_deadline_ = kNoDeadline;
1180
+ pace_due_ = kNoDeadline;
1181
+ pace_tokens_ = 0;
1182
+ have_lost_ = false;
1183
+ window_announces_ = 0; // nobody is waiting on a window this stream will never serve
1184
+ raise(PollErr);
1185
+ }
1186
+
1187
+ void UdpStream::flush_events() {
1188
+ if (events_ == 0) return;
1189
+ const uint32_t events = events_;
1190
+ events_ = 0;
1191
+ host_.stream_events(*this, events);
1192
+ }
1193
+
1194
+ } // namespace librats