librats 1.0.2 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +145 -331
- package/binding.gyp +16 -3
- package/lib/index.d.ts +288 -696
- package/lib/index.js +407 -44
- package/native-src/3rdparty/android/ifaddrs-android.c +1 -0
- package/native-src/3rdparty/android/ifaddrs-android.h +1 -0
- package/native-src/CMakeLists.txt +404 -179
- package/native-src/LICENSE +1 -1
- package/native-src/src/librats/bindings/rats.cpp +762 -0
- package/native-src/src/librats/bindings/rats.h +380 -0
- package/native-src/src/librats/bittorrent/bencode.cpp +437 -0
- package/native-src/src/librats/bittorrent/bencode.h +176 -0
- package/native-src/src/librats/bittorrent/bitfield.cpp +97 -0
- package/native-src/src/librats/bittorrent/bitfield.h +76 -0
- package/native-src/src/librats/bittorrent/byte_io.h +58 -0
- package/native-src/src/librats/bittorrent/choker.cpp +25 -0
- package/native-src/src/librats/bittorrent/choker.h +46 -0
- package/native-src/src/librats/bittorrent/client.cpp +413 -0
- package/native-src/src/librats/bittorrent/client.h +227 -0
- package/native-src/src/librats/bittorrent/disk_io.cpp +209 -0
- package/native-src/src/librats/bittorrent/disk_io.h +150 -0
- package/native-src/src/librats/bittorrent/extensions.cpp +191 -0
- package/native-src/src/librats/bittorrent/extensions.h +94 -0
- package/native-src/src/librats/bittorrent/file_storage.cpp +77 -0
- package/native-src/src/librats/bittorrent/file_storage.h +79 -0
- package/native-src/src/librats/bittorrent/log.h +42 -0
- package/native-src/src/librats/bittorrent/magnet_uri.cpp +98 -0
- package/native-src/src/librats/bittorrent/magnet_uri.h +35 -0
- package/native-src/src/librats/bittorrent/peer_connection.cpp +502 -0
- package/native-src/src/librats/bittorrent/peer_connection.h +194 -0
- package/native-src/src/librats/bittorrent/peer_list.cpp +68 -0
- package/native-src/src/librats/bittorrent/peer_list.h +75 -0
- package/native-src/src/librats/bittorrent/piece_picker.cpp +352 -0
- package/native-src/src/librats/bittorrent/piece_picker.h +201 -0
- package/native-src/src/librats/bittorrent/reactor.cpp +97 -0
- package/native-src/src/librats/bittorrent/reactor.h +89 -0
- package/native-src/src/librats/bittorrent/resume_data.cpp +72 -0
- package/native-src/src/librats/bittorrent/resume_data.h +41 -0
- package/native-src/src/librats/bittorrent/store_buffer.cpp +48 -0
- package/native-src/src/librats/bittorrent/store_buffer.h +47 -0
- package/native-src/src/librats/bittorrent/torrent.cpp +870 -0
- package/native-src/src/librats/bittorrent/torrent.h +260 -0
- package/native-src/src/librats/bittorrent/torrent_creator.cpp +129 -0
- package/native-src/src/librats/bittorrent/torrent_creator.h +58 -0
- package/native-src/src/librats/bittorrent/torrent_info.cpp +314 -0
- package/native-src/src/librats/bittorrent/torrent_info.h +118 -0
- package/native-src/src/librats/bittorrent/tracker.cpp +374 -0
- package/native-src/src/librats/bittorrent/tracker.h +108 -0
- package/native-src/src/librats/bittorrent/types.cpp +206 -0
- package/native-src/src/librats/bittorrent/types.h +86 -0
- package/native-src/src/librats/core/address.cpp +35 -0
- package/native-src/src/librats/core/address.h +78 -0
- package/native-src/src/librats/core/bytes.h +69 -0
- package/native-src/src/librats/core/chained_send_buffer.cpp +172 -0
- package/native-src/src/librats/core/chained_send_buffer.h +183 -0
- package/native-src/src/librats/core/endpoint_parse.cpp +41 -0
- package/native-src/src/librats/core/endpoint_parse.h +31 -0
- package/native-src/src/librats/core/event_bus.h +70 -0
- package/native-src/src/librats/core/host_endpoint.h +56 -0
- package/native-src/src/{io_poller.cpp → librats/core/io_poller.cpp} +520 -65
- package/native-src/src/{io_poller.h → librats/core/io_poller.h} +12 -6
- package/native-src/src/librats/core/ip_address.cpp +120 -0
- package/native-src/src/librats/core/ip_address.h +109 -0
- package/native-src/src/librats/core/mpsc_queue.h +47 -0
- package/native-src/src/librats/core/notifier.h +74 -0
- package/native-src/src/librats/core/receive_buffer.cpp +219 -0
- package/native-src/src/librats/core/receive_buffer.h +171 -0
- package/native-src/src/librats/core/service_registry.h +58 -0
- package/native-src/src/{socket.cpp → librats/core/socket.cpp} +625 -118
- package/native-src/src/librats/core/socket.h +496 -0
- package/native-src/src/librats/core/timer_queue.h +105 -0
- package/native-src/src/librats/core/types.cpp +43 -0
- package/native-src/src/librats/core/types.h +103 -0
- package/native-src/src/librats/core/wakeup_pipe.h +83 -0
- package/native-src/src/{crypto → librats/crypto}/blake2_endian.h +21 -23
- package/native-src/src/{crypto → librats/crypto}/blake2b.c +34 -33
- package/native-src/src/{crypto → librats/crypto}/blake2b.h +7 -6
- package/native-src/src/{crypto → librats/crypto}/blake2s.c +55 -54
- package/native-src/src/{crypto → librats/crypto}/blake2s.h +13 -12
- package/native-src/src/{crypto → librats/crypto}/chacha.c +22 -21
- package/native-src/src/{crypto → librats/crypto}/chacha.h +14 -13
- package/native-src/src/{crypto → librats/crypto}/chachapoly.c +56 -56
- package/native-src/src/{crypto → librats/crypto}/chachapoly.h +24 -17
- package/native-src/src/{crc32.cpp → librats/crypto/crc32.cpp} +1 -1
- package/native-src/src/{crc32.h → librats/crypto/crc32.h} +3 -1
- package/native-src/src/{crypto → librats/crypto}/curve25519.c +6 -4
- package/native-src/src/{crypto → librats/crypto}/curve25519.h +6 -3
- package/native-src/src/librats/crypto/hkdf.c +266 -0
- package/native-src/src/{crypto → librats/crypto}/hkdf.h +19 -19
- package/native-src/src/{noise.cpp → librats/crypto/noise.cpp} +84 -73
- package/native-src/src/{noise.h → librats/crypto/noise.h} +18 -8
- package/native-src/src/{crypto → librats/crypto}/poly1305.c +47 -46
- package/native-src/src/librats/crypto/poly1305.h +37 -0
- package/native-src/src/{sha1.cpp → librats/crypto/sha1.cpp} +33 -1
- package/native-src/src/{sha1.h → librats/crypto/sha1.h} +14 -6
- package/native-src/src/{crypto → librats/crypto}/sha256.c +15 -14
- package/native-src/src/{crypto → librats/crypto}/sha256.h +8 -7
- package/native-src/src/{crypto → librats/crypto}/sha512.c +15 -14
- package/native-src/src/{crypto → librats/crypto}/sha512.h +8 -7
- package/native-src/src/librats/dht/announce.cpp +37 -0
- package/native-src/src/librats/dht/announce.h +41 -0
- package/native-src/src/librats/dht/bep42.cpp +109 -0
- package/native-src/src/librats/dht/bep42.h +48 -0
- package/native-src/src/librats/dht/dht.cpp +501 -0
- package/native-src/src/librats/dht/dht.h +119 -0
- package/native-src/src/librats/dht/dht_runner.cpp +103 -0
- package/native-src/src/librats/dht/dht_runner.h +71 -0
- package/native-src/src/librats/dht/dos_blocker.cpp +42 -0
- package/native-src/src/librats/dht/dos_blocker.h +47 -0
- package/native-src/src/librats/dht/find_peers.cpp +52 -0
- package/native-src/src/librats/dht/find_peers.h +73 -0
- package/native-src/src/librats/dht/id.h +167 -0
- package/native-src/src/{krpc.cpp → librats/dht/krpc.cpp} +32 -81
- package/native-src/src/{krpc.h → librats/dht/krpc.h} +19 -23
- package/native-src/src/librats/dht/log.h +38 -0
- package/native-src/src/librats/dht/node.cpp +473 -0
- package/native-src/src/librats/dht/node.h +164 -0
- package/native-src/src/librats/dht/node_entry.h +81 -0
- package/native-src/src/librats/dht/observer.h +72 -0
- package/native-src/src/librats/dht/persistence.cpp +90 -0
- package/native-src/src/librats/dht/persistence.h +32 -0
- package/native-src/src/librats/dht/routing_table.cpp +559 -0
- package/native-src/src/librats/dht/routing_table.h +185 -0
- package/native-src/src/librats/dht/rpc_manager.cpp +127 -0
- package/native-src/src/librats/dht/rpc_manager.h +77 -0
- package/native-src/src/librats/dht/storage.cpp +92 -0
- package/native-src/src/librats/dht/storage.h +74 -0
- package/native-src/src/librats/dht/transport.h +27 -0
- package/native-src/src/librats/dht/traversal.cpp +326 -0
- package/native-src/src/librats/dht/traversal.h +120 -0
- package/native-src/src/librats/dht/udp_transport.cpp +49 -0
- package/native-src/src/librats/dht/udp_transport.h +51 -0
- package/native-src/src/librats/mdns/log.h +22 -0
- package/native-src/src/{mdns.cpp → librats/mdns/mdns.cpp} +75 -40
- package/native-src/src/{mdns.h → librats/mdns/mdns.h} +9 -8
- package/native-src/src/{natpmp.cpp → librats/nat/natpmp.cpp} +12 -9
- package/native-src/src/{natpmp.h → librats/nat/natpmp.h} +3 -3
- package/native-src/src/{port_mapping.h → librats/nat/port_mapping.h} +3 -2
- package/native-src/src/{stun.cpp → librats/nat/stun.cpp} +4 -4
- package/native-src/src/{stun.h → librats/nat/stun.h} +1 -1
- package/native-src/src/{upnp.cpp → librats/nat/upnp.cpp} +6 -6
- package/native-src/src/{upnp.h → librats/nat/upnp.h} +2 -2
- package/native-src/src/librats/node/circuit_service.h +84 -0
- package/native-src/src/librats/node/config.h +110 -0
- package/native-src/src/librats/node/dial_service.h +54 -0
- package/native-src/src/librats/node/dialer.cpp +264 -0
- package/native-src/src/librats/node/dialer.h +188 -0
- package/native-src/src/librats/node/host_events.h +26 -0
- package/native-src/src/librats/node/identify.cpp +130 -0
- package/native-src/src/librats/node/identify.h +71 -0
- package/native-src/src/librats/node/nat_status.cpp +103 -0
- package/native-src/src/librats/node/nat_status.h +118 -0
- package/native-src/src/librats/node/node.cpp +865 -0
- package/native-src/src/librats/node/node.h +344 -0
- package/native-src/src/librats/node/node_context.h +33 -0
- package/native-src/src/librats/node/peer_network.h +91 -0
- package/native-src/src/librats/peer/peer.h +49 -0
- package/native-src/src/librats/peer/peer_book.cpp +181 -0
- package/native-src/src/librats/peer/peer_book.h +88 -0
- package/native-src/src/librats/peer/peer_id.cpp +72 -0
- package/native-src/src/librats/peer/peer_id.h +62 -0
- package/native-src/src/librats/peer/peer_info.h +37 -0
- package/native-src/src/librats/peer/peer_table.cpp +170 -0
- package/native-src/src/librats/peer/peer_table.h +148 -0
- package/native-src/src/librats/security/handshaker.h +66 -0
- package/native-src/src/librats/security/identity.h +43 -0
- package/native-src/src/librats/security/noise_security.cpp +122 -0
- package/native-src/src/librats/security/noise_security.h +37 -0
- package/native-src/src/librats/security/plaintext_security.h +106 -0
- package/native-src/src/librats/security/session.h +37 -0
- package/native-src/src/{storage.cpp → librats/storage/storage.cpp} +369 -522
- package/native-src/src/{storage.h → librats/storage/storage.h} +135 -299
- package/native-src/src/librats/subsystems/bittorrent.cpp +211 -0
- package/native-src/src/librats/subsystems/bittorrent.h +136 -0
- package/native-src/src/librats/subsystems/dht_discovery.cpp +202 -0
- package/native-src/src/librats/subsystems/dht_discovery.h +123 -0
- package/native-src/src/librats/subsystems/dht_service.h +36 -0
- package/native-src/src/librats/subsystems/file_transfer.cpp +972 -0
- package/native-src/src/librats/subsystems/file_transfer.h +367 -0
- package/native-src/src/librats/subsystems/hole_punch.cpp +605 -0
- package/native-src/src/librats/subsystems/hole_punch.h +290 -0
- package/native-src/src/librats/subsystems/hole_punch_service.h +38 -0
- package/native-src/src/librats/subsystems/mdns_discovery.cpp +66 -0
- package/native-src/src/librats/subsystems/mdns_discovery.h +55 -0
- package/native-src/src/librats/subsystems/message_json.cpp +112 -0
- package/native-src/src/librats/subsystems/message_json.h +88 -0
- package/native-src/src/librats/subsystems/peer_exchange.cpp +241 -0
- package/native-src/src/librats/subsystems/peer_exchange.h +136 -0
- package/native-src/src/librats/subsystems/ping_service.cpp +98 -0
- package/native-src/src/librats/subsystems/ping_service.h +66 -0
- package/native-src/src/librats/subsystems/port_mapping_service.cpp +192 -0
- package/native-src/src/librats/subsystems/port_mapping_service.h +84 -0
- package/native-src/src/librats/subsystems/pubsub.cpp +567 -0
- package/native-src/src/librats/subsystems/pubsub.h +175 -0
- package/native-src/src/librats/subsystems/reconnection.cpp +239 -0
- package/native-src/src/librats/subsystems/reconnection.h +126 -0
- package/native-src/src/librats/subsystems/relay.cpp +1142 -0
- package/native-src/src/librats/subsystems/relay.h +211 -0
- package/native-src/src/librats/subsystems/relay_service.h +46 -0
- package/native-src/src/librats/transport/connection.cpp +343 -0
- package/native-src/src/librats/transport/connection.h +283 -0
- package/native-src/src/librats/transport/link.h +96 -0
- package/native-src/src/librats/transport/reactor.cpp +588 -0
- package/native-src/src/librats/transport/reactor.h +262 -0
- package/native-src/src/librats/transport/reactor_pool.h +81 -0
- package/native-src/src/librats/transport/relay_link.cpp +208 -0
- package/native-src/src/librats/transport/relay_link.h +303 -0
- package/native-src/src/librats/transport/tcp_link.cpp +49 -0
- package/native-src/src/librats/transport/tcp_link.h +43 -0
- package/native-src/src/librats/transport/udp_mux.cpp +617 -0
- package/native-src/src/librats/transport/udp_mux.h +363 -0
- package/native-src/src/librats/transport/udp_packet.cpp +121 -0
- package/native-src/src/librats/transport/udp_packet.h +190 -0
- package/native-src/src/librats/transport/udp_stream.cpp +1194 -0
- package/native-src/src/librats/transport/udp_stream.h +614 -0
- package/native-src/src/librats/util/features.h.in +51 -0
- package/native-src/src/{fs.cpp → librats/util/fs.cpp} +51 -3
- package/native-src/src/librats/util/fs.h +136 -0
- package/native-src/src/librats/util/json.cpp +1002 -0
- package/native-src/src/librats/util/json.h +444 -0
- package/native-src/src/{logger.cpp → librats/util/logger.cpp} +1 -1
- package/native-src/src/{logger.h → librats/util/logger.h} +43 -31
- package/native-src/src/{network_monitor.cpp → librats/util/network_monitor.cpp} +12 -4
- package/native-src/src/{network_monitor.h → librats/util/network_monitor.h} +2 -1
- package/native-src/src/{network_utils.cpp → librats/util/network_utils.cpp} +38 -23
- package/native-src/src/{network_utils.h → librats/util/network_utils.h} +15 -8
- package/native-src/src/{os.cpp → librats/util/os.cpp} +48 -18
- package/native-src/src/librats/util/rats_export.h +69 -0
- package/native-src/src/{version.cpp → librats/util/version.cpp} +2 -2
- package/native-src/src/{version.h.in → librats/util/version.h.in} +1 -1
- package/native-src/src/librats/wire/frame.cpp +76 -0
- package/native-src/src/librats/wire/frame.h +111 -0
- package/native-src/src/librats/wire/message_router.cpp +45 -0
- package/native-src/src/librats/wire/message_router.h +47 -0
- package/package.json +5 -4
- package/scripts/build-librats.js +1 -0
- package/scripts/postinstall.js +3 -3
- package/scripts/prepare-package.js +4 -4
- package/scripts/verify-installation.js +63 -105
- package/src/librats_node.cpp +1067 -1323
- package/native-src/src/bencode.cpp +0 -485
- package/native-src/src/bencode.h +0 -145
- package/native-src/src/bittorrent.cpp +0 -14
- package/native-src/src/bittorrent.h +0 -74
- package/native-src/src/bt_bitfield.cpp +0 -372
- package/native-src/src/bt_bitfield.h +0 -316
- package/native-src/src/bt_choker.cpp +0 -228
- package/native-src/src/bt_choker.h +0 -147
- package/native-src/src/bt_client.cpp +0 -1047
- package/native-src/src/bt_client.h +0 -445
- package/native-src/src/bt_create_torrent.cpp +0 -677
- package/native-src/src/bt_create_torrent.h +0 -473
- package/native-src/src/bt_extension.cpp +0 -469
- package/native-src/src/bt_extension.h +0 -309
- package/native-src/src/bt_file_storage.cpp +0 -261
- package/native-src/src/bt_file_storage.h +0 -298
- package/native-src/src/bt_handshake.cpp +0 -134
- package/native-src/src/bt_handshake.h +0 -157
- package/native-src/src/bt_messages.cpp +0 -364
- package/native-src/src/bt_messages.h +0 -324
- package/native-src/src/bt_network.cpp +0 -1007
- package/native-src/src/bt_network.h +0 -417
- package/native-src/src/bt_peer_connection.cpp +0 -742
- package/native-src/src/bt_peer_connection.h +0 -592
- package/native-src/src/bt_piece_picker.cpp +0 -786
- package/native-src/src/bt_piece_picker.h +0 -473
- package/native-src/src/bt_resume_data.cpp +0 -410
- package/native-src/src/bt_resume_data.h +0 -249
- package/native-src/src/bt_torrent.cpp +0 -2120
- package/native-src/src/bt_torrent.h +0 -641
- package/native-src/src/bt_torrent_info.cpp +0 -659
- package/native-src/src/bt_torrent_info.h +0 -418
- package/native-src/src/bt_types.h +0 -621
- package/native-src/src/chained_send_buffer.cpp +0 -75
- package/native-src/src/chained_send_buffer.h +0 -137
- package/native-src/src/crypto/hkdf.c +0 -266
- package/native-src/src/crypto/poly1305.h +0 -36
- package/native-src/src/dht.cpp +0 -3311
- package/native-src/src/dht.h +0 -717
- package/native-src/src/disk_io.cpp +0 -632
- package/native-src/src/disk_io.h +0 -315
- package/native-src/src/file_transfer.cpp +0 -1415
- package/native-src/src/file_transfer.h +0 -286
- package/native-src/src/fs.h +0 -108
- package/native-src/src/gossipsub.cpp +0 -1139
- package/native-src/src/gossipsub.h +0 -403
- package/native-src/src/ice.cpp +0 -893
- package/native-src/src/ice.h +0 -559
- package/native-src/src/json.hpp +0 -25526
- package/native-src/src/librats.cpp +0 -2378
- package/native-src/src/librats.h +0 -2324
- package/native-src/src/librats_bittorrent.cpp +0 -601
- package/native-src/src/librats_c.cpp +0 -1557
- package/native-src/src/librats_c.h +0 -323
- package/native-src/src/librats_discovery.cpp +0 -402
- package/native-src/src/librats_encryption.cpp +0 -275
- package/native-src/src/librats_file_transfer.cpp +0 -144
- package/native-src/src/librats_gossipsub.cpp +0 -289
- package/native-src/src/librats_ice.cpp +0 -213
- package/native-src/src/librats_log_macros.h +0 -36
- package/native-src/src/librats_logging.cpp +0 -173
- package/native-src/src/librats_mdns.cpp +0 -166
- package/native-src/src/librats_persistence.cpp +0 -796
- package/native-src/src/librats_portmap.cpp +0 -419
- package/native-src/src/librats_reconnection.cpp +0 -218
- package/native-src/src/librats_statistic.cpp +0 -105
- package/native-src/src/librats_storage.cpp +0 -189
- package/native-src/src/rats_export.h +0 -17
- package/native-src/src/receive_buffer.cpp +0 -82
- package/native-src/src/receive_buffer.h +0 -127
- package/native-src/src/socket.h +0 -228
- package/native-src/src/threadmanager.cpp +0 -105
- package/native-src/src/threadmanager.h +0 -53
- package/native-src/src/tracker.cpp +0 -1264
- package/native-src/src/tracker.h +0 -319
- package/native-src/src/turn.cpp +0 -762
- package/native-src/src/turn.h +0 -460
- package/native-src/src/wakeup_pipe.h +0 -60
- /package/native-src/src/{os.h → librats/util/os.h} +0 -0
|
@@ -0,0 +1,614 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file udp_stream.h
|
|
5
|
+
* @brief An ordered, reliable byte stream over datagrams — TCP's guarantees, in
|
|
6
|
+
* user space, on a socket shared by every peer.
|
|
7
|
+
*
|
|
8
|
+
* ── Why ──────────────────────────────────────────────────────────────────────
|
|
9
|
+
* For a peer-to-peer node UDP is the better default wire: a single socket serves
|
|
10
|
+
* every peer (so a NAT keeps one mapping open, and hole punching has something to
|
|
11
|
+
* punch), the port a peer sees is the port we listen on, and nothing in the path
|
|
12
|
+
* has to hold per-connection kernel state. What UDP does not give is what every
|
|
13
|
+
* layer above needs — order, reliability and congestion control. That is this
|
|
14
|
+
* file. Above it, nothing knows the difference: the same block framing, the same
|
|
15
|
+
* Noise handshake and the same Session run unchanged over TCP or over this.
|
|
16
|
+
*
|
|
17
|
+
* ── The protocol ─────────────────────────────────────────────────────────────
|
|
18
|
+
* Wire format lives in udp_packet.h. The stream itself is a compact, standard
|
|
19
|
+
* design — deliberately familiar rather than novel, because a transport is the
|
|
20
|
+
* wrong place to be clever:
|
|
21
|
+
*
|
|
22
|
+
* - Packet-numbered, not byte-numbered. Syn, Data and Fin each consume exactly
|
|
23
|
+
* one sequence number; a pure Ack consumes none. That single rule is what
|
|
24
|
+
* makes the retransmission queue a plain deque whose i-th entry is always
|
|
25
|
+
* `front().seq + i`, so a selective ack resolves to an index instead of a
|
|
26
|
+
* search.
|
|
27
|
+
* - Cumulative ack + a 32-bit selective-ack bitmap, so one lost packet is
|
|
28
|
+
* repaired without stalling everything queued behind it.
|
|
29
|
+
* - RFC 6298 retransmission timing (SRTT/RTTVAR → RTO, doubling on each
|
|
30
|
+
* timeout, Karn's rule so a retransmitted packet never poisons the estimate),
|
|
31
|
+
* plus fast retransmit on the third duplicate ack.
|
|
32
|
+
* - Reno-style congestion control: slow start to `ssthresh`, then additive
|
|
33
|
+
* increase; multiplicative decrease on loss. Flow control is separate and
|
|
34
|
+
* absolute — the receiver advertises, in packets, how much more it will
|
|
35
|
+
* buffer, and the sender never exceeds it.
|
|
36
|
+
* - No Nagle: a partial packet goes out rather than waiting for company, which
|
|
37
|
+
* is what keeps a request/response exchange from paying a round trip per
|
|
38
|
+
* turn. What stands in for it is write() itself — it tops up the tail packet
|
|
39
|
+
* while it has room, so consecutive small frames share one datagram anyway,
|
|
40
|
+
* and UdpMux then hands the socket a whole batch of them per syscall. The
|
|
41
|
+
* packing catches everything a reactor turn produced, because Connection
|
|
42
|
+
* aggregates a turn's frames and writes them once rather than flushing each
|
|
43
|
+
* — which is where most of the per-message cost used to go.
|
|
44
|
+
* - Paced: the window says how much may be outstanding, not how fast it may
|
|
45
|
+
* leave, so transmissions are metered at `gain * cwnd / srtt` rather than
|
|
46
|
+
* released in a burst. HyStart++ ends slow start on a rising round trip
|
|
47
|
+
* instead of on a loss, and a window nobody has validated for a round trip
|
|
48
|
+
* is given back (RFC 2861) rather than believed.
|
|
49
|
+
* - A tail loss is probed, not timed out: the last packet of a burst has
|
|
50
|
+
* nothing behind it to produce duplicate acknowledgements, so silence is
|
|
51
|
+
* answered with a question (RFC 8985) before it is treated as congestion.
|
|
52
|
+
*
|
|
53
|
+
* ── Ownership and threading ──────────────────────────────────────────────────
|
|
54
|
+
* A stream is owned by the UdpMux and touched only by the reactor thread that
|
|
55
|
+
* drives it — no locks, no atomics, like everything else on this path. It reaches
|
|
56
|
+
* the wire and reports events through UdpStreamHost, and never sees the socket,
|
|
57
|
+
* the reactor or the Connection directly. Events are *recorded* by the host, not
|
|
58
|
+
* delivered inline, so a stream is never destroyed underneath the call that is
|
|
59
|
+
* still running inside it.
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
#include "librats/core/address.h"
|
|
63
|
+
#include "librats/core/bytes.h"
|
|
64
|
+
#include "librats/core/receive_buffer.h"
|
|
65
|
+
#include "librats/core/types.h"
|
|
66
|
+
#include "librats/transport/udp_packet.h"
|
|
67
|
+
|
|
68
|
+
#include <chrono>
|
|
69
|
+
#include <cstdint>
|
|
70
|
+
#include <deque>
|
|
71
|
+
#include <optional>
|
|
72
|
+
#include <unordered_map>
|
|
73
|
+
#include <vector>
|
|
74
|
+
|
|
75
|
+
namespace librats {
|
|
76
|
+
|
|
77
|
+
class UdpStream;
|
|
78
|
+
|
|
79
|
+
/// What a stream needs from its owner: a way to the wire, and somewhere to leave
|
|
80
|
+
/// the events its connection should see.
|
|
81
|
+
class UdpStreamHost {
|
|
82
|
+
public:
|
|
83
|
+
virtual ~UdpStreamHost() = default;
|
|
84
|
+
|
|
85
|
+
/// Emit one datagram. Best effort by design — a datagram the socket refuses is
|
|
86
|
+
/// indistinguishable from one the path drops, and retransmission covers both.
|
|
87
|
+
virtual void send_datagram(const Address& to, const uint8_t* data, size_t len) = 0;
|
|
88
|
+
|
|
89
|
+
/// Record poll-equivalent events (PollIn/PollOut/PollErr) for the connection
|
|
90
|
+
/// that owns `stream`. Implementations must only *record*: the events are
|
|
91
|
+
/// dispatched once the current batch of packets or timers is done, so a
|
|
92
|
+
/// handler that tears the connection down cannot pull the stream out from
|
|
93
|
+
/// under the code that is still walking it. Repeated events for one stream
|
|
94
|
+
/// within a batch are expected — a stream raises PollIn per packet delivered —
|
|
95
|
+
/// and an implementation is free to coalesce them into one dispatch.
|
|
96
|
+
virtual void stream_events(UdpStream& stream, uint32_t events) = 0;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
class UdpStream {
|
|
100
|
+
public:
|
|
101
|
+
using Clock = std::chrono::steady_clock;
|
|
102
|
+
|
|
103
|
+
// ── Tunables ────────────────────────────────────────────────────────────
|
|
104
|
+
|
|
105
|
+
/// Bytes the stream will take from the connection before it says "no more".
|
|
106
|
+
/// The connection keeps the rest in its own send queue, where the existing
|
|
107
|
+
/// high-water mark governs it, so this only bounds what the transport itself
|
|
108
|
+
/// holds — roughly a full window plus room to keep the pipe fed.
|
|
109
|
+
///
|
|
110
|
+
/// It has to stay comfortably above a full window (kMaxWindowPackets *
|
|
111
|
+
/// kMaxPayload ≈ 1.2 MiB), because it caps `sent_` and `unsent_` *together*:
|
|
112
|
+
/// set at or below the window it, not the window, becomes the throughput
|
|
113
|
+
/// ceiling, and the pipe drains between acks because nothing is queued behind
|
|
114
|
+
/// what is in flight.
|
|
115
|
+
static constexpr size_t kSendQueueLimit = 2 * 1024 * 1024;
|
|
116
|
+
|
|
117
|
+
/// Packet buffers kept for reuse after their packet is acknowledged. The send
|
|
118
|
+
/// path allocates one buffer per packet, and in a bulk transfer that is one
|
|
119
|
+
/// allocation per 1200 bytes shipped; recycling turns the steady state into no
|
|
120
|
+
/// allocation at all. Small on purpose — creation and retirement run at the
|
|
121
|
+
/// same rate once a transfer is going, so a handful covers the churn, and an
|
|
122
|
+
/// idle stream should not sit on memory it is not using.
|
|
123
|
+
static constexpr size_t kMaxSpareBuffers = 8;
|
|
124
|
+
|
|
125
|
+
/// Initial congestion window. Four packets is the classic conservative start
|
|
126
|
+
/// (RFC 3390 territory) — enough to get an RTT sample and trigger fast
|
|
127
|
+
/// retransmit on an early loss, without a burst into an unknown path.
|
|
128
|
+
static constexpr uint32_t kInitialCwnd = 4 * rudp::kMaxPayload;
|
|
129
|
+
static constexpr uint32_t kMinCwnd = 2 * rudp::kMaxPayload;
|
|
130
|
+
/// A full window. Growing the congestion window past what the receiver will
|
|
131
|
+
/// ever hold buys nothing: the packet-count check in can_transmit() would stop
|
|
132
|
+
/// the sender first, and a cwnd that has run away above the real limit takes a
|
|
133
|
+
/// spurious loss with it when it is finally halved.
|
|
134
|
+
static constexpr uint32_t kMaxCwnd = rudp::kMaxWindowPackets * rudp::kMaxPayload;
|
|
135
|
+
|
|
136
|
+
static constexpr std::chrono::milliseconds kInitialRto{500};
|
|
137
|
+
static constexpr std::chrono::milliseconds kMinRto{100};
|
|
138
|
+
static constexpr std::chrono::milliseconds kMaxRto{6000};
|
|
139
|
+
|
|
140
|
+
/// Transmissions of the Syn before an unanswered dial is called failed. Three
|
|
141
|
+
/// attempts at a doubling 500 ms RTO give up after ~3.5 s — fast enough that a
|
|
142
|
+
/// node on a UDP-blocking network falls back to TCP promptly (see the dialer),
|
|
143
|
+
/// and slow enough to ride out a genuinely lossy path.
|
|
144
|
+
static constexpr int kSynMaxAttempts = 3;
|
|
145
|
+
/// Transmissions of one packet on an established stream before the peer is
|
|
146
|
+
/// declared gone. With RTO doubling to its 6 s cap this is a bit over a minute.
|
|
147
|
+
static constexpr int kMaxRetransmits = 12;
|
|
148
|
+
|
|
149
|
+
/// How long an acknowledgement may wait for a packet to ride along on. Only
|
|
150
|
+
/// ever delays a *pure* ack: an out-of-order packet, a Syn or a Fin is
|
|
151
|
+
/// acknowledged at once, and any outgoing packet carries the ack for free.
|
|
152
|
+
static constexpr std::chrono::milliseconds kDelayedAck{20};
|
|
153
|
+
/// Packets a single acknowledgement may repair. Bounds the burst one ack can
|
|
154
|
+
/// provoke, so a window that was mostly lost is rebuilt over a few acks
|
|
155
|
+
/// instead of being dumped back onto a path that just proved it is congested.
|
|
156
|
+
static constexpr int kMaxRepairsPerAck = 8;
|
|
157
|
+
|
|
158
|
+
/// Minimum gap between two transmissions of the same packet. Selective acks
|
|
159
|
+
/// arrive several times per round trip, and without this floor each of them
|
|
160
|
+
/// would re-send the same not-yet-repaired packet — turning a single loss into
|
|
161
|
+
/// a flood. It also stands in for the round-trip estimate on a path (loopback,
|
|
162
|
+
/// LAN) whose RTT is too small to space anything out.
|
|
163
|
+
static constexpr std::chrono::milliseconds kMinRepairSpacing{10};
|
|
164
|
+
|
|
165
|
+
// ── Pacing ──────────────────────────────────────────────────────────────
|
|
166
|
+
//
|
|
167
|
+
// A congestion window is permission to have N bytes *outstanding*, not
|
|
168
|
+
// permission to put them on the wire back to back. Emitting a whole window
|
|
169
|
+
// at line rate is what turns a queue that was merely full into a queue that
|
|
170
|
+
// dropped a hundred packets at once — and it happens even when the window is
|
|
171
|
+
// well under what the path can hold, because the burst arrives faster than
|
|
172
|
+
// the bottleneck can drain it. So transmissions are released at the rate the
|
|
173
|
+
// window implies rather than as fast as the loop can produce them.
|
|
174
|
+
//
|
|
175
|
+
// The rate is `gain * cwnd / srtt`. The gain is above 1 on purpose: slow
|
|
176
|
+
// start has to deliver a *doubling* window within one round trip, so pacing
|
|
177
|
+
// it at exactly cwnd/srtt would hold growth back by a factor of two, and in
|
|
178
|
+
// congestion avoidance a little headroom keeps an ack-clocked sender from
|
|
179
|
+
// being throttled by its own estimate. These are the gains RFC 9002 (7.7)
|
|
180
|
+
// recommends and Linux uses.
|
|
181
|
+
static constexpr uint32_t kPaceGainSlowStartNum = 2;
|
|
182
|
+
static constexpr uint32_t kPaceGainSlowStartDen = 1;
|
|
183
|
+
static constexpr uint32_t kPaceGainSteadyNum = 5; ///< 1.25x
|
|
184
|
+
static constexpr uint32_t kPaceGainSteadyDen = 4;
|
|
185
|
+
|
|
186
|
+
/// Burst the pacer tolerates, expressed as time rather than packets: it is
|
|
187
|
+
/// what accrues at the current rate over one wake-up of the reactor. Below
|
|
188
|
+
/// this there is nothing to gain — the timer cannot space packets finer than
|
|
189
|
+
/// it can wake — and pacing every packet against a 1 ms clock would cap a
|
|
190
|
+
/// stream at one packet per millisecond. Above it the smoothing is thrown
|
|
191
|
+
/// away. So the pacer does not remove bursts, it bounds them to one clock
|
|
192
|
+
/// tick's worth of data, which is what the queue can absorb.
|
|
193
|
+
static constexpr std::chrono::milliseconds kPaceQuantum{1};
|
|
194
|
+
/// Floor for that burst. Two packets is the classic allowance (a delayed ack
|
|
195
|
+
/// releases two at a time), and it keeps a stream on a very slow path from
|
|
196
|
+
/// pacing itself below one packet per round trip.
|
|
197
|
+
static constexpr size_t kPaceMinBurst = 2 * rudp::kMaxPayload;
|
|
198
|
+
|
|
199
|
+
// ── HyStart++ (RFC 9406): leaving slow start before the loss ─────────────
|
|
200
|
+
//
|
|
201
|
+
// Slow start doubles the window every round trip and, left alone, stops only
|
|
202
|
+
// when something is dropped — which means it always overshoots the path by
|
|
203
|
+
// roughly a factor of two and pays for the discovery with a lost window.
|
|
204
|
+
// HyStart++ watches the *minimum* round-trip time per round instead: a queue
|
|
205
|
+
// building in front of the bottleneck raises it well before it overflows.
|
|
206
|
+
// When it rises past a threshold the sender leaves exponential growth for a
|
|
207
|
+
// cautious phase (CSS), and if the rise turns out to be noise it goes back.
|
|
208
|
+
static constexpr std::chrono::milliseconds kHyMinRttThresh{4};
|
|
209
|
+
static constexpr std::chrono::milliseconds kHyMaxRttThresh{16};
|
|
210
|
+
/// Round-trip samples a round needs before its minimum is worth comparing.
|
|
211
|
+
static constexpr int kHyRttSamples = 8;
|
|
212
|
+
/// Growth divisor in the cautious phase: a quarter of slow start, so the
|
|
213
|
+
/// window still probes upward but cannot double while the verdict is out.
|
|
214
|
+
static constexpr uint32_t kHyCssGrowthDivisor = 4;
|
|
215
|
+
/// Rounds the cautious phase lasts before the exit is believed.
|
|
216
|
+
static constexpr int kHyCssRounds = 5;
|
|
217
|
+
|
|
218
|
+
// ── Tail loss probe (RFC 8985 / RFC 9002 §6.2) ──────────────────────────
|
|
219
|
+
//
|
|
220
|
+
// A loss is normally noticed by what arrives *after* it: duplicate
|
|
221
|
+
// acknowledgements, or a selective ack naming the hole. Neither exists when
|
|
222
|
+
// the packet that went missing was the last one — the receiver has nothing
|
|
223
|
+
// further to acknowledge and simply falls silent. Left to the retransmission
|
|
224
|
+
// timeout, that costs kMinRto (100 ms) *and* collapses the congestion window
|
|
225
|
+
// to one packet, on a stream where nothing is actually congested.
|
|
226
|
+
//
|
|
227
|
+
// For request/response traffic — which is most of what a peer-to-peer node
|
|
228
|
+
// does — the tail is not an edge case, it is every message. So the first one
|
|
229
|
+
// or two expiries are treated as a question rather than a verdict: re-send
|
|
230
|
+
// the packet the peer has gone quiet on, leave the window alone, and only
|
|
231
|
+
// escalate to the real timeout if the silence persists.
|
|
232
|
+
static constexpr int kMaxTailProbes = 2;
|
|
233
|
+
/// Floor for the probe timer. Well under kMinRto — that floor exists to keep
|
|
234
|
+
/// a *timeout* from firing spuriously, and a spurious timeout is expensive
|
|
235
|
+
/// where a spurious probe costs one packet — but still above the delayed
|
|
236
|
+
/// acknowledgement it must not race.
|
|
237
|
+
static constexpr std::chrono::milliseconds kMinProbeTimeout{30};
|
|
238
|
+
|
|
239
|
+
/// Times a re-opened receive window is announced before the matter is left to
|
|
240
|
+
/// the keep-alive. A window update rides on a bare acknowledgement, and nothing
|
|
241
|
+
/// retransmits one — while the sender it is meant for is stopped and produces
|
|
242
|
+
/// no traffic for a second copy to ride on. So a single dropped update would
|
|
243
|
+
/// cost that sender a full keep-alive interval of silence over one lost packet.
|
|
244
|
+
/// Repeating it on the retransmission timeout makes the common recovery a round
|
|
245
|
+
/// trip instead, and the keep-alive remains the backstop behind these.
|
|
246
|
+
static constexpr int kMaxWindowAnnounces = 4;
|
|
247
|
+
|
|
248
|
+
/// Idle gap after which an ack is sent purely to prove we are still here.
|
|
249
|
+
static constexpr std::chrono::seconds kKeepAlive{10};
|
|
250
|
+
/// Silence from the peer that ends the stream. Comfortably more than four
|
|
251
|
+
/// keep-alive intervals, so only real loss of contact trips it.
|
|
252
|
+
static constexpr std::chrono::seconds kIdleTimeout{45};
|
|
253
|
+
|
|
254
|
+
/// @param profile How hard an OUTBOUND dial tries (see DialProfile). Ignored
|
|
255
|
+
/// for an inbound stream, which never sends a Syn. The default is the
|
|
256
|
+
/// ordinary dial; a hole punch passes DialProfile::punch().
|
|
257
|
+
UdpStream(UdpStreamHost& host, const Address& remote, uint32_t recv_id, uint32_t send_id,
|
|
258
|
+
ConnRole role, Clock::time_point now, DialProfile profile = {});
|
|
259
|
+
|
|
260
|
+
UdpStream(const UdpStream&) = delete;
|
|
261
|
+
UdpStream& operator=(const UdpStream&) = delete;
|
|
262
|
+
|
|
263
|
+
// ── Identity ────────────────────────────────────────────────────────────
|
|
264
|
+
|
|
265
|
+
/// The id peers put in packets addressed to this stream (our demux key).
|
|
266
|
+
uint32_t recv_id() const noexcept { return recv_id_; }
|
|
267
|
+
/// The id we put in packets we send (the peer's demux key).
|
|
268
|
+
uint32_t send_id() const noexcept { return send_id_; }
|
|
269
|
+
const Address& remote() const noexcept { return remote_; }
|
|
270
|
+
ConnRole role() const noexcept { return role_; }
|
|
271
|
+
|
|
272
|
+
/// The connection this stream belongs to, once the reactor has adopted it.
|
|
273
|
+
ConnId conn_id() const noexcept { return conn_id_; }
|
|
274
|
+
void set_conn_id(ConnId id) noexcept { conn_id_ = id; }
|
|
275
|
+
|
|
276
|
+
// ── Driven by the mux ───────────────────────────────────────────────────
|
|
277
|
+
|
|
278
|
+
/// Feed one decoded datagram addressed to this stream.
|
|
279
|
+
void on_packet(const rudp::Packet& p, Clock::time_point now);
|
|
280
|
+
|
|
281
|
+
/// Periodic work: retransmission, delayed acks, keep-alive, idle death.
|
|
282
|
+
void tick(Clock::time_point now);
|
|
283
|
+
|
|
284
|
+
/// When tick() next has something to do — the earliest of the retransmission
|
|
285
|
+
/// timeout, an owed acknowledgement, the keep-alive and the idle deadline.
|
|
286
|
+
///
|
|
287
|
+
/// This is what lets the mux schedule streams instead of sweeping them: a
|
|
288
|
+
/// stream that is merely connected wants to be visited once per keep-alive
|
|
289
|
+
/// (10 s), not fifty times a second, and a node with no streams at all wants
|
|
290
|
+
/// no timer whatsoever. Every mutating entry point re-reads this, so a
|
|
291
|
+
/// deadline can never move earlier without the owner being told.
|
|
292
|
+
///
|
|
293
|
+
/// Two values are special:
|
|
294
|
+
/// - `nullopt` — never; only a dead stream, which the mux drops rather than
|
|
295
|
+
/// services.
|
|
296
|
+
/// - the clock epoch — *now*. An acknowledgement owed with no deadline (see
|
|
297
|
+
/// `ack_due_`) is one that must go out at the first opportunity, because
|
|
298
|
+
/// the peer it is meant for is stopped on a zero window and will send
|
|
299
|
+
/// nothing for it to ride on.
|
|
300
|
+
std::optional<Clock::time_point> next_deadline() const noexcept;
|
|
301
|
+
|
|
302
|
+
// ── Driven by the Link / Connection ─────────────────────────────────────
|
|
303
|
+
|
|
304
|
+
/// Copy up to `len` bytes of in-order stream data out. 0 means nothing is
|
|
305
|
+
/// ready (check eof() to tell "not yet" from "never again").
|
|
306
|
+
size_t read(uint8_t* into, size_t len);
|
|
307
|
+
|
|
308
|
+
/// Queue `count` slices for the peer, taking as much as the send queue will
|
|
309
|
+
/// hold. Returns the bytes accepted; 0 means the queue is full and the caller
|
|
310
|
+
/// should ask to be told when it drains (see want_write).
|
|
311
|
+
size_t write(const ByteView* slices, size_t count, Clock::time_point now);
|
|
312
|
+
|
|
313
|
+
/// Ask to be given a writable event when the send queue drains again.
|
|
314
|
+
void want_write(bool on) noexcept { want_write_ = on; }
|
|
315
|
+
|
|
316
|
+
/// Orderly shutdown: queue a Fin behind everything already written, so the
|
|
317
|
+
/// peer sees every byte we owe it and then a clean end of stream.
|
|
318
|
+
void begin_close(Clock::time_point now);
|
|
319
|
+
|
|
320
|
+
/// Abrupt shutdown: tell the peer the stream is gone and stop. Used when
|
|
321
|
+
/// there is nothing worth flushing, or when the stream has already failed.
|
|
322
|
+
void abort(Clock::time_point now);
|
|
323
|
+
|
|
324
|
+
// ── State ───────────────────────────────────────────────────────────────
|
|
325
|
+
|
|
326
|
+
bool connecting() const noexcept { return state_ == State::SynSent; }
|
|
327
|
+
bool connected() const noexcept { return state_ == State::Connected; }
|
|
328
|
+
bool dead() const noexcept { return state_ == State::Dead; }
|
|
329
|
+
|
|
330
|
+
/// The peer finished sending AND everything it sent has been read out.
|
|
331
|
+
bool eof() const noexcept { return peer_fin_ && inbox_.empty(); }
|
|
332
|
+
|
|
333
|
+
/// Why the stream died (only meaningful once dead()).
|
|
334
|
+
CloseReason close_reason() const noexcept { return close_reason_; }
|
|
335
|
+
|
|
336
|
+
/// Nothing left to deliver: every packet we queued has been acknowledged.
|
|
337
|
+
/// The condition the mux lingers a released stream until.
|
|
338
|
+
bool flushed() const noexcept { return sent_.empty() && unsent_.empty(); }
|
|
339
|
+
|
|
340
|
+
// — diagnostics (tests, logging) —
|
|
341
|
+
uint32_t cwnd() const noexcept { return cwnd_; }
|
|
342
|
+
/// Where slow start stops. Starts at the ceiling and comes down either when
|
|
343
|
+
/// HyStart++ sees the round-trip time rise or when something is lost — which
|
|
344
|
+
/// of the two happened is the whole question F2 asks.
|
|
345
|
+
uint32_t ssthresh() const noexcept { return ssthresh_; }
|
|
346
|
+
size_t bytes_in_flight() const noexcept { return flight_bytes_; }
|
|
347
|
+
size_t queued_bytes() const noexcept { return queued_bytes_; }
|
|
348
|
+
uint32_t retransmits() const noexcept { return retransmits_; }
|
|
349
|
+
/// Tail probes sent since the last acknowledgement (diagnostics, tests).
|
|
350
|
+
int tail_probes() const noexcept { return tail_probes_; }
|
|
351
|
+
/// Times the congestion window has been reduced. One per loss *episode* is the
|
|
352
|
+
/// invariant that keeps a single lost packet from walking the window to the
|
|
353
|
+
/// floor over the many acks that report it — see enter_recovery().
|
|
354
|
+
uint32_t window_reductions() const noexcept { return window_reductions_; }
|
|
355
|
+
|
|
356
|
+
private:
|
|
357
|
+
enum class State {
|
|
358
|
+
SynSent, ///< outbound: the Syn is in flight, nothing else may go yet
|
|
359
|
+
Connected, ///< both directions open (a Fin may still be queued)
|
|
360
|
+
Dead, ///< finished or failed; the mux will drop it
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
/// One packet occupying exactly one sequence number.
|
|
364
|
+
///
|
|
365
|
+
/// `buf` is the datagram itself, laid out as
|
|
366
|
+
///
|
|
367
|
+
/// [ kMaxHeaderSize bytes of headroom ][ payload ]
|
|
368
|
+
///
|
|
369
|
+
/// so transmit() writes the header into the tail of the headroom, directly in
|
|
370
|
+
/// front of the payload, and hands the socket one contiguous range. The
|
|
371
|
+
/// alternative — payload in its own buffer, copied into a scratch datagram
|
|
372
|
+
/// behind a freshly built header — costs a full payload copy on every send
|
|
373
|
+
/// *and* every retransmission, on the hottest path this transport has.
|
|
374
|
+
struct OutPacket {
|
|
375
|
+
Bytes buf; ///< headroom + payload; only headroom for Syn/Fin
|
|
376
|
+
uint32_t seq = 0;
|
|
377
|
+
rudp::PacketType type = rudp::PacketType::Data;
|
|
378
|
+
Clock::time_point sent_at{};
|
|
379
|
+
int sends = 0; ///< transmissions so far (0 = still unsent)
|
|
380
|
+
bool acked = false; ///< selectively acknowledged, awaiting the cumulative ack
|
|
381
|
+
/// This packet's bytes are counted in flight_bytes_ right now. Set by the
|
|
382
|
+
/// transmission that put them on the wire, cleared when they are either
|
|
383
|
+
/// acknowledged or declared lost — because a packet a retransmission
|
|
384
|
+
/// timeout has given up on is no longer occupying the path, and leaving it
|
|
385
|
+
/// counted is what would stop the sender from ever refilling the pipe.
|
|
386
|
+
bool in_flight = false;
|
|
387
|
+
|
|
388
|
+
/// Payload bytes — what the accounting (flight_bytes_, queued_bytes_) counts.
|
|
389
|
+
size_t size() const noexcept {
|
|
390
|
+
return buf.size() > rudp::kMaxHeaderSize ? buf.size() - rudp::kMaxHeaderSize : 0;
|
|
391
|
+
}
|
|
392
|
+
/// Room left in a partially filled tail packet (only meaningful while unsent).
|
|
393
|
+
size_t space() const noexcept { return rudp::kMaxPayload - size(); }
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
/// A packet held out of order, waiting for the gap in front of it to fill.
|
|
397
|
+
struct InPacket {
|
|
398
|
+
Bytes payload;
|
|
399
|
+
bool fin = false;
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
// — outbound —
|
|
403
|
+
OutPacket new_packet(rudp::PacketType type);
|
|
404
|
+
void recycle(OutPacket& p);
|
|
405
|
+
void transmit(OutPacket& p, Clock::time_point now);
|
|
406
|
+
void send_control(rudp::PacketType type, Clock::time_point now);
|
|
407
|
+
void pump(Clock::time_point now);
|
|
408
|
+
void retransmit_lost(Clock::time_point now);
|
|
409
|
+
bool can_transmit() const noexcept;
|
|
410
|
+
/// Everything can_transmit() checks *except* the pacer: the state machine,
|
|
411
|
+
/// the peer's window and our own. Split out because pump() has to tell "the
|
|
412
|
+
/// pacer is holding this back" — which a timer resolves — from "a window is",
|
|
413
|
+
/// which only an acknowledgement can.
|
|
414
|
+
bool window_allows() const noexcept;
|
|
415
|
+
bool cwnd_allows(size_t bytes) const noexcept;
|
|
416
|
+
void fill_common(rudp::Packet& p) const;
|
|
417
|
+
uint16_t advertised_window() const noexcept;
|
|
418
|
+
|
|
419
|
+
// — pacing —
|
|
420
|
+
/// The window the pacer meters against: cwnd scaled by the phase's gain.
|
|
421
|
+
/// Zero means "not pacing" (no round-trip estimate yet, so no rate to pace at).
|
|
422
|
+
uint64_t pace_window() const noexcept;
|
|
423
|
+
/// Bytes the pacer may release over `dt` at the current rate.
|
|
424
|
+
uint64_t pace_bytes_over(Clock::duration dt) const noexcept;
|
|
425
|
+
/// Hand the token bucket whatever has accrued since it was last topped up.
|
|
426
|
+
void pace_accrue(Clock::time_point now);
|
|
427
|
+
/// Whether `bytes` may go out now. Always true when nothing is in flight —
|
|
428
|
+
/// the lone packet a recovering stream is allowed and the first packet after
|
|
429
|
+
/// an idle period must never be held back by a rate derived from an empty
|
|
430
|
+
/// pipe. A receiver's zero window is not this check's business: it stops the
|
|
431
|
+
/// packet earlier, in window_allows().
|
|
432
|
+
bool pace_allows(size_t bytes) const noexcept;
|
|
433
|
+
/// How long until `bytes` could be released. Zero when they can go now.
|
|
434
|
+
Clock::duration pace_wait(size_t bytes) const noexcept;
|
|
435
|
+
|
|
436
|
+
// — window validation after an idle period (RFC 2861) —
|
|
437
|
+
void restart_after_idle(Clock::time_point now);
|
|
438
|
+
|
|
439
|
+
// — HyStart++ (RFC 9406) —
|
|
440
|
+
void hystart_reset();
|
|
441
|
+
void hystart_sample(Clock::duration rtt);
|
|
442
|
+
void hystart_on_ack(uint32_t ack);
|
|
443
|
+
bool in_slow_start() const noexcept { return cwnd_ < ssthresh_; }
|
|
444
|
+
|
|
445
|
+
// — inbound —
|
|
446
|
+
void handle_ack(const rudp::Packet& p, Clock::time_point now);
|
|
447
|
+
void handle_retry(const rudp::Packet& p, Clock::time_point now);
|
|
448
|
+
void repair_sacked_holes(Clock::time_point now);
|
|
449
|
+
void handle_sequenced(const rudp::Packet& p);
|
|
450
|
+
void deliver(ByteView payload, bool fin);
|
|
451
|
+
void drain_reorder();
|
|
452
|
+
uint32_t sack_bitmap() const noexcept;
|
|
453
|
+
|
|
454
|
+
// — timing / congestion —
|
|
455
|
+
void on_rto(Clock::time_point now);
|
|
456
|
+
/// How long to wait before asking whether the tail got through. A round trip
|
|
457
|
+
/// plus what the peer may sit on an acknowledgement for, doubled per
|
|
458
|
+
/// consecutive unanswered probe.
|
|
459
|
+
Clock::duration probe_timeout() const noexcept;
|
|
460
|
+
/// What the one loss-detection timer should be set to right now: the probe
|
|
461
|
+
/// interval while there are probes left to spend, the retransmission timeout
|
|
462
|
+
/// once there are not. One timer, two meanings — which is how RFC 9002 models
|
|
463
|
+
/// it too, and why arming it in one place keeps the two from disagreeing.
|
|
464
|
+
Clock::duration loss_timeout() const noexcept;
|
|
465
|
+
void sample_rtt(Clock::duration rtt);
|
|
466
|
+
void enter_recovery();
|
|
467
|
+
void on_loss(bool timeout);
|
|
468
|
+
void grow_window(size_t acked_bytes);
|
|
469
|
+
|
|
470
|
+
void die(CloseReason reason);
|
|
471
|
+
void raise(uint32_t events) noexcept { events_ |= events; }
|
|
472
|
+
void flush_events();
|
|
473
|
+
|
|
474
|
+
UdpStreamHost& host_;
|
|
475
|
+
Address remote_;
|
|
476
|
+
uint32_t recv_id_;
|
|
477
|
+
uint32_t send_id_;
|
|
478
|
+
ConnRole role_;
|
|
479
|
+
ConnId conn_id_ = kInvalidConnId;
|
|
480
|
+
|
|
481
|
+
State state_;
|
|
482
|
+
CloseReason close_reason_ = CloseReason::PeerClosed;
|
|
483
|
+
bool peer_fin_ = false; ///< peer's Fin delivered in order
|
|
484
|
+
bool fin_queued_ = false; ///< our Fin is in the send queue
|
|
485
|
+
bool want_write_ = false;
|
|
486
|
+
/// A Retry has already been answered on this stream. The responder is entitled
|
|
487
|
+
/// to ask us to prove our address once; honouring a second one would let anyone
|
|
488
|
+
/// who can forge a datagram from the peer keep the dial going round forever.
|
|
489
|
+
bool retried_ = false;
|
|
490
|
+
uint32_t events_ = 0; ///< pending PollIn/PollOut/PollErr
|
|
491
|
+
|
|
492
|
+
// — send side —
|
|
493
|
+
// `sent_` holds transmitted-but-unacknowledged packets in strictly
|
|
494
|
+
// consecutive sequence order, which is the whole reason a selective ack can
|
|
495
|
+
// be resolved by index rather than by search: sent_[i].seq == sent_[0].seq + i.
|
|
496
|
+
std::deque<OutPacket> sent_;
|
|
497
|
+
std::deque<OutPacket> unsent_;
|
|
498
|
+
std::vector<Bytes> spare_; ///< retired packet buffers, kept for reuse
|
|
499
|
+
uint32_t next_seq_ = 1; ///< sequence number for the next packet created
|
|
500
|
+
size_t flight_bytes_ = 0; ///< payload bytes transmitted and not yet acked
|
|
501
|
+
size_t queued_bytes_ = 0; ///< payload bytes held by sent_ + unsent_
|
|
502
|
+
uint32_t cwnd_ = kInitialCwnd;
|
|
503
|
+
uint32_t ssthresh_ = kMaxCwnd;
|
|
504
|
+
uint16_t peer_window_ = rudp::kMaxWindowPackets;
|
|
505
|
+
/// The cumulative acknowledgement `peer_window_` came in on. What orders two
|
|
506
|
+
/// windows in time: a peer's ack never moves backwards, so a packet carrying
|
|
507
|
+
/// one that has is a packet from the past, and the window on it with it.
|
|
508
|
+
uint32_t window_ack_ = 0;
|
|
509
|
+
uint32_t last_ack_recv_ = 0;
|
|
510
|
+
int dup_acks_ = 0;
|
|
511
|
+
uint32_t retransmits_ = 0;
|
|
512
|
+
uint32_t window_reductions_ = 0;
|
|
513
|
+
/// Consecutive tail probes sent with nothing acknowledged in between. Reset by
|
|
514
|
+
/// any acknowledgement that covers new data, so it counts a single episode of
|
|
515
|
+
/// silence rather than the life of the stream.
|
|
516
|
+
int tail_probes_ = 0;
|
|
517
|
+
|
|
518
|
+
// Loss episodes. A window is reduced once per episode, not once per ack that
|
|
519
|
+
// happens to repair something — several acks arrive per round trip, and each
|
|
520
|
+
// of them halving again is what turns two losses in a window into a collapse
|
|
521
|
+
// to the floor. `recover_seq_` is the NewReno recovery point: the highest
|
|
522
|
+
// sequence number outstanding when the episode began, so the episode ends
|
|
523
|
+
// exactly when the cumulative ack has covered everything that was in flight
|
|
524
|
+
// when the loss was detected.
|
|
525
|
+
bool in_recovery_ = false;
|
|
526
|
+
uint32_t recover_seq_ = 0;
|
|
527
|
+
/// A retransmission timeout has given up on packets still sitting in `sent_`,
|
|
528
|
+
/// so retransmit_lost() has work to do. Purely a hint that keeps it from
|
|
529
|
+
/// scanning the whole queue on every acknowledgement of a healthy transfer.
|
|
530
|
+
bool have_lost_ = false;
|
|
531
|
+
|
|
532
|
+
// — pacing —
|
|
533
|
+
/// Bytes the pacer will currently let through, and when the bucket was last
|
|
534
|
+
/// topped up. Kept in bytes rather than packets so a partly filled tail
|
|
535
|
+
/// packet costs what it actually weighs.
|
|
536
|
+
uint64_t pace_tokens_ = 0;
|
|
537
|
+
Clock::time_point pace_last_{};
|
|
538
|
+
/// When the pacer expects to have enough for the packet it is holding back
|
|
539
|
+
/// (epoch = it is holding nothing). This is a deadline like any other: the
|
|
540
|
+
/// mux wakes the stream on it, and pump() re-arms or clears it.
|
|
541
|
+
Clock::time_point pace_due_{};
|
|
542
|
+
/// When data — not a bare acknowledgement — last went out. The keep-alive
|
|
543
|
+
/// writes last_send_ every ten seconds, so it cannot answer "has this stream
|
|
544
|
+
/// been sending?", which is exactly what the idle-restart rule needs to know.
|
|
545
|
+
Clock::time_point last_data_send_{};
|
|
546
|
+
|
|
547
|
+
// — HyStart++ —
|
|
548
|
+
/// Cautious phase: slow start has seen the round-trip time rise and is
|
|
549
|
+
/// probing gently until the rise is confirmed or withdrawn.
|
|
550
|
+
bool css_ = false;
|
|
551
|
+
int css_rounds_ = 0;
|
|
552
|
+
Clock::duration css_baseline_rtt_{};
|
|
553
|
+
/// Lowest round-trip time seen in this round and in the one before it. The
|
|
554
|
+
/// *minimum* is what matters: a queue building in front of the bottleneck
|
|
555
|
+
/// lifts even the luckiest packet's round trip, where an average would just
|
|
556
|
+
/// as easily be moved by one straggler.
|
|
557
|
+
Clock::duration round_min_rtt_{};
|
|
558
|
+
Clock::duration prev_round_min_rtt_{};
|
|
559
|
+
int round_samples_ = 0;
|
|
560
|
+
/// Highest sequence number outstanding when this round began; the round ends
|
|
561
|
+
/// when the cumulative acknowledgement reaches it.
|
|
562
|
+
uint32_t round_end_ = 0;
|
|
563
|
+
|
|
564
|
+
// — receive side —
|
|
565
|
+
ReceiveBuffer inbox_; ///< in-order bytes awaiting read()
|
|
566
|
+
std::unordered_map<uint32_t, InPacket> reorder_; ///< packets past a gap
|
|
567
|
+
uint32_t recv_next_ = 1; ///< next sequence number expected
|
|
568
|
+
bool need_ack_ = false;
|
|
569
|
+
int unacked_packets_ = 0;
|
|
570
|
+
/// Announcements left of a receive window that has just re-opened, and when the
|
|
571
|
+
/// next one is due (the epoch = at once). Zero means there is nothing to
|
|
572
|
+
/// announce, which is what arms this: the peer is stopped and sends nothing for
|
|
573
|
+
/// an update to ride on, so this is the only clock either side has for it.
|
|
574
|
+
int window_announces_ = 0;
|
|
575
|
+
Clock::time_point window_due_{};
|
|
576
|
+
/// Cached selective-ack bitmap, rebuilt only when the reorder buffer or the
|
|
577
|
+
/// expected sequence number moves. Every outgoing packet carries this field,
|
|
578
|
+
/// so deriving it from 32 hash lookups per *packet* — during loss recovery,
|
|
579
|
+
/// when packets are at their most frequent — was pure repeated work: it can
|
|
580
|
+
/// only change when one of the two things it is derived from changes.
|
|
581
|
+
mutable uint32_t sack_bits_ = 0;
|
|
582
|
+
mutable bool sack_dirty_ = false;
|
|
583
|
+
|
|
584
|
+
// — timing —
|
|
585
|
+
Clock::duration srtt_{};
|
|
586
|
+
Clock::duration rttvar_{};
|
|
587
|
+
bool have_rtt_ = false;
|
|
588
|
+
Clock::duration rto_ = kInitialRto;
|
|
589
|
+
// How the dial (and only the dial) is retried — see DialProfile. Kept as plain
|
|
590
|
+
// members rather than a stored profile so the SynSent path costs no indirection,
|
|
591
|
+
// and clamped in the constructor so a caller cannot ask for zero attempts or an
|
|
592
|
+
// interval outside what the timer can honour.
|
|
593
|
+
int syn_attempts_ = kSynMaxAttempts;
|
|
594
|
+
bool syn_backoff_ = true;
|
|
595
|
+
Clock::time_point last_recv_;
|
|
596
|
+
Clock::time_point last_send_;
|
|
597
|
+
/// When an owed acknowledgement must go out. The epoch means "no deadline",
|
|
598
|
+
/// which with need_ack_ set is not "never" but "at once": read() leaves it that
|
|
599
|
+
/// way to ask for a window update, because a peer stopped on a zero window sends
|
|
600
|
+
/// nothing for the ack to ride on. Every other owed ack arms a real deadline.
|
|
601
|
+
Clock::time_point ack_due_{};
|
|
602
|
+
/// When the retransmission timeout fires (epoch = the timer is not running).
|
|
603
|
+
///
|
|
604
|
+
/// One deadline for the stream, not one per packet — RFC 6298's model. It is
|
|
605
|
+
/// started by a transmission that finds it idle, restarted whenever an
|
|
606
|
+
/// acknowledgement covers new data, and turned off once nothing is outstanding.
|
|
607
|
+
/// Deriving it instead from `sent_.front().sent_at` (the obvious shortcut) is
|
|
608
|
+
/// wrong after a timeout: the packets behind the one that was resent still
|
|
609
|
+
/// carry their original timestamps, so every one of them looks instantly
|
|
610
|
+
/// overdue and the window is driven back to the floor on every tick.
|
|
611
|
+
Clock::time_point rto_deadline_{};
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
} // namespace librats
|