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
package/src/librats_node.cpp
CHANGED
|
@@ -1,1419 +1,1163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LibRats Node.js native addon.
|
|
3
|
+
*
|
|
4
|
+
* N-API wrapper over the canonical C ABI in src/bindings/rats.h. Callbacks fire
|
|
5
|
+
* on librats' internal reactor thread, so every native callback marshals into
|
|
6
|
+
* the JS thread with a Napi::ThreadSafeFunction (TSFN). Per-channel / per-topic
|
|
7
|
+
* / per-json-type handlers are kept in maps owned by the RatsNode instance and
|
|
8
|
+
* torn down on destruction.
|
|
9
|
+
*
|
|
10
|
+
* Contract reminder (enforced by the C core, surfaced here as thrown errors):
|
|
11
|
+
* - Register callbacks and enable subsystems BEFORE start().
|
|
12
|
+
* - Calling an enable after start() -> RATS_ERR_ALREADY_STARTED.
|
|
13
|
+
* - Calling a subsystem op before its enable -> RATS_ERR_NOT_ENABLED.
|
|
14
|
+
*/
|
|
15
|
+
|
|
1
16
|
#include <napi.h>
|
|
2
|
-
#include <
|
|
3
|
-
#include <string>
|
|
17
|
+
#include <cstring>
|
|
4
18
|
#include <memory>
|
|
5
|
-
#include <
|
|
6
|
-
#include
|
|
19
|
+
#include <string>
|
|
20
|
+
#include <vector>
|
|
21
|
+
#include <librats/bindings/rats.h>
|
|
7
22
|
|
|
8
23
|
using namespace Napi;
|
|
9
24
|
|
|
10
|
-
|
|
11
|
-
class RatsClient;
|
|
25
|
+
namespace {
|
|
12
26
|
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
std::unordered_map<rats_client_t, std::shared_ptr<CallbackData>> connection_callbacks;
|
|
22
|
-
std::unordered_map<rats_client_t, std::shared_ptr<CallbackData>> string_callbacks;
|
|
23
|
-
std::unordered_map<rats_client_t, std::shared_ptr<CallbackData>> binary_callbacks;
|
|
24
|
-
std::unordered_map<rats_client_t, std::shared_ptr<CallbackData>> json_callbacks;
|
|
25
|
-
std::unordered_map<rats_client_t, std::shared_ptr<CallbackData>> disconnect_callbacks;
|
|
26
|
-
std::unordered_map<rats_client_t, std::shared_ptr<CallbackData>> file_progress_callbacks;
|
|
27
|
-
std::unordered_map<rats_client_t, std::shared_ptr<CallbackData>> file_request_callbacks;
|
|
28
|
-
|
|
29
|
-
// C callback wrappers
|
|
30
|
-
void connection_callback_wrapper(void* user_data, const char* peer_id) {
|
|
31
|
-
rats_client_t client = static_cast<rats_client_t>(user_data);
|
|
32
|
-
auto it = connection_callbacks.find(client);
|
|
33
|
-
if (it != connection_callbacks.end() && it->second) {
|
|
34
|
-
auto callback_data = it->second;
|
|
35
|
-
callback_data->callback.Call({Napi::String::New(callback_data->env, peer_id)});
|
|
36
|
-
}
|
|
27
|
+
// Translate a rats_error_t into a JS exception when it is not RATS_OK.
|
|
28
|
+
// Returns true if an error was thrown (caller should bail out / return).
|
|
29
|
+
bool throw_on_error(Napi::Env env, rats_error_t err) {
|
|
30
|
+
if (err == RATS_OK) return false;
|
|
31
|
+
Napi::Error::New(env, std::string("librats: ") + rats_error_str(err))
|
|
32
|
+
.ThrowAsJavaScriptException();
|
|
33
|
+
return true;
|
|
37
34
|
}
|
|
38
35
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
Napi::
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
void binary_callback_wrapper(void* user_data, const char* peer_id, const void* data, size_t size) {
|
|
52
|
-
rats_client_t client = static_cast<rats_client_t>(user_data);
|
|
53
|
-
auto it = binary_callbacks.find(client);
|
|
54
|
-
if (it != binary_callbacks.end() && it->second) {
|
|
55
|
-
auto callback_data = it->second;
|
|
56
|
-
auto buffer = Napi::Buffer<uint8_t>::New(callback_data->env, size);
|
|
57
|
-
memcpy(buffer.Data(), data, size);
|
|
58
|
-
callback_data->callback.Call({
|
|
59
|
-
Napi::String::New(callback_data->env, peer_id),
|
|
60
|
-
buffer
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
}
|
|
36
|
+
// Every instance method funnels through this: the C ABI does not null-check its
|
|
37
|
+
// handle, and destroy() legitimately leaves us without one. The variadic
|
|
38
|
+
// argument is the value to return — empty for a void method.
|
|
39
|
+
#define RATS_REQUIRE_NODE(...) \
|
|
40
|
+
do { \
|
|
41
|
+
if (!node_) { \
|
|
42
|
+
Napi::Error::New(info.Env(), "librats: node has been destroyed") \
|
|
43
|
+
.ThrowAsJavaScriptException(); \
|
|
44
|
+
return __VA_ARGS__; \
|
|
45
|
+
} \
|
|
46
|
+
} while (0)
|
|
64
47
|
|
|
65
|
-
|
|
66
|
-
rats_client_t client = static_cast<rats_client_t>(user_data);
|
|
67
|
-
auto it = json_callbacks.find(client);
|
|
68
|
-
if (it != json_callbacks.end() && it->second) {
|
|
69
|
-
auto callback_data = it->second;
|
|
70
|
-
callback_data->callback.Call({
|
|
71
|
-
Napi::String::New(callback_data->env, peer_id),
|
|
72
|
-
Napi::String::New(callback_data->env, json_str)
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
}
|
|
48
|
+
} // namespace
|
|
76
49
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
callback_data->callback.Call({Napi::String::New(callback_data->env, peer_id)});
|
|
83
|
-
}
|
|
84
|
-
}
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Per-callback context. Each registration owns a TSFN plus a back-pointer used
|
|
52
|
+
// only for cleanup bookkeeping. The trampoline (the C function we hand to
|
|
53
|
+
// librats) receives the context as `user`.
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
85
55
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (it != file_progress_callbacks.end() && it->second) {
|
|
90
|
-
auto callback_data = it->second;
|
|
91
|
-
callback_data->callback.Call({
|
|
92
|
-
Napi::String::New(callback_data->env, transfer_id),
|
|
93
|
-
Napi::Number::New(callback_data->env, progress_percent),
|
|
94
|
-
Napi::String::New(callback_data->env, status)
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
}
|
|
56
|
+
struct CbContext {
|
|
57
|
+
Napi::ThreadSafeFunction tsfn;
|
|
58
|
+
bool acquired = false;
|
|
98
59
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
Napi::String::New(callback_data->env, peer_id),
|
|
110
|
-
Napi::String::New(callback_data->env, transfer_id),
|
|
111
|
-
Napi::String::New(callback_data->env, filename)
|
|
112
|
-
});
|
|
60
|
+
void init(Napi::Env env, const Napi::Function& fn, const char* name) {
|
|
61
|
+
release();
|
|
62
|
+
tsfn = Napi::ThreadSafeFunction::New(env, fn, name, 0, 1);
|
|
63
|
+
acquired = true;
|
|
64
|
+
}
|
|
65
|
+
void release() {
|
|
66
|
+
if (acquired) {
|
|
67
|
+
tsfn.Release();
|
|
68
|
+
acquired = false;
|
|
69
|
+
}
|
|
113
70
|
}
|
|
114
|
-
}
|
|
71
|
+
};
|
|
115
72
|
|
|
116
|
-
class
|
|
73
|
+
class RatsNode : public Napi::ObjectWrap<RatsNode> {
|
|
117
74
|
public:
|
|
118
|
-
static Napi::Object Init(Napi::Env env, Napi::Object exports)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
InstanceMethod("stop", &RatsClient::Stop),
|
|
122
|
-
InstanceMethod("connect", &RatsClient::Connect),
|
|
123
|
-
InstanceMethod("disconnect", &RatsClient::Disconnect),
|
|
124
|
-
InstanceMethod("broadcastString", &RatsClient::BroadcastString),
|
|
125
|
-
InstanceMethod("sendString", &RatsClient::SendString),
|
|
126
|
-
InstanceMethod("broadcastBinary", &RatsClient::BroadcastBinary),
|
|
127
|
-
InstanceMethod("sendBinary", &RatsClient::SendBinary),
|
|
128
|
-
InstanceMethod("broadcastJson", &RatsClient::BroadcastJson),
|
|
129
|
-
InstanceMethod("sendJson", &RatsClient::SendJson),
|
|
130
|
-
InstanceMethod("getPeerCount", &RatsClient::GetPeerCount),
|
|
131
|
-
InstanceMethod("getListenPort", &RatsClient::GetListenPort),
|
|
132
|
-
InstanceMethod("getOurPeerId", &RatsClient::GetOurPeerId),
|
|
133
|
-
InstanceMethod("getPeerIds", &RatsClient::GetPeerIds),
|
|
134
|
-
InstanceMethod("getConnectionStatistics", &RatsClient::GetConnectionStatistics),
|
|
135
|
-
InstanceMethod("setMaxPeers", &RatsClient::SetMaxPeers),
|
|
136
|
-
InstanceMethod("getMaxPeers", &RatsClient::GetMaxPeers),
|
|
137
|
-
InstanceMethod("isPeerLimitReached", &RatsClient::IsPeerLimitReached),
|
|
138
|
-
|
|
139
|
-
// DHT methods
|
|
140
|
-
InstanceMethod("startDhtDiscovery", &RatsClient::StartDhtDiscovery),
|
|
141
|
-
InstanceMethod("stopDhtDiscovery", &RatsClient::StopDhtDiscovery),
|
|
142
|
-
InstanceMethod("isDhtRunning", &RatsClient::IsDhtRunning),
|
|
143
|
-
InstanceMethod("announceForHash", &RatsClient::AnnounceForHash),
|
|
144
|
-
InstanceMethod("getDhtRoutingTableSize", &RatsClient::GetDhtRoutingTableSize),
|
|
145
|
-
|
|
146
|
-
// mDNS methods
|
|
147
|
-
InstanceMethod("startMdnsDiscovery", &RatsClient::StartMdnsDiscovery),
|
|
148
|
-
InstanceMethod("stopMdnsDiscovery", &RatsClient::StopMdnsDiscovery),
|
|
149
|
-
InstanceMethod("isMdnsRunning", &RatsClient::IsMdnsRunning),
|
|
150
|
-
InstanceMethod("queryMdnsServices", &RatsClient::QueryMdnsServices),
|
|
151
|
-
|
|
152
|
-
// Basic Encryption methods
|
|
153
|
-
InstanceMethod("setEncryptionEnabled", &RatsClient::SetEncryptionEnabled),
|
|
154
|
-
InstanceMethod("isEncryptionEnabled", &RatsClient::IsEncryptionEnabled),
|
|
155
|
-
|
|
156
|
-
// Enhanced Encryption methods
|
|
157
|
-
InstanceMethod("initializeEncryption", &RatsClient::InitializeEncryption),
|
|
158
|
-
InstanceMethod("isPeerEncrypted", &RatsClient::IsPeerEncrypted),
|
|
159
|
-
InstanceMethod("setNoiseStaticKeypair", &RatsClient::SetNoiseStaticKeypair),
|
|
160
|
-
InstanceMethod("getNoiseStaticPublicKey", &RatsClient::GetNoiseStaticPublicKey),
|
|
161
|
-
InstanceMethod("getPeerNoisePublicKey", &RatsClient::GetPeerNoisePublicKey),
|
|
162
|
-
InstanceMethod("getPeerHandshakeHash", &RatsClient::GetPeerHandshakeHash),
|
|
163
|
-
|
|
164
|
-
// ICE (NAT Traversal) methods
|
|
165
|
-
InstanceMethod("isIceAvailable", &RatsClient::IsIceAvailable),
|
|
166
|
-
InstanceMethod("addStunServer", &RatsClient::AddStunServer),
|
|
167
|
-
InstanceMethod("addTurnServer", &RatsClient::AddTurnServer),
|
|
168
|
-
InstanceMethod("clearIceServers", &RatsClient::ClearIceServers),
|
|
169
|
-
InstanceMethod("gatherIceCandidates", &RatsClient::GatherIceCandidates),
|
|
170
|
-
InstanceMethod("getIceCandidates", &RatsClient::GetIceCandidates),
|
|
171
|
-
InstanceMethod("isIceGatheringComplete", &RatsClient::IsIceGatheringComplete),
|
|
172
|
-
InstanceMethod("getPublicAddress", &RatsClient::GetPublicAddress),
|
|
173
|
-
InstanceMethod("discoverPublicAddress", &RatsClient::DiscoverPublicAddress),
|
|
174
|
-
InstanceMethod("addRemoteIceCandidate", &RatsClient::AddRemoteIceCandidate),
|
|
175
|
-
InstanceMethod("endOfRemoteIceCandidates", &RatsClient::EndOfRemoteIceCandidates),
|
|
176
|
-
InstanceMethod("startIceChecks", &RatsClient::StartIceChecks),
|
|
177
|
-
InstanceMethod("getIceConnectionState", &RatsClient::GetIceConnectionState),
|
|
178
|
-
InstanceMethod("getIceGatheringState", &RatsClient::GetIceGatheringState),
|
|
179
|
-
InstanceMethod("isIceConnected", &RatsClient::IsIceConnected),
|
|
180
|
-
InstanceMethod("getIceSelectedPair", &RatsClient::GetIceSelectedPair),
|
|
181
|
-
InstanceMethod("closeIce", &RatsClient::CloseIce),
|
|
182
|
-
InstanceMethod("restartIce", &RatsClient::RestartIce),
|
|
183
|
-
|
|
184
|
-
// GossipSub methods
|
|
185
|
-
InstanceMethod("isGossipsubAvailable", &RatsClient::IsGossipsubAvailable),
|
|
186
|
-
InstanceMethod("isGossipsubRunning", &RatsClient::IsGossipsubRunning),
|
|
187
|
-
InstanceMethod("subscribeToTopic", &RatsClient::SubscribeToTopic),
|
|
188
|
-
InstanceMethod("unsubscribeFromTopic", &RatsClient::UnsubscribeFromTopic),
|
|
189
|
-
InstanceMethod("isSubscribedToTopic", &RatsClient::IsSubscribedToTopic),
|
|
190
|
-
InstanceMethod("getSubscribedTopics", &RatsClient::GetSubscribedTopics),
|
|
191
|
-
InstanceMethod("publishToTopic", &RatsClient::PublishToTopic),
|
|
192
|
-
InstanceMethod("publishJsonToTopic", &RatsClient::PublishJsonToTopic),
|
|
193
|
-
InstanceMethod("getTopicPeers", &RatsClient::GetTopicPeers),
|
|
194
|
-
InstanceMethod("getGossipsubStatistics", &RatsClient::GetGossipsubStatistics),
|
|
195
|
-
|
|
196
|
-
// File Transfer methods
|
|
197
|
-
InstanceMethod("sendFile", &RatsClient::SendFile),
|
|
198
|
-
InstanceMethod("sendDirectory", &RatsClient::SendDirectory),
|
|
199
|
-
InstanceMethod("acceptFileTransfer", &RatsClient::AcceptFileTransfer),
|
|
200
|
-
InstanceMethod("rejectFileTransfer", &RatsClient::RejectFileTransfer),
|
|
201
|
-
InstanceMethod("cancelFileTransfer", &RatsClient::CancelFileTransfer),
|
|
202
|
-
InstanceMethod("pauseFileTransfer", &RatsClient::PauseFileTransfer),
|
|
203
|
-
InstanceMethod("resumeFileTransfer", &RatsClient::ResumeFileTransfer),
|
|
204
|
-
InstanceMethod("getFileTransferProgress", &RatsClient::GetFileTransferProgress),
|
|
205
|
-
InstanceMethod("getFileTransferStatistics", &RatsClient::GetFileTransferStatistics),
|
|
206
|
-
|
|
207
|
-
// Address blocking
|
|
208
|
-
InstanceMethod("addIgnoredAddress", &RatsClient::AddIgnoredAddress),
|
|
209
|
-
|
|
210
|
-
// Callback methods
|
|
211
|
-
InstanceMethod("onConnection", &RatsClient::OnConnection),
|
|
212
|
-
InstanceMethod("onString", &RatsClient::OnString),
|
|
213
|
-
InstanceMethod("onBinary", &RatsClient::OnBinary),
|
|
214
|
-
InstanceMethod("onJson", &RatsClient::OnJson),
|
|
215
|
-
InstanceMethod("onDisconnect", &RatsClient::OnDisconnect),
|
|
216
|
-
InstanceMethod("onFileProgress", &RatsClient::OnFileProgress),
|
|
217
|
-
InstanceMethod("onFileRequest", &RatsClient::OnFileRequest),
|
|
218
|
-
|
|
219
|
-
// Configuration persistence
|
|
220
|
-
InstanceMethod("loadConfiguration", &RatsClient::LoadConfiguration),
|
|
221
|
-
InstanceMethod("saveConfiguration", &RatsClient::SaveConfiguration),
|
|
222
|
-
InstanceMethod("setDataDirectory", &RatsClient::SetDataDirectory),
|
|
223
|
-
InstanceMethod("getDataDirectory", &RatsClient::GetDataDirectory)
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
exports.Set("RatsClient", func);
|
|
227
|
-
return exports;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
RatsClient(const Napi::CallbackInfo& info) : Napi::ObjectWrap<RatsClient>(info), client_(nullptr) {
|
|
231
|
-
Napi::Env env = info.Env();
|
|
232
|
-
|
|
233
|
-
if (info.Length() < 1 || !info[0].IsNumber()) {
|
|
234
|
-
Napi::TypeError::New(env, "Expected port number").ThrowAsJavaScriptException();
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
int port = info[0].As<Napi::Number>().Int32Value();
|
|
239
|
-
if (port < 0 || port > 65535) {
|
|
240
|
-
Napi::RangeError::New(env, "Port number must be between 0 and 65535").ThrowAsJavaScriptException();
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
client_ = rats_create(port);
|
|
244
|
-
|
|
245
|
-
if (!client_) {
|
|
246
|
-
Napi::Error::New(env, "Failed to create RatsClient").ThrowAsJavaScriptException();
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
~RatsClient() {
|
|
252
|
-
if (client_ != nullptr) {
|
|
253
|
-
// Clean up callbacks
|
|
254
|
-
connection_callbacks.erase(client_);
|
|
255
|
-
string_callbacks.erase(client_);
|
|
256
|
-
binary_callbacks.erase(client_);
|
|
257
|
-
json_callbacks.erase(client_);
|
|
258
|
-
disconnect_callbacks.erase(client_);
|
|
259
|
-
file_progress_callbacks.erase(client_);
|
|
260
|
-
file_request_callbacks.erase(client_);
|
|
261
|
-
|
|
262
|
-
rats_destroy(client_);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
75
|
+
static Napi::Object Init(Napi::Env env, Napi::Object exports);
|
|
76
|
+
RatsNode(const Napi::CallbackInfo& info);
|
|
77
|
+
~RatsNode();
|
|
265
78
|
|
|
266
79
|
private:
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
//
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
if (info.Length() < 2 || !info[0].IsString() || !info[1].IsNumber()) {
|
|
286
|
-
Napi::TypeError::New(env, "Expected host (string) and port (number)").ThrowAsJavaScriptException();
|
|
287
|
-
return env.Null();
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
std::string host = info[0].As<Napi::String>().Utf8Value();
|
|
291
|
-
int port = info[1].As<Napi::Number>().Int32Value();
|
|
292
|
-
|
|
293
|
-
int result = rats_connect(client_, host.c_str(), port);
|
|
294
|
-
return Napi::Boolean::New(env, result == 1);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
void Disconnect(const Napi::CallbackInfo& info) {
|
|
298
|
-
if (info.Length() < 1 || !info[0].IsString()) {
|
|
299
|
-
Napi::TypeError::New(info.Env(), "Expected peer_id (string)").ThrowAsJavaScriptException();
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
std::string peer_id = info[0].As<Napi::String>().Utf8Value();
|
|
304
|
-
rats_disconnect_peer_by_id(client_, peer_id.c_str());
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
Napi::Value BroadcastString(const Napi::CallbackInfo& info) {
|
|
308
|
-
Napi::Env env = info.Env();
|
|
309
|
-
|
|
310
|
-
if (info.Length() < 1 || !info[0].IsString()) {
|
|
311
|
-
Napi::TypeError::New(env, "Expected message (string)").ThrowAsJavaScriptException();
|
|
312
|
-
return env.Null();
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
std::string message = info[0].As<Napi::String>().Utf8Value();
|
|
316
|
-
int result = rats_broadcast_string(client_, message.c_str());
|
|
317
|
-
return Napi::Number::New(env, result);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
Napi::Value SendString(const Napi::CallbackInfo& info) {
|
|
321
|
-
Napi::Env env = info.Env();
|
|
322
|
-
|
|
323
|
-
if (info.Length() < 2 || !info[0].IsString() || !info[1].IsString()) {
|
|
324
|
-
Napi::TypeError::New(env, "Expected peer_id (string) and message (string)").ThrowAsJavaScriptException();
|
|
325
|
-
return env.Null();
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
std::string peer_id = info[0].As<Napi::String>().Utf8Value();
|
|
329
|
-
std::string message = info[1].As<Napi::String>().Utf8Value();
|
|
330
|
-
|
|
331
|
-
int result = rats_send_string(client_, peer_id.c_str(), message.c_str());
|
|
332
|
-
return Napi::Boolean::New(env, result == RATS_SUCCESS);
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
Napi::Value BroadcastBinary(const Napi::CallbackInfo& info) {
|
|
336
|
-
Napi::Env env = info.Env();
|
|
337
|
-
|
|
338
|
-
if (info.Length() < 1 || !info[0].IsBuffer()) {
|
|
339
|
-
Napi::TypeError::New(env, "Expected data (Buffer)").ThrowAsJavaScriptException();
|
|
340
|
-
return env.Null();
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
Napi::Buffer<uint8_t> buffer = info[0].As<Napi::Buffer<uint8_t>>();
|
|
344
|
-
int result = rats_broadcast_binary(client_, buffer.Data(), buffer.Length());
|
|
345
|
-
return Napi::Number::New(env, result);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
Napi::Value SendBinary(const Napi::CallbackInfo& info) {
|
|
349
|
-
Napi::Env env = info.Env();
|
|
350
|
-
|
|
351
|
-
if (info.Length() < 2 || !info[0].IsString() || !info[1].IsBuffer()) {
|
|
352
|
-
Napi::TypeError::New(env, "Expected peer_id (string) and data (Buffer)").ThrowAsJavaScriptException();
|
|
353
|
-
return env.Null();
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
std::string peer_id = info[0].As<Napi::String>().Utf8Value();
|
|
357
|
-
Napi::Buffer<uint8_t> buffer = info[1].As<Napi::Buffer<uint8_t>>();
|
|
358
|
-
|
|
359
|
-
rats_error_t result = rats_send_binary(client_, peer_id.c_str(), buffer.Data(), buffer.Length());
|
|
360
|
-
return Napi::Boolean::New(env, result == RATS_SUCCESS);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
Napi::Value BroadcastJson(const Napi::CallbackInfo& info) {
|
|
364
|
-
Napi::Env env = info.Env();
|
|
365
|
-
|
|
366
|
-
if (info.Length() < 1 || !info[0].IsString()) {
|
|
367
|
-
Napi::TypeError::New(env, "Expected json_str (string)").ThrowAsJavaScriptException();
|
|
368
|
-
return env.Null();
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
std::string json_str = info[0].As<Napi::String>().Utf8Value();
|
|
372
|
-
int result = rats_broadcast_json(client_, json_str.c_str());
|
|
373
|
-
return Napi::Number::New(env, result);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
Napi::Value SendJson(const Napi::CallbackInfo& info) {
|
|
377
|
-
Napi::Env env = info.Env();
|
|
378
|
-
|
|
379
|
-
if (info.Length() < 2 || !info[0].IsString() || !info[1].IsString()) {
|
|
380
|
-
Napi::TypeError::New(env, "Expected peer_id (string) and json_str (string)").ThrowAsJavaScriptException();
|
|
381
|
-
return env.Null();
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
std::string peer_id = info[0].As<Napi::String>().Utf8Value();
|
|
385
|
-
std::string json_str = info[1].As<Napi::String>().Utf8Value();
|
|
386
|
-
|
|
387
|
-
rats_error_t result = rats_send_json(client_, peer_id.c_str(), json_str.c_str());
|
|
388
|
-
return Napi::Boolean::New(env, result == RATS_SUCCESS);
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
// Info methods
|
|
392
|
-
Napi::Value GetPeerCount(const Napi::CallbackInfo& info) {
|
|
393
|
-
Napi::Env env = info.Env();
|
|
394
|
-
int count = rats_get_peer_count(client_);
|
|
395
|
-
return Napi::Number::New(env, count);
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
Napi::Value GetListenPort(const Napi::CallbackInfo& info) {
|
|
399
|
-
Napi::Env env = info.Env();
|
|
400
|
-
int port = rats_get_listen_port(client_);
|
|
401
|
-
return Napi::Number::New(env, port);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
Napi::Value GetOurPeerId(const Napi::CallbackInfo& info) {
|
|
405
|
-
Napi::Env env = info.Env();
|
|
406
|
-
char* peer_id = rats_get_our_peer_id(client_);
|
|
407
|
-
if (!peer_id) return env.Null();
|
|
408
|
-
|
|
409
|
-
Napi::String result = Napi::String::New(env, peer_id);
|
|
410
|
-
rats_string_free(peer_id);
|
|
411
|
-
return result;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
Napi::Value GetPeerIds(const Napi::CallbackInfo& info) {
|
|
415
|
-
Napi::Env env = info.Env();
|
|
416
|
-
int count = 0;
|
|
417
|
-
char** peer_ids = rats_get_peer_ids(client_, &count);
|
|
418
|
-
|
|
419
|
-
if (!peer_ids || count == 0) {
|
|
420
|
-
return Napi::Array::New(env, 0);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
Napi::Array result = Napi::Array::New(env, count);
|
|
424
|
-
for (int i = 0; i < count; i++) {
|
|
425
|
-
result[i] = Napi::String::New(env, peer_ids[i]);
|
|
426
|
-
rats_string_free(peer_ids[i]);
|
|
427
|
-
}
|
|
428
|
-
free(peer_ids);
|
|
429
|
-
|
|
430
|
-
return result;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
Napi::Value GetConnectionStatistics(const Napi::CallbackInfo& info) {
|
|
434
|
-
Napi::Env env = info.Env();
|
|
435
|
-
char* stats_json = rats_get_connection_statistics_json(client_);
|
|
436
|
-
if (!stats_json) return env.Null();
|
|
437
|
-
|
|
438
|
-
Napi::String result = Napi::String::New(env, stats_json);
|
|
439
|
-
rats_string_free(stats_json);
|
|
440
|
-
return result;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
// Peer configuration
|
|
444
|
-
Napi::Value SetMaxPeers(const Napi::CallbackInfo& info) {
|
|
445
|
-
Napi::Env env = info.Env();
|
|
446
|
-
|
|
447
|
-
if (info.Length() < 1 || !info[0].IsNumber()) {
|
|
448
|
-
Napi::TypeError::New(env, "Expected max_peers (number)").ThrowAsJavaScriptException();
|
|
449
|
-
return env.Null();
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
int max_peers = info[0].As<Napi::Number>().Int32Value();
|
|
453
|
-
rats_error_t result = rats_set_max_peers(client_, max_peers);
|
|
454
|
-
return Napi::Boolean::New(env, result == RATS_SUCCESS);
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
Napi::Value GetMaxPeers(const Napi::CallbackInfo& info) {
|
|
458
|
-
Napi::Env env = info.Env();
|
|
459
|
-
int max_peers = rats_get_max_peers(client_);
|
|
460
|
-
return Napi::Number::New(env, max_peers);
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
Napi::Value IsPeerLimitReached(const Napi::CallbackInfo& info) {
|
|
464
|
-
Napi::Env env = info.Env();
|
|
465
|
-
int reached = rats_is_peer_limit_reached(client_);
|
|
466
|
-
return Napi::Boolean::New(env, reached != 0);
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
// DHT methods
|
|
470
|
-
Napi::Value StartDhtDiscovery(const Napi::CallbackInfo& info) {
|
|
471
|
-
Napi::Env env = info.Env();
|
|
472
|
-
|
|
473
|
-
if (info.Length() < 1 || !info[0].IsNumber()) {
|
|
474
|
-
Napi::TypeError::New(env, "Expected dht_port (number)").ThrowAsJavaScriptException();
|
|
475
|
-
return env.Null();
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
int dht_port = info[0].As<Napi::Number>().Int32Value();
|
|
479
|
-
rats_error_t result = rats_start_dht_discovery(client_, dht_port);
|
|
480
|
-
return Napi::Boolean::New(env, result == RATS_SUCCESS);
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
void StopDhtDiscovery(const Napi::CallbackInfo& info) {
|
|
484
|
-
rats_stop_dht_discovery(client_);
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
Napi::Value IsDhtRunning(const Napi::CallbackInfo& info) {
|
|
488
|
-
Napi::Env env = info.Env();
|
|
489
|
-
int running = rats_is_dht_running(client_);
|
|
490
|
-
return Napi::Boolean::New(env, running != 0);
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
Napi::Value AnnounceForHash(const Napi::CallbackInfo& info) {
|
|
494
|
-
Napi::Env env = info.Env();
|
|
495
|
-
|
|
496
|
-
if (info.Length() < 2 || !info[0].IsString() || !info[1].IsNumber()) {
|
|
497
|
-
Napi::TypeError::New(env, "Expected content_hash (string) and port (number)").ThrowAsJavaScriptException();
|
|
498
|
-
return env.Null();
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
std::string content_hash = info[0].As<Napi::String>().Utf8Value();
|
|
502
|
-
int port = info[1].As<Napi::Number>().Int32Value();
|
|
503
|
-
|
|
504
|
-
// Check if optional callback is provided
|
|
505
|
-
rats_peers_found_cb c_callback = nullptr;
|
|
506
|
-
void* callback_user_data = nullptr;
|
|
507
|
-
Napi::ThreadSafeFunction* tsfn_ptr = nullptr;
|
|
508
|
-
|
|
509
|
-
if (info.Length() >= 3 && info[2].IsFunction()) {
|
|
510
|
-
// Create thread-safe function for callback
|
|
511
|
-
auto tsfn = new Napi::ThreadSafeFunction();
|
|
512
|
-
*tsfn = Napi::ThreadSafeFunction::New(
|
|
513
|
-
env,
|
|
514
|
-
info[2].As<Napi::Function>(),
|
|
515
|
-
"AnnounceForHashCallback",
|
|
516
|
-
0,
|
|
517
|
-
1,
|
|
518
|
-
[](Napi::Env) {} // Release callback
|
|
519
|
-
);
|
|
520
|
-
tsfn_ptr = tsfn;
|
|
521
|
-
|
|
522
|
-
c_callback = [](void* user_data, const char** peer_addresses, int count) {
|
|
523
|
-
auto* tsfn = static_cast<Napi::ThreadSafeFunction*>(user_data);
|
|
524
|
-
if (!tsfn) return;
|
|
525
|
-
|
|
526
|
-
// Copy peer addresses for async callback
|
|
527
|
-
std::vector<std::string>* peers = new std::vector<std::string>();
|
|
528
|
-
for (int i = 0; i < count; i++) {
|
|
529
|
-
if (peer_addresses[i]) {
|
|
530
|
-
peers->push_back(peer_addresses[i]);
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
tsfn->BlockingCall(peers, [](Napi::Env env, Napi::Function jsCallback, std::vector<std::string>* data) {
|
|
535
|
-
Napi::Array arr = Napi::Array::New(env, data->size());
|
|
536
|
-
for (size_t i = 0; i < data->size(); i++) {
|
|
537
|
-
arr.Set(static_cast<uint32_t>(i), Napi::String::New(env, (*data)[i]));
|
|
538
|
-
}
|
|
539
|
-
jsCallback.Call({arr});
|
|
540
|
-
delete data;
|
|
541
|
-
});
|
|
542
|
-
|
|
543
|
-
tsfn->Release();
|
|
544
|
-
delete tsfn;
|
|
545
|
-
};
|
|
546
|
-
callback_user_data = tsfn_ptr;
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
rats_error_t result = rats_announce_for_hash(client_, content_hash.c_str(), port, c_callback, callback_user_data);
|
|
550
|
-
return Napi::Boolean::New(env, result == RATS_SUCCESS);
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
Napi::Value GetDhtRoutingTableSize(const Napi::CallbackInfo& info) {
|
|
554
|
-
Napi::Env env = info.Env();
|
|
555
|
-
size_t size = rats_get_dht_routing_table_size(client_);
|
|
556
|
-
return Napi::Number::New(env, static_cast<double>(size));
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
// mDNS methods
|
|
560
|
-
Napi::Value StartMdnsDiscovery(const Napi::CallbackInfo& info) {
|
|
561
|
-
Napi::Env env = info.Env();
|
|
562
|
-
|
|
563
|
-
std::string service;
|
|
564
|
-
const char* service_name = nullptr;
|
|
565
|
-
if (info.Length() > 0 && info[0].IsString()) {
|
|
566
|
-
service = info[0].As<Napi::String>().Utf8Value();
|
|
567
|
-
service_name = service.c_str();
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
rats_error_t result = rats_start_mdns_discovery(client_, service_name);
|
|
571
|
-
return Napi::Boolean::New(env, result == RATS_SUCCESS);
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
void StopMdnsDiscovery(const Napi::CallbackInfo& info) {
|
|
575
|
-
rats_stop_mdns_discovery(client_);
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
Napi::Value IsMdnsRunning(const Napi::CallbackInfo& info) {
|
|
579
|
-
Napi::Env env = info.Env();
|
|
580
|
-
int running = rats_is_mdns_running(client_);
|
|
581
|
-
return Napi::Boolean::New(env, running != 0);
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
Napi::Value QueryMdnsServices(const Napi::CallbackInfo& info) {
|
|
585
|
-
Napi::Env env = info.Env();
|
|
586
|
-
rats_error_t result = rats_query_mdns_services(client_);
|
|
587
|
-
return Napi::Boolean::New(env, result == RATS_SUCCESS);
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
// Encryption methods
|
|
591
|
-
Napi::Value SetEncryptionEnabled(const Napi::CallbackInfo& info) {
|
|
592
|
-
Napi::Env env = info.Env();
|
|
593
|
-
|
|
594
|
-
if (info.Length() < 1 || !info[0].IsBoolean()) {
|
|
595
|
-
Napi::TypeError::New(env, "Expected enabled (boolean)").ThrowAsJavaScriptException();
|
|
596
|
-
return env.Null();
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
bool enabled = info[0].As<Napi::Boolean>().Value();
|
|
600
|
-
rats_error_t result = rats_set_encryption_enabled(client_, enabled ? 1 : 0);
|
|
601
|
-
return Napi::Boolean::New(env, result == RATS_SUCCESS);
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
Napi::Value IsEncryptionEnabled(const Napi::CallbackInfo& info) {
|
|
605
|
-
Napi::Env env = info.Env();
|
|
606
|
-
int enabled = rats_is_encryption_enabled(client_);
|
|
607
|
-
return Napi::Boolean::New(env, enabled != 0);
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
// ===================== ENHANCED ENCRYPTION METHODS =====================
|
|
611
|
-
|
|
612
|
-
Napi::Value InitializeEncryption(const Napi::CallbackInfo& info) {
|
|
613
|
-
Napi::Env env = info.Env();
|
|
614
|
-
|
|
615
|
-
if (info.Length() < 1 || !info[0].IsBoolean()) {
|
|
616
|
-
Napi::TypeError::New(env, "Expected enable (boolean)").ThrowAsJavaScriptException();
|
|
617
|
-
return env.Null();
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
bool enable = info[0].As<Napi::Boolean>().Value();
|
|
621
|
-
rats_error_t result = rats_initialize_encryption(client_, enable ? 1 : 0);
|
|
622
|
-
return Napi::Boolean::New(env, result == RATS_SUCCESS);
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
Napi::Value IsPeerEncrypted(const Napi::CallbackInfo& info) {
|
|
626
|
-
Napi::Env env = info.Env();
|
|
627
|
-
|
|
628
|
-
if (info.Length() < 1 || !info[0].IsString()) {
|
|
629
|
-
Napi::TypeError::New(env, "Expected peer_id (string)").ThrowAsJavaScriptException();
|
|
630
|
-
return env.Null();
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
std::string peer_id = info[0].As<Napi::String>().Utf8Value();
|
|
634
|
-
int encrypted = rats_is_peer_encrypted(client_, peer_id.c_str());
|
|
635
|
-
return Napi::Boolean::New(env, encrypted != 0);
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
Napi::Value SetNoiseStaticKeypair(const Napi::CallbackInfo& info) {
|
|
639
|
-
Napi::Env env = info.Env();
|
|
640
|
-
|
|
641
|
-
if (info.Length() < 1 || !info[0].IsString()) {
|
|
642
|
-
Napi::TypeError::New(env, "Expected private_key_hex (string)").ThrowAsJavaScriptException();
|
|
643
|
-
return env.Null();
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
std::string private_key_hex = info[0].As<Napi::String>().Utf8Value();
|
|
647
|
-
rats_error_t result = rats_set_noise_static_keypair(client_, private_key_hex.c_str());
|
|
648
|
-
return Napi::Boolean::New(env, result == RATS_SUCCESS);
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
Napi::Value GetNoiseStaticPublicKey(const Napi::CallbackInfo& info) {
|
|
652
|
-
Napi::Env env = info.Env();
|
|
653
|
-
char* key = rats_get_noise_static_public_key(client_);
|
|
654
|
-
if (!key) return env.Null();
|
|
655
|
-
|
|
656
|
-
Napi::String result = Napi::String::New(env, key);
|
|
657
|
-
rats_string_free(key);
|
|
658
|
-
return result;
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
Napi::Value GetPeerNoisePublicKey(const Napi::CallbackInfo& info) {
|
|
662
|
-
Napi::Env env = info.Env();
|
|
663
|
-
|
|
664
|
-
if (info.Length() < 1 || !info[0].IsString()) {
|
|
665
|
-
Napi::TypeError::New(env, "Expected peer_id (string)").ThrowAsJavaScriptException();
|
|
666
|
-
return env.Null();
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
std::string peer_id = info[0].As<Napi::String>().Utf8Value();
|
|
670
|
-
char* key = rats_get_peer_noise_public_key(client_, peer_id.c_str());
|
|
671
|
-
if (!key) return env.Null();
|
|
672
|
-
|
|
673
|
-
Napi::String result = Napi::String::New(env, key);
|
|
674
|
-
rats_string_free(key);
|
|
675
|
-
return result;
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
Napi::Value GetPeerHandshakeHash(const Napi::CallbackInfo& info) {
|
|
679
|
-
Napi::Env env = info.Env();
|
|
680
|
-
|
|
681
|
-
if (info.Length() < 1 || !info[0].IsString()) {
|
|
682
|
-
Napi::TypeError::New(env, "Expected peer_id (string)").ThrowAsJavaScriptException();
|
|
683
|
-
return env.Null();
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
std::string peer_id = info[0].As<Napi::String>().Utf8Value();
|
|
687
|
-
char* hash = rats_get_peer_handshake_hash(client_, peer_id.c_str());
|
|
688
|
-
if (!hash) return env.Null();
|
|
689
|
-
|
|
690
|
-
Napi::String result = Napi::String::New(env, hash);
|
|
691
|
-
rats_string_free(hash);
|
|
692
|
-
return result;
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
// ===================== ICE (NAT TRAVERSAL) METHODS =====================
|
|
696
|
-
|
|
697
|
-
Napi::Value IsIceAvailable(const Napi::CallbackInfo& info) {
|
|
698
|
-
Napi::Env env = info.Env();
|
|
699
|
-
int available = rats_is_ice_available(client_);
|
|
700
|
-
return Napi::Boolean::New(env, available != 0);
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
void AddStunServer(const Napi::CallbackInfo& info) {
|
|
704
|
-
if (info.Length() < 1 || !info[0].IsString()) {
|
|
705
|
-
Napi::TypeError::New(info.Env(), "Expected host (string)").ThrowAsJavaScriptException();
|
|
706
|
-
return;
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
std::string host = info[0].As<Napi::String>().Utf8Value();
|
|
710
|
-
uint16_t port = 3478;
|
|
711
|
-
if (info.Length() > 1 && info[1].IsNumber()) {
|
|
712
|
-
port = static_cast<uint16_t>(info[1].As<Napi::Number>().Uint32Value());
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
rats_add_stun_server(client_, host.c_str(), port);
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
void AddTurnServer(const Napi::CallbackInfo& info) {
|
|
719
|
-
if (info.Length() < 4 || !info[0].IsString() || !info[1].IsNumber() ||
|
|
720
|
-
!info[2].IsString() || !info[3].IsString()) {
|
|
721
|
-
Napi::TypeError::New(info.Env(), "Expected host, port, username, password").ThrowAsJavaScriptException();
|
|
722
|
-
return;
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
std::string host = info[0].As<Napi::String>().Utf8Value();
|
|
726
|
-
uint16_t port = static_cast<uint16_t>(info[1].As<Napi::Number>().Uint32Value());
|
|
727
|
-
std::string username = info[2].As<Napi::String>().Utf8Value();
|
|
728
|
-
std::string password = info[3].As<Napi::String>().Utf8Value();
|
|
729
|
-
|
|
730
|
-
rats_add_turn_server(client_, host.c_str(), port, username.c_str(), password.c_str());
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
void ClearIceServers(const Napi::CallbackInfo& info) {
|
|
734
|
-
rats_clear_ice_servers(client_);
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
Napi::Value GatherIceCandidates(const Napi::CallbackInfo& info) {
|
|
738
|
-
Napi::Env env = info.Env();
|
|
739
|
-
int result = rats_gather_ice_candidates(client_);
|
|
740
|
-
return Napi::Boolean::New(env, result != 0);
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
Napi::Value GetIceCandidates(const Napi::CallbackInfo& info) {
|
|
744
|
-
Napi::Env env = info.Env();
|
|
745
|
-
char* candidates_json = rats_get_ice_candidates_json(client_);
|
|
746
|
-
if (!candidates_json) return env.Null();
|
|
747
|
-
|
|
748
|
-
Napi::String result = Napi::String::New(env, candidates_json);
|
|
749
|
-
rats_string_free(candidates_json);
|
|
750
|
-
return result;
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
Napi::Value IsIceGatheringComplete(const Napi::CallbackInfo& info) {
|
|
754
|
-
Napi::Env env = info.Env();
|
|
755
|
-
int complete = rats_is_ice_gathering_complete(client_);
|
|
756
|
-
return Napi::Boolean::New(env, complete != 0);
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
Napi::Value GetPublicAddress(const Napi::CallbackInfo& info) {
|
|
760
|
-
Napi::Env env = info.Env();
|
|
761
|
-
char* address = rats_get_public_address(client_);
|
|
762
|
-
if (!address) return env.Null();
|
|
763
|
-
|
|
764
|
-
Napi::String result = Napi::String::New(env, address);
|
|
765
|
-
rats_string_free(address);
|
|
766
|
-
return result;
|
|
80
|
+
rats_t node_ = nullptr;
|
|
81
|
+
|
|
82
|
+
// Single-slot callbacks (peer connect/disconnect, file offer/progress/complete).
|
|
83
|
+
std::unique_ptr<CbContext> on_connected_;
|
|
84
|
+
std::unique_ptr<CbContext> on_disconnected_;
|
|
85
|
+
std::unique_ptr<CbContext> on_file_offer_;
|
|
86
|
+
std::unique_ptr<CbContext> on_file_progress_;
|
|
87
|
+
std::unique_ptr<CbContext> on_file_complete_;
|
|
88
|
+
|
|
89
|
+
// Multi-slot callbacks keyed by channel / topic / json type. We keep them
|
|
90
|
+
// alive for the lifetime of the client; the trampolines look up nothing —
|
|
91
|
+
// each registration has its own CbContext handed to librats as `user`.
|
|
92
|
+
std::vector<std::unique_ptr<CbContext>> handlers_;
|
|
93
|
+
|
|
94
|
+
CbContext* new_handler() {
|
|
95
|
+
handlers_.push_back(std::make_unique<CbContext>());
|
|
96
|
+
return handlers_.back().get();
|
|
767
97
|
}
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
98
|
+
|
|
99
|
+
// Release TSFNs + the native node. Idempotent; used by ~RatsNode and destroy().
|
|
100
|
+
void Teardown();
|
|
101
|
+
|
|
102
|
+
// ---- lifecycle / core ----
|
|
103
|
+
void Start(const Napi::CallbackInfo& info);
|
|
104
|
+
void Stop(const Napi::CallbackInfo& info);
|
|
105
|
+
void Destroy(const Napi::CallbackInfo& info);
|
|
106
|
+
Napi::Value GetListenPort(const Napi::CallbackInfo& info);
|
|
107
|
+
Napi::Value GetLocalId(const Napi::CallbackInfo& info);
|
|
108
|
+
Napi::Value GetProtocol(const Napi::CallbackInfo& info);
|
|
109
|
+
Napi::Value GetTransports(const Napi::CallbackInfo& info);
|
|
110
|
+
|
|
111
|
+
// ---- connections ----
|
|
112
|
+
Napi::Value Connect(const Napi::CallbackInfo& info);
|
|
113
|
+
Napi::Value GetPeerCount(const Napi::CallbackInfo& info);
|
|
114
|
+
Napi::Value GetPeerIds(const Napi::CallbackInfo& info);
|
|
115
|
+
void SetMaxPeers(const Napi::CallbackInfo& info);
|
|
116
|
+
Napi::Value GetMaxPeers(const Napi::CallbackInfo& info);
|
|
117
|
+
Napi::Value GetPeerTransport(const Napi::CallbackInfo& info);
|
|
118
|
+
Napi::Value GetPeerTransports(const Napi::CallbackInfo& info);
|
|
119
|
+
|
|
120
|
+
// ---- raw channel messaging ----
|
|
121
|
+
Napi::Value Send(const Napi::CallbackInfo& info);
|
|
122
|
+
Napi::Value Broadcast(const Napi::CallbackInfo& info);
|
|
123
|
+
void On(const Napi::CallbackInfo& info);
|
|
124
|
+
|
|
125
|
+
// ---- peer events ----
|
|
126
|
+
void OnPeerConnected(const Napi::CallbackInfo& info);
|
|
127
|
+
void OnPeerDisconnected(const Napi::CallbackInfo& info);
|
|
128
|
+
|
|
129
|
+
// ---- discovery / NAT ----
|
|
130
|
+
void EnableDht(const Napi::CallbackInfo& info);
|
|
131
|
+
void EnableMdns(const Napi::CallbackInfo& info);
|
|
132
|
+
void EnablePortMapping(const Napi::CallbackInfo& info);
|
|
133
|
+
void EnableHolePunch(const Napi::CallbackInfo& info);
|
|
134
|
+
Napi::Value PunchPeer(const Napi::CallbackInfo& info);
|
|
135
|
+
void EnableRelay(const Napi::CallbackInfo& info);
|
|
136
|
+
Napi::Value ConnectViaRelay(const Napi::CallbackInfo& info);
|
|
137
|
+
Napi::Value GetNatMapping(const Napi::CallbackInfo& info);
|
|
138
|
+
|
|
139
|
+
// ---- pub/sub ----
|
|
140
|
+
void EnablePubsub(const Napi::CallbackInfo& info);
|
|
141
|
+
void Subscribe(const Napi::CallbackInfo& info);
|
|
142
|
+
void Unsubscribe(const Napi::CallbackInfo& info);
|
|
143
|
+
Napi::Value Publish(const Napi::CallbackInfo& info);
|
|
144
|
+
|
|
145
|
+
// ---- typed JSON ----
|
|
146
|
+
void EnableJson(const Napi::CallbackInfo& info);
|
|
147
|
+
void OnJson(const Napi::CallbackInfo& info);
|
|
148
|
+
void OnceJson(const Napi::CallbackInfo& info);
|
|
149
|
+
void OffJson(const Napi::CallbackInfo& info);
|
|
150
|
+
Napi::Value SendJson(const Napi::CallbackInfo& info);
|
|
151
|
+
Napi::Value BroadcastJson(const Napi::CallbackInfo& info);
|
|
152
|
+
void OnJsonImpl(const Napi::CallbackInfo& info, bool once);
|
|
153
|
+
|
|
154
|
+
// ---- file transfer ----
|
|
155
|
+
void EnableFileTransfer(const Napi::CallbackInfo& info);
|
|
156
|
+
void OnFileOffer(const Napi::CallbackInfo& info);
|
|
157
|
+
void OnFileProgress(const Napi::CallbackInfo& info);
|
|
158
|
+
void OnFileComplete(const Napi::CallbackInfo& info);
|
|
159
|
+
Napi::Value SendFile(const Napi::CallbackInfo& info);
|
|
160
|
+
Napi::Value SendDirectory(const Napi::CallbackInfo& info);
|
|
161
|
+
Napi::Value AcceptFile(const Napi::CallbackInfo& info);
|
|
162
|
+
Napi::Value RejectFile(const Napi::CallbackInfo& info);
|
|
163
|
+
Napi::Value CancelFile(const Napi::CallbackInfo& info);
|
|
164
|
+
Napi::Value PauseFile(const Napi::CallbackInfo& info);
|
|
165
|
+
Napi::Value ResumeFile(const Napi::CallbackInfo& info);
|
|
166
|
+
|
|
167
|
+
// ---- ping / reconnect ----
|
|
168
|
+
void EnablePing(const Napi::CallbackInfo& info);
|
|
169
|
+
Napi::Value GetPeerRttMs(const Napi::CallbackInfo& info);
|
|
170
|
+
void EnableReconnect(const Napi::CallbackInfo& info);
|
|
171
|
+
Napi::Value AddReconnect(const Napi::CallbackInfo& info);
|
|
172
|
+
Napi::Value RemoveReconnect(const Napi::CallbackInfo& info);
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
// Construction / lifecycle
|
|
177
|
+
// ---------------------------------------------------------------------------
|
|
178
|
+
|
|
179
|
+
RatsNode::RatsNode(const Napi::CallbackInfo& info)
|
|
180
|
+
: Napi::ObjectWrap<RatsNode>(info) {
|
|
181
|
+
Napi::Env env = info.Env();
|
|
182
|
+
|
|
183
|
+
// Two construction forms:
|
|
184
|
+
// new RatsNode(port) -> rats_create(port)
|
|
185
|
+
// new RatsNode({ ...config }) -> rats_create_config(&cfg)
|
|
186
|
+
if (info.Length() >= 1 && info[0].IsObject() && !info[0].IsBuffer()) {
|
|
187
|
+
Napi::Object cfg = info[0].As<Napi::Object>();
|
|
188
|
+
rats_config_t c = rats_config_default();
|
|
189
|
+
|
|
190
|
+
// Hold string storage alive until rats_create_config() returns (the
|
|
191
|
+
// struct borrows the pointers only for the duration of the call).
|
|
192
|
+
std::string bind_addr, data_dir, protocol;
|
|
193
|
+
|
|
194
|
+
if (cfg.Has("listenPort"))
|
|
195
|
+
c.listen_port = static_cast<uint16_t>(cfg.Get("listenPort").As<Napi::Number>().Uint32Value());
|
|
196
|
+
if (cfg.Has("enableListen"))
|
|
197
|
+
c.enable_listen = cfg.Get("enableListen").As<Napi::Boolean>().Value() ? 1 : 0;
|
|
198
|
+
if (cfg.Has("bindAddress") && cfg.Get("bindAddress").IsString()) {
|
|
199
|
+
bind_addr = cfg.Get("bindAddress").As<Napi::String>().Utf8Value();
|
|
200
|
+
c.bind_address = bind_addr.c_str();
|
|
780
201
|
}
|
|
781
|
-
if (
|
|
782
|
-
|
|
202
|
+
if (cfg.Has("security"))
|
|
203
|
+
c.security = static_cast<rats_security_t>(cfg.Get("security").As<Napi::Number>().Int32Value());
|
|
204
|
+
if (cfg.Has("dataDir") && cfg.Get("dataDir").IsString()) {
|
|
205
|
+
data_dir = cfg.Get("dataDir").As<Napi::String>().Utf8Value();
|
|
206
|
+
c.data_dir = data_dir.c_str();
|
|
783
207
|
}
|
|
784
|
-
if (
|
|
785
|
-
|
|
208
|
+
if (cfg.Has("protocol") && cfg.Get("protocol").IsString()) {
|
|
209
|
+
protocol = cfg.Get("protocol").As<Napi::String>().Utf8Value();
|
|
210
|
+
c.protocol = protocol.c_str();
|
|
786
211
|
}
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
if (
|
|
798
|
-
|
|
212
|
+
if (cfg.Has("maxPeers"))
|
|
213
|
+
c.max_peers = static_cast<size_t>(cfg.Get("maxPeers").As<Napi::Number>().Int64Value());
|
|
214
|
+
|
|
215
|
+
// Transports. Both are on by default and share one port; preferredTransport
|
|
216
|
+
// decides which a dial tries first (UDP), transportFallbackMs how long
|
|
217
|
+
// before the other is raced alongside it.
|
|
218
|
+
if (cfg.Has("enableTcp"))
|
|
219
|
+
c.enable_tcp = cfg.Get("enableTcp").As<Napi::Boolean>().Value() ? 1 : 0;
|
|
220
|
+
if (cfg.Has("enableUdp"))
|
|
221
|
+
c.enable_udp = cfg.Get("enableUdp").As<Napi::Boolean>().Value() ? 1 : 0;
|
|
222
|
+
if (cfg.Has("preferredTransport"))
|
|
223
|
+
c.preferred_transport = static_cast<rats_transport_t>(
|
|
224
|
+
cfg.Get("preferredTransport").As<Napi::Number>().Int32Value());
|
|
225
|
+
if (cfg.Has("transportFallbackMs"))
|
|
226
|
+
c.transport_fallback_ms =
|
|
227
|
+
cfg.Get("transportFallbackMs").As<Napi::Number>().Uint32Value();
|
|
228
|
+
|
|
229
|
+
node_ = rats_create_config(&c);
|
|
230
|
+
} else {
|
|
231
|
+
int port = 0;
|
|
232
|
+
if (info.Length() >= 1 && info[0].IsNumber()) {
|
|
233
|
+
port = info[0].As<Napi::Number>().Int32Value();
|
|
234
|
+
if (port < 0 || port > 65535) {
|
|
235
|
+
Napi::RangeError::New(env, "Port number must be between 0 and 65535")
|
|
236
|
+
.ThrowAsJavaScriptException();
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
} else if (info.Length() >= 1) {
|
|
240
|
+
Napi::TypeError::New(env, "Expected a port number or a config object")
|
|
241
|
+
.ThrowAsJavaScriptException();
|
|
799
242
|
return;
|
|
800
243
|
}
|
|
801
|
-
|
|
802
|
-
std::string candidate_sdp = info[0].As<Napi::String>().Utf8Value();
|
|
803
|
-
rats_add_remote_ice_candidate(client_, candidate_sdp.c_str());
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
void EndOfRemoteIceCandidates(const Napi::CallbackInfo& info) {
|
|
807
|
-
rats_end_of_remote_ice_candidates(client_);
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
void StartIceChecks(const Napi::CallbackInfo& info) {
|
|
811
|
-
rats_start_ice_checks(client_);
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
Napi::Value GetIceConnectionState(const Napi::CallbackInfo& info) {
|
|
815
|
-
Napi::Env env = info.Env();
|
|
816
|
-
rats_ice_connection_state_t state = rats_get_ice_connection_state(client_);
|
|
817
|
-
return Napi::Number::New(env, static_cast<int>(state));
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
Napi::Value GetIceGatheringState(const Napi::CallbackInfo& info) {
|
|
821
|
-
Napi::Env env = info.Env();
|
|
822
|
-
rats_ice_gathering_state_t state = rats_get_ice_gathering_state(client_);
|
|
823
|
-
return Napi::Number::New(env, static_cast<int>(state));
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
Napi::Value IsIceConnected(const Napi::CallbackInfo& info) {
|
|
827
|
-
Napi::Env env = info.Env();
|
|
828
|
-
int connected = rats_is_ice_connected(client_);
|
|
829
|
-
return Napi::Boolean::New(env, connected != 0);
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
Napi::Value GetIceSelectedPair(const Napi::CallbackInfo& info) {
|
|
833
|
-
Napi::Env env = info.Env();
|
|
834
|
-
char* pair_json = rats_get_ice_selected_pair_json(client_);
|
|
835
|
-
if (!pair_json) return env.Null();
|
|
836
|
-
|
|
837
|
-
Napi::String result = Napi::String::New(env, pair_json);
|
|
838
|
-
rats_string_free(pair_json);
|
|
839
|
-
return result;
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
void CloseIce(const Napi::CallbackInfo& info) {
|
|
843
|
-
rats_close_ice(client_);
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
void RestartIce(const Napi::CallbackInfo& info) {
|
|
847
|
-
rats_restart_ice(client_);
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
// GossipSub methods
|
|
851
|
-
Napi::Value IsGossipsubAvailable(const Napi::CallbackInfo& info) {
|
|
852
|
-
Napi::Env env = info.Env();
|
|
853
|
-
int available = rats_is_gossipsub_available(client_);
|
|
854
|
-
return Napi::Boolean::New(env, available != 0);
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
Napi::Value IsGossipsubRunning(const Napi::CallbackInfo& info) {
|
|
858
|
-
Napi::Env env = info.Env();
|
|
859
|
-
int running = rats_is_gossipsub_running(client_);
|
|
860
|
-
return Napi::Boolean::New(env, running != 0);
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
Napi::Value SubscribeToTopic(const Napi::CallbackInfo& info) {
|
|
864
|
-
Napi::Env env = info.Env();
|
|
865
|
-
|
|
866
|
-
if (info.Length() < 1 || !info[0].IsString()) {
|
|
867
|
-
Napi::TypeError::New(env, "Expected topic (string)").ThrowAsJavaScriptException();
|
|
868
|
-
return env.Null();
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
std::string topic = info[0].As<Napi::String>().Utf8Value();
|
|
872
|
-
rats_error_t result = rats_subscribe_to_topic(client_, topic.c_str());
|
|
873
|
-
return Napi::Boolean::New(env, result == RATS_SUCCESS);
|
|
244
|
+
node_ = rats_create(static_cast<uint16_t>(port));
|
|
874
245
|
}
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
Napi::
|
|
878
|
-
|
|
879
|
-
if (info.Length() < 1 || !info[0].IsString()) {
|
|
880
|
-
Napi::TypeError::New(env, "Expected topic (string)").ThrowAsJavaScriptException();
|
|
881
|
-
return env.Null();
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
std::string topic = info[0].As<Napi::String>().Utf8Value();
|
|
885
|
-
rats_error_t result = rats_unsubscribe_from_topic(client_, topic.c_str());
|
|
886
|
-
return Napi::Boolean::New(env, result == RATS_SUCCESS);
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
Napi::Value IsSubscribedToTopic(const Napi::CallbackInfo& info) {
|
|
890
|
-
Napi::Env env = info.Env();
|
|
891
|
-
|
|
892
|
-
if (info.Length() < 1 || !info[0].IsString()) {
|
|
893
|
-
Napi::TypeError::New(env, "Expected topic (string)").ThrowAsJavaScriptException();
|
|
894
|
-
return env.Null();
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
std::string topic = info[0].As<Napi::String>().Utf8Value();
|
|
898
|
-
int subscribed = rats_is_subscribed_to_topic(client_, topic.c_str());
|
|
899
|
-
return Napi::Boolean::New(env, subscribed != 0);
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
Napi::Value GetSubscribedTopics(const Napi::CallbackInfo& info) {
|
|
903
|
-
Napi::Env env = info.Env();
|
|
904
|
-
int count = 0;
|
|
905
|
-
char** topics = rats_get_subscribed_topics(client_, &count);
|
|
906
|
-
|
|
907
|
-
if (!topics || count == 0) {
|
|
908
|
-
return Napi::Array::New(env, 0);
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
Napi::Array result = Napi::Array::New(env, count);
|
|
912
|
-
for (int i = 0; i < count; i++) {
|
|
913
|
-
result[i] = Napi::String::New(env, topics[i]);
|
|
914
|
-
rats_string_free(topics[i]);
|
|
915
|
-
}
|
|
916
|
-
free(topics);
|
|
917
|
-
|
|
918
|
-
return result;
|
|
246
|
+
|
|
247
|
+
if (!node_) {
|
|
248
|
+
Napi::Error::New(env, "Failed to create RatsNode").ThrowAsJavaScriptException();
|
|
249
|
+
return;
|
|
919
250
|
}
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
RatsNode::~RatsNode() {
|
|
254
|
+
Teardown();
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Release all TSFNs first so no JS callback can be invoked during/after
|
|
258
|
+
// destruction, then destroy the native node. Idempotent.
|
|
259
|
+
void RatsNode::Teardown() {
|
|
260
|
+
if (on_connected_) on_connected_->release();
|
|
261
|
+
if (on_disconnected_) on_disconnected_->release();
|
|
262
|
+
if (on_file_offer_) on_file_offer_->release();
|
|
263
|
+
if (on_file_progress_) on_file_progress_->release();
|
|
264
|
+
if (on_file_complete_) on_file_complete_->release();
|
|
265
|
+
for (auto& h : handlers_) h->release();
|
|
266
|
+
|
|
267
|
+
if (node_) {
|
|
268
|
+
rats_destroy(node_);
|
|
269
|
+
node_ = nullptr;
|
|
934
270
|
}
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
void RatsNode::Start(const Napi::CallbackInfo& info) {
|
|
274
|
+
RATS_REQUIRE_NODE();
|
|
275
|
+
throw_on_error(info.Env(), rats_start(node_));
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
void RatsNode::Stop(const Napi::CallbackInfo& info) {
|
|
279
|
+
if (node_) rats_stop(node_);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Explicit release. After this the instance is inert; further calls are no-ops
|
|
283
|
+
// or throw, and the GC has nothing left to free.
|
|
284
|
+
void RatsNode::Destroy(const Napi::CallbackInfo& info) {
|
|
285
|
+
Teardown();
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
Napi::Value RatsNode::GetListenPort(const Napi::CallbackInfo& info) {
|
|
289
|
+
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
290
|
+
return Napi::Number::New(info.Env(), rats_listen_port(node_));
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
Napi::Value RatsNode::GetLocalId(const Napi::CallbackInfo& info) {
|
|
294
|
+
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
295
|
+
Napi::Env env = info.Env();
|
|
296
|
+
char* id = rats_local_id(node_);
|
|
297
|
+
if (!id) return env.Null();
|
|
298
|
+
Napi::String result = Napi::String::New(env, id);
|
|
299
|
+
rats_string_free(id);
|
|
300
|
+
return result;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
Napi::Value RatsNode::GetProtocol(const Napi::CallbackInfo& info) {
|
|
304
|
+
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
305
|
+
Napi::Env env = info.Env();
|
|
306
|
+
char* s = rats_protocol(node_);
|
|
307
|
+
if (!s) return env.Null();
|
|
308
|
+
Napi::String result = Napi::String::New(env, s);
|
|
309
|
+
rats_string_free(s);
|
|
310
|
+
return result;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Transports actually running, as a RATS_TRANSPORT_MASK_* bitmask. May be
|
|
314
|
+
// narrower than the config asked for; 0 before start() and after stop().
|
|
315
|
+
Napi::Value RatsNode::GetTransports(const Napi::CallbackInfo& info) {
|
|
316
|
+
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
317
|
+
return Napi::Number::New(info.Env(), rats_transports(node_));
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// ---------------------------------------------------------------------------
|
|
321
|
+
// Connections
|
|
322
|
+
// ---------------------------------------------------------------------------
|
|
323
|
+
|
|
324
|
+
Napi::Value RatsNode::Connect(const Napi::CallbackInfo& info) {
|
|
325
|
+
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
326
|
+
Napi::Env env = info.Env();
|
|
327
|
+
if (info.Length() < 2 || !info[0].IsString() || !info[1].IsNumber()) {
|
|
328
|
+
Napi::TypeError::New(env, "Expected host (string) and port (number)")
|
|
329
|
+
.ThrowAsJavaScriptException();
|
|
330
|
+
return env.Undefined();
|
|
949
331
|
}
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
for (
|
|
969
|
-
result[i] = Napi::String::New(env,
|
|
970
|
-
rats_string_free(peers[i]);
|
|
332
|
+
std::string host = info[0].As<Napi::String>().Utf8Value();
|
|
333
|
+
uint16_t port = static_cast<uint16_t>(info[1].As<Napi::Number>().Uint32Value());
|
|
334
|
+
throw_on_error(env, rats_connect(node_, host.c_str(), port));
|
|
335
|
+
return env.Undefined();
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
Napi::Value RatsNode::GetPeerCount(const Napi::CallbackInfo& info) {
|
|
339
|
+
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
340
|
+
return Napi::Number::New(info.Env(), static_cast<double>(rats_peer_count(node_)));
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
Napi::Value RatsNode::GetPeerIds(const Napi::CallbackInfo& info) {
|
|
344
|
+
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
345
|
+
Napi::Env env = info.Env();
|
|
346
|
+
size_t count = 0;
|
|
347
|
+
char** ids = rats_peer_ids(node_, &count);
|
|
348
|
+
Napi::Array result = Napi::Array::New(env, count);
|
|
349
|
+
if (ids) {
|
|
350
|
+
for (size_t i = 0; i < count; i++) {
|
|
351
|
+
result[static_cast<uint32_t>(i)] = Napi::String::New(env, ids[i]);
|
|
971
352
|
}
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
return result;
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
Napi::Value GetGossipsubStatistics(const Napi::CallbackInfo& info) {
|
|
978
|
-
Napi::Env env = info.Env();
|
|
979
|
-
char* stats_json = rats_get_gossipsub_statistics_json(client_);
|
|
980
|
-
if (!stats_json) return env.Null();
|
|
981
|
-
|
|
982
|
-
Napi::String result = Napi::String::New(env, stats_json);
|
|
983
|
-
rats_string_free(stats_json);
|
|
984
|
-
return result;
|
|
353
|
+
rats_free_peer_ids(ids, count);
|
|
985
354
|
}
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
Napi::Value SendFile(const Napi::CallbackInfo& info) {
|
|
989
|
-
Napi::Env env = info.Env();
|
|
990
|
-
|
|
991
|
-
if (info.Length() < 2 || !info[0].IsString() || !info[1].IsString()) {
|
|
992
|
-
Napi::TypeError::New(env, "Expected peer_id (string) and file_path (string)").ThrowAsJavaScriptException();
|
|
993
|
-
return env.Null();
|
|
994
|
-
}
|
|
995
|
-
|
|
996
|
-
std::string peer_id = info[0].As<Napi::String>().Utf8Value();
|
|
997
|
-
std::string file_path = info[1].As<Napi::String>().Utf8Value();
|
|
998
|
-
|
|
999
|
-
std::string remote_name;
|
|
1000
|
-
const char* remote_filename = nullptr;
|
|
1001
|
-
if (info.Length() > 2 && info[2].IsString()) {
|
|
1002
|
-
remote_name = info[2].As<Napi::String>().Utf8Value();
|
|
1003
|
-
remote_filename = remote_name.c_str();
|
|
1004
|
-
}
|
|
355
|
+
return result;
|
|
356
|
+
}
|
|
1005
357
|
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
return
|
|
358
|
+
void RatsNode::SetMaxPeers(const Napi::CallbackInfo& info) {
|
|
359
|
+
RATS_REQUIRE_NODE();
|
|
360
|
+
Napi::Env env = info.Env();
|
|
361
|
+
if (info.Length() < 1 || !info[0].IsNumber()) {
|
|
362
|
+
Napi::TypeError::New(env, "Expected maxPeers (number)").ThrowAsJavaScriptException();
|
|
363
|
+
return;
|
|
1012
364
|
}
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
std::string peer_id = info[0].As<Napi::String>().Utf8Value();
|
|
1023
|
-
std::string directory_path = info[1].As<Napi::String>().Utf8Value();
|
|
1024
|
-
|
|
1025
|
-
std::string remote_name;
|
|
1026
|
-
const char* remote_directory_name = nullptr;
|
|
1027
|
-
if (info.Length() > 2 && info[2].IsString()) {
|
|
1028
|
-
remote_name = info[2].As<Napi::String>().Utf8Value();
|
|
1029
|
-
remote_directory_name = remote_name.c_str();
|
|
1030
|
-
}
|
|
365
|
+
rats_set_max_peers(node_, static_cast<size_t>(info[0].As<Napi::Number>().Int64Value()));
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
Napi::Value RatsNode::GetMaxPeers(const Napi::CallbackInfo& info) {
|
|
369
|
+
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
370
|
+
return Napi::Number::New(info.Env(), static_cast<double>(rats_max_peers(node_)));
|
|
371
|
+
}
|
|
1031
372
|
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
return
|
|
373
|
+
// Which transport a connected peer's link runs on; null if not connected.
|
|
374
|
+
Napi::Value RatsNode::GetPeerTransport(const Napi::CallbackInfo& info) {
|
|
375
|
+
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
376
|
+
Napi::Env env = info.Env();
|
|
377
|
+
if (info.Length() < 1 || !info[0].IsString()) {
|
|
378
|
+
Napi::TypeError::New(env, "Expected peerId (string)").ThrowAsJavaScriptException();
|
|
379
|
+
return env.Undefined();
|
|
1039
380
|
}
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
return
|
|
381
|
+
std::string peer = info[0].As<Napi::String>().Utf8Value();
|
|
382
|
+
int t = rats_peer_transport(node_, peer.c_str());
|
|
383
|
+
if (t < 0) return env.Null();
|
|
384
|
+
return Napi::Number::New(env, t);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Transports a connected peer advertised, as a bitmask; null if not connected.
|
|
388
|
+
// 0 means the peer did not say (an older build) — "no information", not "none".
|
|
389
|
+
Napi::Value RatsNode::GetPeerTransports(const Napi::CallbackInfo& info) {
|
|
390
|
+
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
391
|
+
Napi::Env env = info.Env();
|
|
392
|
+
if (info.Length() < 1 || !info[0].IsString()) {
|
|
393
|
+
Napi::TypeError::New(env, "Expected peerId (string)").ThrowAsJavaScriptException();
|
|
394
|
+
return env.Undefined();
|
|
1054
395
|
}
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
396
|
+
std::string peer = info[0].As<Napi::String>().Utf8Value();
|
|
397
|
+
int t = rats_peer_transports(node_, peer.c_str());
|
|
398
|
+
if (t < 0) return env.Null();
|
|
399
|
+
return Napi::Number::New(env, t);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// ---------------------------------------------------------------------------
|
|
403
|
+
// Raw channel messaging
|
|
404
|
+
// ---------------------------------------------------------------------------
|
|
405
|
+
|
|
406
|
+
// Coerce a JS string or Buffer argument into a contiguous byte vector.
|
|
407
|
+
static bool to_bytes(Napi::Env env, const Napi::Value& v, std::vector<uint8_t>& out) {
|
|
408
|
+
if (v.IsBuffer()) {
|
|
409
|
+
Napi::Buffer<uint8_t> buf = v.As<Napi::Buffer<uint8_t>>();
|
|
410
|
+
out.assign(buf.Data(), buf.Data() + buf.Length());
|
|
411
|
+
return true;
|
|
412
|
+
}
|
|
413
|
+
if (v.IsString()) {
|
|
414
|
+
std::string s = v.As<Napi::String>().Utf8Value();
|
|
415
|
+
out.assign(s.begin(), s.end());
|
|
416
|
+
return true;
|
|
417
|
+
}
|
|
418
|
+
Napi::TypeError::New(env, "Expected data (string or Buffer)").ThrowAsJavaScriptException();
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
1072
421
|
|
|
1073
|
-
|
|
1074
|
-
|
|
422
|
+
Napi::Value RatsNode::Send(const Napi::CallbackInfo& info) {
|
|
423
|
+
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
424
|
+
Napi::Env env = info.Env();
|
|
425
|
+
if (info.Length() < 3 || !info[0].IsString() || !info[1].IsString()) {
|
|
426
|
+
Napi::TypeError::New(env, "Expected peerId (string), channel (string), data")
|
|
427
|
+
.ThrowAsJavaScriptException();
|
|
428
|
+
return env.Undefined();
|
|
1075
429
|
}
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
430
|
+
std::string peer = info[0].As<Napi::String>().Utf8Value();
|
|
431
|
+
std::string channel = info[1].As<Napi::String>().Utf8Value();
|
|
432
|
+
std::vector<uint8_t> data;
|
|
433
|
+
if (!to_bytes(env, info[2], data)) return env.Undefined();
|
|
434
|
+
throw_on_error(env, rats_send(node_, peer.c_str(), channel.c_str(), data.data(), data.size()));
|
|
435
|
+
return env.Undefined();
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
Napi::Value RatsNode::Broadcast(const Napi::CallbackInfo& info) {
|
|
439
|
+
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
440
|
+
Napi::Env env = info.Env();
|
|
441
|
+
if (info.Length() < 2 || !info[0].IsString()) {
|
|
442
|
+
Napi::TypeError::New(env, "Expected channel (string) and data")
|
|
443
|
+
.ThrowAsJavaScriptException();
|
|
444
|
+
return env.Undefined();
|
|
1089
445
|
}
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
446
|
+
std::string channel = info[0].As<Napi::String>().Utf8Value();
|
|
447
|
+
std::vector<uint8_t> data;
|
|
448
|
+
if (!to_bytes(env, info[1], data)) return env.Undefined();
|
|
449
|
+
throw_on_error(env, rats_broadcast(node_, channel.c_str(), data.data(), data.size()));
|
|
450
|
+
return env.Undefined();
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// rats_message_cb(user, peer_id_hex, data, len) -> JS (peerId, Buffer)
|
|
454
|
+
void RatsNode::On(const Napi::CallbackInfo& info) {
|
|
455
|
+
RATS_REQUIRE_NODE();
|
|
456
|
+
Napi::Env env = info.Env();
|
|
457
|
+
if (info.Length() < 2 || !info[0].IsString() || !info[1].IsFunction()) {
|
|
458
|
+
Napi::TypeError::New(env, "Expected channel (string) and callback (function)")
|
|
459
|
+
.ThrowAsJavaScriptException();
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
std::string channel = info[0].As<Napi::String>().Utf8Value();
|
|
463
|
+
CbContext* ctx = new_handler();
|
|
464
|
+
ctx->init(env, info[1].As<Napi::Function>(), "on_message");
|
|
465
|
+
|
|
466
|
+
auto trampoline = [](void* user, const char* peer_id, const void* data, size_t len) {
|
|
467
|
+
auto* c = static_cast<CbContext*>(user);
|
|
468
|
+
std::string peer = peer_id ? peer_id : "";
|
|
469
|
+
std::vector<uint8_t> bytes(static_cast<const uint8_t*>(data),
|
|
470
|
+
static_cast<const uint8_t*>(data) + len);
|
|
471
|
+
c->tsfn.BlockingCall([peer, bytes](Napi::Env env, Napi::Function js) {
|
|
472
|
+
js.Call({Napi::String::New(env, peer),
|
|
473
|
+
Napi::Buffer<uint8_t>::Copy(env, bytes.data(), bytes.size())});
|
|
474
|
+
});
|
|
475
|
+
};
|
|
476
|
+
throw_on_error(env, rats_on(node_, channel.c_str(), trampoline, ctx));
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// ---------------------------------------------------------------------------
|
|
480
|
+
// Peer events
|
|
481
|
+
// ---------------------------------------------------------------------------
|
|
482
|
+
|
|
483
|
+
void RatsNode::OnPeerConnected(const Napi::CallbackInfo& info) {
|
|
484
|
+
RATS_REQUIRE_NODE();
|
|
485
|
+
Napi::Env env = info.Env();
|
|
486
|
+
if (info.Length() < 1 || !info[0].IsFunction()) {
|
|
487
|
+
Napi::TypeError::New(env, "Expected callback function").ThrowAsJavaScriptException();
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
on_connected_ = std::make_unique<CbContext>();
|
|
491
|
+
on_connected_->init(env, info[0].As<Napi::Function>(), "on_peer_connected");
|
|
492
|
+
auto trampoline = [](void* user, const char* peer_id) {
|
|
493
|
+
auto* c = static_cast<CbContext*>(user);
|
|
494
|
+
std::string peer = peer_id ? peer_id : "";
|
|
495
|
+
c->tsfn.BlockingCall([peer](Napi::Env env, Napi::Function js) {
|
|
496
|
+
js.Call({Napi::String::New(env, peer)});
|
|
497
|
+
});
|
|
498
|
+
};
|
|
499
|
+
throw_on_error(env, rats_on_peer_connected(node_, trampoline, on_connected_.get()));
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
void RatsNode::OnPeerDisconnected(const Napi::CallbackInfo& info) {
|
|
503
|
+
RATS_REQUIRE_NODE();
|
|
504
|
+
Napi::Env env = info.Env();
|
|
505
|
+
if (info.Length() < 1 || !info[0].IsFunction()) {
|
|
506
|
+
Napi::TypeError::New(env, "Expected callback function").ThrowAsJavaScriptException();
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
on_disconnected_ = std::make_unique<CbContext>();
|
|
510
|
+
on_disconnected_->init(env, info[0].As<Napi::Function>(), "on_peer_disconnected");
|
|
511
|
+
auto trampoline = [](void* user, const char* peer_id) {
|
|
512
|
+
auto* c = static_cast<CbContext*>(user);
|
|
513
|
+
std::string peer = peer_id ? peer_id : "";
|
|
514
|
+
c->tsfn.BlockingCall([peer](Napi::Env env, Napi::Function js) {
|
|
515
|
+
js.Call({Napi::String::New(env, peer)});
|
|
516
|
+
});
|
|
517
|
+
};
|
|
518
|
+
throw_on_error(env, rats_on_peer_disconnected(node_, trampoline, on_disconnected_.get()));
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// ---------------------------------------------------------------------------
|
|
522
|
+
// Discovery / NAT
|
|
523
|
+
// ---------------------------------------------------------------------------
|
|
524
|
+
|
|
525
|
+
void RatsNode::EnableDht(const Napi::CallbackInfo& info) {
|
|
526
|
+
RATS_REQUIRE_NODE();
|
|
527
|
+
Napi::Env env = info.Env();
|
|
528
|
+
uint16_t dht_port = 0;
|
|
529
|
+
std::string key;
|
|
530
|
+
const char* key_ptr = nullptr;
|
|
531
|
+
if (info.Length() >= 1 && info[0].IsNumber())
|
|
532
|
+
dht_port = static_cast<uint16_t>(info[0].As<Napi::Number>().Uint32Value());
|
|
533
|
+
if (info.Length() >= 2 && info[1].IsString()) {
|
|
534
|
+
key = info[1].As<Napi::String>().Utf8Value();
|
|
535
|
+
key_ptr = key.c_str();
|
|
536
|
+
}
|
|
537
|
+
throw_on_error(env, rats_enable_dht(node_, dht_port, key_ptr));
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
void RatsNode::EnableMdns(const Napi::CallbackInfo& info) {
|
|
541
|
+
RATS_REQUIRE_NODE();
|
|
542
|
+
throw_on_error(info.Env(), rats_enable_mdns(node_));
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
void RatsNode::EnablePortMapping(const Napi::CallbackInfo& info) {
|
|
546
|
+
RATS_REQUIRE_NODE();
|
|
547
|
+
int upnp = 1, natpmp = 1;
|
|
548
|
+
if (info.Length() >= 1 && info[0].IsBoolean()) upnp = info[0].As<Napi::Boolean>().Value() ? 1 : 0;
|
|
549
|
+
if (info.Length() >= 2 && info[1].IsBoolean()) natpmp = info[1].As<Napi::Boolean>().Value() ? 1 : 0;
|
|
550
|
+
throw_on_error(info.Env(), rats_enable_port_mapping(node_, upnp, natpmp));
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// Hole punching. serveAsRelay (default true) also carries other peers'
|
|
554
|
+
// rendezvous — a mesh in which nobody relays cannot punch at all.
|
|
555
|
+
void RatsNode::EnableHolePunch(const Napi::CallbackInfo& info) {
|
|
556
|
+
RATS_REQUIRE_NODE();
|
|
557
|
+
int relay = 1;
|
|
558
|
+
if (info.Length() >= 1 && info[0].IsBoolean()) relay = info[0].As<Napi::Boolean>().Value() ? 1 : 0;
|
|
559
|
+
throw_on_error(info.Env(), rats_enable_hole_punch(node_, relay));
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// Start a punch to a peer. Non-blocking: success arrives as onPeerConnected.
|
|
563
|
+
Napi::Value RatsNode::PunchPeer(const Napi::CallbackInfo& info) {
|
|
564
|
+
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
565
|
+
Napi::Env env = info.Env();
|
|
566
|
+
if (info.Length() < 1 || !info[0].IsString()) {
|
|
567
|
+
Napi::TypeError::New(env, "Expected peerId (string)").ThrowAsJavaScriptException();
|
|
568
|
+
return env.Undefined();
|
|
1103
569
|
}
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
570
|
+
std::string peer = info[0].As<Napi::String>().Utf8Value();
|
|
571
|
+
throw_on_error(env, rats_punch_peer(node_, peer.c_str()));
|
|
572
|
+
return env.Undefined();
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// Relaying. serveAsRelay (default false) also carries OTHER peers' connections,
|
|
576
|
+
// which spends real bandwidth — so it is opted into rather than assumed.
|
|
577
|
+
void RatsNode::EnableRelay(const Napi::CallbackInfo& info) {
|
|
578
|
+
RATS_REQUIRE_NODE();
|
|
579
|
+
int serve = 0;
|
|
580
|
+
if (info.Length() >= 1 && info[0].IsBoolean()) serve = info[0].As<Napi::Boolean>().Value() ? 1 : 0;
|
|
581
|
+
throw_on_error(info.Env(), rats_enable_relay(node_, serve));
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// Reach a peer through a relay. Non-blocking: success arrives as onPeerConnected.
|
|
585
|
+
Napi::Value RatsNode::ConnectViaRelay(const Napi::CallbackInfo& info) {
|
|
586
|
+
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
587
|
+
Napi::Env env = info.Env();
|
|
588
|
+
if (info.Length() < 1 || !info[0].IsString()) {
|
|
589
|
+
Napi::TypeError::New(env, "Expected peerId (string)").ThrowAsJavaScriptException();
|
|
590
|
+
return env.Undefined();
|
|
1117
591
|
}
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
592
|
+
std::string peer = info[0].As<Napi::String>().Utf8Value();
|
|
593
|
+
throw_on_error(env, rats_connect_via_relay(node_, peer.c_str()));
|
|
594
|
+
return env.Undefined();
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// What the mesh has shown about this node's own NAT (a RATS_NAT_* value).
|
|
598
|
+
Napi::Value RatsNode::GetNatMapping(const Napi::CallbackInfo& info) {
|
|
599
|
+
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
600
|
+
return Napi::Number::New(info.Env(), rats_nat_mapping(node_));
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// ---------------------------------------------------------------------------
|
|
604
|
+
// Pub/sub
|
|
605
|
+
// ---------------------------------------------------------------------------
|
|
606
|
+
|
|
607
|
+
void RatsNode::EnablePubsub(const Napi::CallbackInfo& info) {
|
|
608
|
+
RATS_REQUIRE_NODE();
|
|
609
|
+
throw_on_error(info.Env(), rats_enable_pubsub(node_));
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// rats_topic_cb(user, peer_id_hex, topic, data, len) -> JS (peerId, topic, Buffer)
|
|
613
|
+
void RatsNode::Subscribe(const Napi::CallbackInfo& info) {
|
|
614
|
+
RATS_REQUIRE_NODE();
|
|
615
|
+
Napi::Env env = info.Env();
|
|
616
|
+
if (info.Length() < 2 || !info[0].IsString() || !info[1].IsFunction()) {
|
|
617
|
+
Napi::TypeError::New(env, "Expected topic (string) and callback (function)")
|
|
618
|
+
.ThrowAsJavaScriptException();
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
std::string topic = info[0].As<Napi::String>().Utf8Value();
|
|
622
|
+
CbContext* ctx = new_handler();
|
|
623
|
+
ctx->init(env, info[1].As<Napi::Function>(), "on_topic");
|
|
624
|
+
auto trampoline = [](void* user, const char* peer_id, const char* topic,
|
|
625
|
+
const void* data, size_t len) {
|
|
626
|
+
auto* c = static_cast<CbContext*>(user);
|
|
627
|
+
std::string peer = peer_id ? peer_id : "";
|
|
628
|
+
std::string t = topic ? topic : "";
|
|
629
|
+
std::vector<uint8_t> bytes(static_cast<const uint8_t*>(data),
|
|
630
|
+
static_cast<const uint8_t*>(data) + len);
|
|
631
|
+
c->tsfn.BlockingCall([peer, t, bytes](Napi::Env env, Napi::Function js) {
|
|
632
|
+
js.Call({Napi::String::New(env, peer),
|
|
633
|
+
Napi::String::New(env, t),
|
|
634
|
+
Napi::Buffer<uint8_t>::Copy(env, bytes.data(), bytes.size())});
|
|
635
|
+
});
|
|
636
|
+
};
|
|
637
|
+
throw_on_error(env, rats_subscribe(node_, topic.c_str(), trampoline, ctx));
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
void RatsNode::Unsubscribe(const Napi::CallbackInfo& info) {
|
|
641
|
+
RATS_REQUIRE_NODE();
|
|
642
|
+
Napi::Env env = info.Env();
|
|
643
|
+
if (info.Length() < 1 || !info[0].IsString()) {
|
|
644
|
+
Napi::TypeError::New(env, "Expected topic (string)").ThrowAsJavaScriptException();
|
|
645
|
+
return;
|
|
1135
646
|
}
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
647
|
+
std::string topic = info[0].As<Napi::String>().Utf8Value();
|
|
648
|
+
throw_on_error(env, rats_unsubscribe(node_, topic.c_str()));
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
Napi::Value RatsNode::Publish(const Napi::CallbackInfo& info) {
|
|
652
|
+
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
653
|
+
Napi::Env env = info.Env();
|
|
654
|
+
if (info.Length() < 2 || !info[0].IsString()) {
|
|
655
|
+
Napi::TypeError::New(env, "Expected topic (string) and data")
|
|
656
|
+
.ThrowAsJavaScriptException();
|
|
657
|
+
return env.Undefined();
|
|
1145
658
|
}
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
659
|
+
std::string topic = info[0].As<Napi::String>().Utf8Value();
|
|
660
|
+
std::vector<uint8_t> data;
|
|
661
|
+
if (!to_bytes(env, info[1], data)) return env.Undefined();
|
|
662
|
+
throw_on_error(env, rats_publish(node_, topic.c_str(), data.data(), data.size()));
|
|
663
|
+
return env.Undefined();
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// ---------------------------------------------------------------------------
|
|
667
|
+
// Typed JSON
|
|
668
|
+
// ---------------------------------------------------------------------------
|
|
669
|
+
|
|
670
|
+
void RatsNode::EnableJson(const Napi::CallbackInfo& info) {
|
|
671
|
+
RATS_REQUIRE_NODE();
|
|
672
|
+
throw_on_error(info.Env(), rats_enable_json(node_));
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
void RatsNode::OnJsonImpl(const Napi::CallbackInfo& info, bool once) {
|
|
676
|
+
Napi::Env env = info.Env();
|
|
677
|
+
if (info.Length() < 2 || !info[0].IsString() || !info[1].IsFunction()) {
|
|
678
|
+
Napi::TypeError::New(env, "Expected type (string) and callback (function)")
|
|
679
|
+
.ThrowAsJavaScriptException();
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
std::string type = info[0].As<Napi::String>().Utf8Value();
|
|
683
|
+
CbContext* ctx = new_handler();
|
|
684
|
+
ctx->init(env, info[1].As<Napi::Function>(), "on_json");
|
|
685
|
+
auto trampoline = [](void* user, const char* peer_id, const char* json) {
|
|
686
|
+
auto* c = static_cast<CbContext*>(user);
|
|
687
|
+
std::string peer = peer_id ? peer_id : "";
|
|
688
|
+
std::string j = json ? json : "";
|
|
689
|
+
c->tsfn.BlockingCall([peer, j](Napi::Env env, Napi::Function js) {
|
|
690
|
+
js.Call({Napi::String::New(env, peer), Napi::String::New(env, j)});
|
|
691
|
+
});
|
|
692
|
+
};
|
|
693
|
+
if (once)
|
|
694
|
+
throw_on_error(env, rats_once_json(node_, type.c_str(), trampoline, ctx));
|
|
695
|
+
else
|
|
696
|
+
throw_on_error(env, rats_on_json(node_, type.c_str(), trampoline, ctx));
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
void RatsNode::OnJson(const Napi::CallbackInfo& info) { OnJsonImpl(info, false); }
|
|
700
|
+
void RatsNode::OnceJson(const Napi::CallbackInfo& info) { OnJsonImpl(info, true); }
|
|
701
|
+
|
|
702
|
+
void RatsNode::OffJson(const Napi::CallbackInfo& info) {
|
|
703
|
+
RATS_REQUIRE_NODE();
|
|
704
|
+
Napi::Env env = info.Env();
|
|
705
|
+
if (info.Length() < 1 || !info[0].IsString()) {
|
|
706
|
+
Napi::TypeError::New(env, "Expected type (string)").ThrowAsJavaScriptException();
|
|
707
|
+
return;
|
|
1156
708
|
}
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
callback_data->callback = Napi::Persistent(info[0].As<Napi::Function>());
|
|
1169
|
-
|
|
1170
|
-
connection_callbacks[client_] = callback_data;
|
|
1171
|
-
rats_set_connection_callback(client_, connection_callback_wrapper, client_);
|
|
709
|
+
std::string type = info[0].As<Napi::String>().Utf8Value();
|
|
710
|
+
throw_on_error(env, rats_off_json(node_, type.c_str()));
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
Napi::Value RatsNode::SendJson(const Napi::CallbackInfo& info) {
|
|
714
|
+
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
715
|
+
Napi::Env env = info.Env();
|
|
716
|
+
if (info.Length() < 3 || !info[0].IsString() || !info[1].IsString() || !info[2].IsString()) {
|
|
717
|
+
Napi::TypeError::New(env, "Expected peerId (string), type (string), json (string)")
|
|
718
|
+
.ThrowAsJavaScriptException();
|
|
719
|
+
return env.Undefined();
|
|
1172
720
|
}
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
721
|
+
std::string peer = info[0].As<Napi::String>().Utf8Value();
|
|
722
|
+
std::string type = info[1].As<Napi::String>().Utf8Value();
|
|
723
|
+
std::string json = info[2].As<Napi::String>().Utf8Value();
|
|
724
|
+
throw_on_error(env, rats_send_json(node_, peer.c_str(), type.c_str(), json.c_str()));
|
|
725
|
+
return env.Undefined();
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
Napi::Value RatsNode::BroadcastJson(const Napi::CallbackInfo& info) {
|
|
729
|
+
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
730
|
+
Napi::Env env = info.Env();
|
|
731
|
+
if (info.Length() < 2 || !info[0].IsString() || !info[1].IsString()) {
|
|
732
|
+
Napi::TypeError::New(env, "Expected type (string) and json (string)")
|
|
733
|
+
.ThrowAsJavaScriptException();
|
|
734
|
+
return env.Undefined();
|
|
1187
735
|
}
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
736
|
+
std::string type = info[0].As<Napi::String>().Utf8Value();
|
|
737
|
+
std::string json = info[1].As<Napi::String>().Utf8Value();
|
|
738
|
+
throw_on_error(env, rats_broadcast_json(node_, type.c_str(), json.c_str()));
|
|
739
|
+
return env.Undefined();
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// ---------------------------------------------------------------------------
|
|
743
|
+
// File transfer
|
|
744
|
+
// ---------------------------------------------------------------------------
|
|
745
|
+
|
|
746
|
+
void RatsNode::EnableFileTransfer(const Napi::CallbackInfo& info) {
|
|
747
|
+
RATS_REQUIRE_NODE();
|
|
748
|
+
Napi::Env env = info.Env();
|
|
749
|
+
std::string tmp;
|
|
750
|
+
const char* tmp_ptr = nullptr;
|
|
751
|
+
if (info.Length() >= 1 && info[0].IsString()) {
|
|
752
|
+
tmp = info[0].As<Napi::String>().Utf8Value();
|
|
753
|
+
tmp_ptr = tmp.c_str();
|
|
1202
754
|
}
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
755
|
+
throw_on_error(env, rats_enable_file_transfer(node_, tmp_ptr));
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// rats_file_offer_cb(user, peer_id, transfer_id, name, size, is_directory)
|
|
759
|
+
void RatsNode::OnFileOffer(const Napi::CallbackInfo& info) {
|
|
760
|
+
RATS_REQUIRE_NODE();
|
|
761
|
+
Napi::Env env = info.Env();
|
|
762
|
+
if (info.Length() < 1 || !info[0].IsFunction()) {
|
|
763
|
+
Napi::TypeError::New(env, "Expected callback function").ThrowAsJavaScriptException();
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
on_file_offer_ = std::make_unique<CbContext>();
|
|
767
|
+
on_file_offer_->init(env, info[0].As<Napi::Function>(), "on_file_offer");
|
|
768
|
+
auto trampoline = [](void* user, const char* peer_id, uint64_t transfer_id,
|
|
769
|
+
const char* name, uint64_t size, int is_directory) {
|
|
770
|
+
auto* c = static_cast<CbContext*>(user);
|
|
771
|
+
std::string peer = peer_id ? peer_id : "";
|
|
772
|
+
std::string n = name ? name : "";
|
|
773
|
+
bool isdir = is_directory != 0;
|
|
774
|
+
c->tsfn.BlockingCall([peer, transfer_id, n, size, isdir](Napi::Env env, Napi::Function js) {
|
|
775
|
+
js.Call({Napi::String::New(env, peer),
|
|
776
|
+
Napi::Number::New(env, static_cast<double>(transfer_id)),
|
|
777
|
+
Napi::String::New(env, n),
|
|
778
|
+
Napi::Number::New(env, static_cast<double>(size)),
|
|
779
|
+
Napi::Boolean::New(env, isdir)});
|
|
780
|
+
});
|
|
781
|
+
};
|
|
782
|
+
throw_on_error(env, rats_on_file_offer(node_, trampoline, on_file_offer_.get()));
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
// rats_file_progress_cb(user, transfer_id, peer_id, bytes_transferred, total_bytes, status)
|
|
786
|
+
void RatsNode::OnFileProgress(const Napi::CallbackInfo& info) {
|
|
787
|
+
RATS_REQUIRE_NODE();
|
|
788
|
+
Napi::Env env = info.Env();
|
|
789
|
+
if (info.Length() < 1 || !info[0].IsFunction()) {
|
|
790
|
+
Napi::TypeError::New(env, "Expected callback function").ThrowAsJavaScriptException();
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
on_file_progress_ = std::make_unique<CbContext>();
|
|
794
|
+
on_file_progress_->init(env, info[0].As<Napi::Function>(), "on_file_progress");
|
|
795
|
+
auto trampoline = [](void* user, uint64_t transfer_id, const char* peer_id,
|
|
796
|
+
uint64_t bytes_transferred, uint64_t total_bytes, int status) {
|
|
797
|
+
auto* c = static_cast<CbContext*>(user);
|
|
798
|
+
std::string peer = peer_id ? peer_id : "";
|
|
799
|
+
c->tsfn.BlockingCall([transfer_id, peer, bytes_transferred, total_bytes, status]
|
|
800
|
+
(Napi::Env env, Napi::Function js) {
|
|
801
|
+
js.Call({Napi::Number::New(env, static_cast<double>(transfer_id)),
|
|
802
|
+
Napi::String::New(env, peer),
|
|
803
|
+
Napi::Number::New(env, static_cast<double>(bytes_transferred)),
|
|
804
|
+
Napi::Number::New(env, static_cast<double>(total_bytes)),
|
|
805
|
+
Napi::Number::New(env, status)});
|
|
806
|
+
});
|
|
807
|
+
};
|
|
808
|
+
throw_on_error(env, rats_on_file_progress(node_, trampoline, on_file_progress_.get()));
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
// rats_file_complete_cb(user, transfer_id, success, path)
|
|
812
|
+
void RatsNode::OnFileComplete(const Napi::CallbackInfo& info) {
|
|
813
|
+
RATS_REQUIRE_NODE();
|
|
814
|
+
Napi::Env env = info.Env();
|
|
815
|
+
if (info.Length() < 1 || !info[0].IsFunction()) {
|
|
816
|
+
Napi::TypeError::New(env, "Expected callback function").ThrowAsJavaScriptException();
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
on_file_complete_ = std::make_unique<CbContext>();
|
|
820
|
+
on_file_complete_->init(env, info[0].As<Napi::Function>(), "on_file_complete");
|
|
821
|
+
auto trampoline = [](void* user, uint64_t transfer_id, int success, const char* path) {
|
|
822
|
+
auto* c = static_cast<CbContext*>(user);
|
|
823
|
+
std::string p = path ? path : "";
|
|
824
|
+
bool ok = success != 0;
|
|
825
|
+
c->tsfn.BlockingCall([transfer_id, ok, p](Napi::Env env, Napi::Function js) {
|
|
826
|
+
js.Call({Napi::Number::New(env, static_cast<double>(transfer_id)),
|
|
827
|
+
Napi::Boolean::New(env, ok),
|
|
828
|
+
Napi::String::New(env, p)});
|
|
829
|
+
});
|
|
830
|
+
};
|
|
831
|
+
throw_on_error(env, rats_on_file_complete(node_, trampoline, on_file_complete_.get()));
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
Napi::Value RatsNode::SendFile(const Napi::CallbackInfo& info) {
|
|
835
|
+
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
836
|
+
Napi::Env env = info.Env();
|
|
837
|
+
if (info.Length() < 2 || !info[0].IsString() || !info[1].IsString()) {
|
|
838
|
+
Napi::TypeError::New(env, "Expected peerId (string) and path (string)")
|
|
839
|
+
.ThrowAsJavaScriptException();
|
|
840
|
+
return env.Undefined();
|
|
1217
841
|
}
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
rats_set_disconnect_callback(client_, disconnect_callback_wrapper, client_);
|
|
842
|
+
std::string peer = info[0].As<Napi::String>().Utf8Value();
|
|
843
|
+
std::string path = info[1].As<Napi::String>().Utf8Value();
|
|
844
|
+
uint64_t id = rats_send_file(node_, peer.c_str(), path.c_str());
|
|
845
|
+
return Napi::Number::New(env, static_cast<double>(id));
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
Napi::Value RatsNode::SendDirectory(const Napi::CallbackInfo& info) {
|
|
849
|
+
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
850
|
+
Napi::Env env = info.Env();
|
|
851
|
+
if (info.Length() < 2 || !info[0].IsString() || !info[1].IsString()) {
|
|
852
|
+
Napi::TypeError::New(env, "Expected peerId (string) and dirPath (string)")
|
|
853
|
+
.ThrowAsJavaScriptException();
|
|
854
|
+
return env.Undefined();
|
|
1232
855
|
}
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
856
|
+
std::string peer = info[0].As<Napi::String>().Utf8Value();
|
|
857
|
+
std::string path = info[1].As<Napi::String>().Utf8Value();
|
|
858
|
+
uint64_t id = rats_send_directory(node_, peer.c_str(), path.c_str());
|
|
859
|
+
return Napi::Number::New(env, static_cast<double>(id));
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
// Shared decoder for the (peerId, transferId[, dest]) control calls.
|
|
863
|
+
static bool parse_xfer_args(const Napi::CallbackInfo& info, std::string& peer,
|
|
864
|
+
uint64_t& transfer_id) {
|
|
865
|
+
Napi::Env env = info.Env();
|
|
866
|
+
if (info.Length() < 2 || !info[0].IsString() || !info[1].IsNumber()) {
|
|
867
|
+
Napi::TypeError::New(env, "Expected peerId (string) and transferId (number)")
|
|
868
|
+
.ThrowAsJavaScriptException();
|
|
869
|
+
return false;
|
|
870
|
+
}
|
|
871
|
+
peer = info[0].As<Napi::String>().Utf8Value();
|
|
872
|
+
transfer_id = static_cast<uint64_t>(info[1].As<Napi::Number>().Int64Value());
|
|
873
|
+
return true;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
Napi::Value RatsNode::AcceptFile(const Napi::CallbackInfo& info) {
|
|
877
|
+
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
878
|
+
Napi::Env env = info.Env();
|
|
879
|
+
std::string peer; uint64_t id;
|
|
880
|
+
if (!parse_xfer_args(info, peer, id)) return env.Undefined();
|
|
881
|
+
if (info.Length() < 3 || !info[2].IsString()) {
|
|
882
|
+
Napi::TypeError::New(env, "Expected destPath (string)").ThrowAsJavaScriptException();
|
|
883
|
+
return env.Undefined();
|
|
1247
884
|
}
|
|
885
|
+
std::string dest = info[2].As<Napi::String>().Utf8Value();
|
|
886
|
+
throw_on_error(env, rats_accept_file(node_, peer.c_str(), id, dest.c_str()));
|
|
887
|
+
return env.Undefined();
|
|
888
|
+
}
|
|
1248
889
|
|
|
1249
|
-
|
|
1250
|
-
|
|
890
|
+
Napi::Value RatsNode::RejectFile(const Napi::CallbackInfo& info) {
|
|
891
|
+
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
892
|
+
Napi::Env env = info.Env();
|
|
893
|
+
std::string peer; uint64_t id;
|
|
894
|
+
if (!parse_xfer_args(info, peer, id)) return env.Undefined();
|
|
895
|
+
throw_on_error(env, rats_reject_file(node_, peer.c_str(), id));
|
|
896
|
+
return env.Undefined();
|
|
897
|
+
}
|
|
1251
898
|
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
899
|
+
Napi::Value RatsNode::CancelFile(const Napi::CallbackInfo& info) {
|
|
900
|
+
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
901
|
+
Napi::Env env = info.Env();
|
|
902
|
+
std::string peer; uint64_t id;
|
|
903
|
+
if (!parse_xfer_args(info, peer, id)) return env.Undefined();
|
|
904
|
+
throw_on_error(env, rats_cancel_file(node_, peer.c_str(), id));
|
|
905
|
+
return env.Undefined();
|
|
906
|
+
}
|
|
1256
907
|
|
|
1257
|
-
|
|
1258
|
-
|
|
908
|
+
Napi::Value RatsNode::PauseFile(const Napi::CallbackInfo& info) {
|
|
909
|
+
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
910
|
+
Napi::Env env = info.Env();
|
|
911
|
+
std::string peer; uint64_t id;
|
|
912
|
+
if (!parse_xfer_args(info, peer, id)) return env.Undefined();
|
|
913
|
+
throw_on_error(env, rats_pause_file(node_, peer.c_str(), id));
|
|
914
|
+
return env.Undefined();
|
|
915
|
+
}
|
|
1259
916
|
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
917
|
+
Napi::Value RatsNode::ResumeFile(const Napi::CallbackInfo& info) {
|
|
918
|
+
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
919
|
+
Napi::Env env = info.Env();
|
|
920
|
+
std::string peer; uint64_t id;
|
|
921
|
+
if (!parse_xfer_args(info, peer, id)) return env.Undefined();
|
|
922
|
+
throw_on_error(env, rats_resume_file(node_, peer.c_str(), id));
|
|
923
|
+
return env.Undefined();
|
|
924
|
+
}
|
|
1263
925
|
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
926
|
+
// ---------------------------------------------------------------------------
|
|
927
|
+
// Ping / reconnect
|
|
928
|
+
// ---------------------------------------------------------------------------
|
|
929
|
+
|
|
930
|
+
void RatsNode::EnablePing(const Napi::CallbackInfo& info) {
|
|
931
|
+
RATS_REQUIRE_NODE();
|
|
932
|
+
throw_on_error(info.Env(), rats_enable_ping(node_));
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
Napi::Value RatsNode::GetPeerRttMs(const Napi::CallbackInfo& info) {
|
|
936
|
+
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
937
|
+
Napi::Env env = info.Env();
|
|
938
|
+
if (info.Length() < 1 || !info[0].IsString()) {
|
|
939
|
+
Napi::TypeError::New(env, "Expected peerId (string)").ThrowAsJavaScriptException();
|
|
940
|
+
return env.Undefined();
|
|
1275
941
|
}
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
942
|
+
std::string peer = info[0].As<Napi::String>().Utf8Value();
|
|
943
|
+
int64_t rtt = rats_peer_rtt_ms(node_, peer.c_str());
|
|
944
|
+
return Napi::Number::New(env, static_cast<double>(rtt));
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
void RatsNode::EnableReconnect(const Napi::CallbackInfo& info) {
|
|
948
|
+
RATS_REQUIRE_NODE();
|
|
949
|
+
throw_on_error(info.Env(), rats_enable_reconnect(node_));
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
Napi::Value RatsNode::AddReconnect(const Napi::CallbackInfo& info) {
|
|
953
|
+
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
954
|
+
Napi::Env env = info.Env();
|
|
955
|
+
if (info.Length() < 2 || !info[0].IsString() || !info[1].IsNumber()) {
|
|
956
|
+
Napi::TypeError::New(env, "Expected host (string) and port (number)")
|
|
957
|
+
.ThrowAsJavaScriptException();
|
|
958
|
+
return env.Undefined();
|
|
1288
959
|
}
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
960
|
+
std::string host = info[0].As<Napi::String>().Utf8Value();
|
|
961
|
+
uint16_t port = static_cast<uint16_t>(info[1].As<Napi::Number>().Uint32Value());
|
|
962
|
+
throw_on_error(env, rats_add_reconnect(node_, host.c_str(), port));
|
|
963
|
+
return env.Undefined();
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
Napi::Value RatsNode::RemoveReconnect(const Napi::CallbackInfo& info) {
|
|
967
|
+
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
968
|
+
Napi::Env env = info.Env();
|
|
969
|
+
if (info.Length() < 2 || !info[0].IsString() || !info[1].IsNumber()) {
|
|
970
|
+
Napi::TypeError::New(env, "Expected host (string) and port (number)")
|
|
971
|
+
.ThrowAsJavaScriptException();
|
|
972
|
+
return env.Undefined();
|
|
1298
973
|
}
|
|
1299
|
-
|
|
974
|
+
std::string host = info[0].As<Napi::String>().Utf8Value();
|
|
975
|
+
uint16_t port = static_cast<uint16_t>(info[1].As<Napi::Number>().Uint32Value());
|
|
976
|
+
throw_on_error(env, rats_remove_reconnect(node_, host.c_str(), port));
|
|
977
|
+
return env.Undefined();
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
// ---------------------------------------------------------------------------
|
|
981
|
+
// Class registration
|
|
982
|
+
// ---------------------------------------------------------------------------
|
|
983
|
+
|
|
984
|
+
Napi::Object RatsNode::Init(Napi::Env env, Napi::Object exports) {
|
|
985
|
+
Napi::Function func = DefineClass(env, "RatsNode", {
|
|
986
|
+
// lifecycle / core
|
|
987
|
+
InstanceMethod("start", &RatsNode::Start),
|
|
988
|
+
InstanceMethod("stop", &RatsNode::Stop),
|
|
989
|
+
InstanceMethod("destroy", &RatsNode::Destroy),
|
|
990
|
+
InstanceMethod("listenPort", &RatsNode::GetListenPort),
|
|
991
|
+
InstanceMethod("localId", &RatsNode::GetLocalId),
|
|
992
|
+
InstanceMethod("protocol", &RatsNode::GetProtocol),
|
|
993
|
+
InstanceMethod("transports", &RatsNode::GetTransports),
|
|
994
|
+
// connections
|
|
995
|
+
InstanceMethod("connect", &RatsNode::Connect),
|
|
996
|
+
InstanceMethod("peerCount", &RatsNode::GetPeerCount),
|
|
997
|
+
InstanceMethod("peerIds", &RatsNode::GetPeerIds),
|
|
998
|
+
InstanceMethod("setMaxPeers", &RatsNode::SetMaxPeers),
|
|
999
|
+
InstanceMethod("maxPeers", &RatsNode::GetMaxPeers),
|
|
1000
|
+
InstanceMethod("peerTransport", &RatsNode::GetPeerTransport),
|
|
1001
|
+
InstanceMethod("peerTransports", &RatsNode::GetPeerTransports),
|
|
1002
|
+
// raw channel messaging
|
|
1003
|
+
InstanceMethod("send", &RatsNode::Send),
|
|
1004
|
+
InstanceMethod("broadcast", &RatsNode::Broadcast),
|
|
1005
|
+
InstanceMethod("on", &RatsNode::On),
|
|
1006
|
+
// peer events
|
|
1007
|
+
InstanceMethod("onPeerConnected", &RatsNode::OnPeerConnected),
|
|
1008
|
+
InstanceMethod("onPeerDisconnected", &RatsNode::OnPeerDisconnected),
|
|
1009
|
+
// discovery / NAT
|
|
1010
|
+
InstanceMethod("enableDht", &RatsNode::EnableDht),
|
|
1011
|
+
InstanceMethod("enableMdns", &RatsNode::EnableMdns),
|
|
1012
|
+
InstanceMethod("enablePortMapping", &RatsNode::EnablePortMapping),
|
|
1013
|
+
InstanceMethod("enableHolePunch", &RatsNode::EnableHolePunch),
|
|
1014
|
+
InstanceMethod("punchPeer", &RatsNode::PunchPeer),
|
|
1015
|
+
InstanceMethod("enableRelay", &RatsNode::EnableRelay),
|
|
1016
|
+
InstanceMethod("connectViaRelay", &RatsNode::ConnectViaRelay),
|
|
1017
|
+
InstanceMethod("natMapping", &RatsNode::GetNatMapping),
|
|
1018
|
+
// pub/sub
|
|
1019
|
+
InstanceMethod("enablePubsub", &RatsNode::EnablePubsub),
|
|
1020
|
+
InstanceMethod("subscribe", &RatsNode::Subscribe),
|
|
1021
|
+
InstanceMethod("unsubscribe", &RatsNode::Unsubscribe),
|
|
1022
|
+
InstanceMethod("publish", &RatsNode::Publish),
|
|
1023
|
+
// typed JSON
|
|
1024
|
+
InstanceMethod("enableJson", &RatsNode::EnableJson),
|
|
1025
|
+
InstanceMethod("onJson", &RatsNode::OnJson),
|
|
1026
|
+
InstanceMethod("onceJson", &RatsNode::OnceJson),
|
|
1027
|
+
InstanceMethod("offJson", &RatsNode::OffJson),
|
|
1028
|
+
InstanceMethod("sendJson", &RatsNode::SendJson),
|
|
1029
|
+
InstanceMethod("broadcastJson", &RatsNode::BroadcastJson),
|
|
1030
|
+
// file transfer
|
|
1031
|
+
InstanceMethod("enableFileTransfer", &RatsNode::EnableFileTransfer),
|
|
1032
|
+
InstanceMethod("onFileOffer", &RatsNode::OnFileOffer),
|
|
1033
|
+
InstanceMethod("onFileProgress", &RatsNode::OnFileProgress),
|
|
1034
|
+
InstanceMethod("onFileComplete", &RatsNode::OnFileComplete),
|
|
1035
|
+
InstanceMethod("sendFile", &RatsNode::SendFile),
|
|
1036
|
+
InstanceMethod("sendDirectory", &RatsNode::SendDirectory),
|
|
1037
|
+
InstanceMethod("acceptFile", &RatsNode::AcceptFile),
|
|
1038
|
+
InstanceMethod("rejectFile", &RatsNode::RejectFile),
|
|
1039
|
+
InstanceMethod("cancelFile", &RatsNode::CancelFile),
|
|
1040
|
+
InstanceMethod("pauseFile", &RatsNode::PauseFile),
|
|
1041
|
+
InstanceMethod("resumeFile", &RatsNode::ResumeFile),
|
|
1042
|
+
// ping / reconnect
|
|
1043
|
+
InstanceMethod("enablePing", &RatsNode::EnablePing),
|
|
1044
|
+
InstanceMethod("peerRttMs", &RatsNode::GetPeerRttMs),
|
|
1045
|
+
InstanceMethod("enableReconnect", &RatsNode::EnableReconnect),
|
|
1046
|
+
InstanceMethod("addReconnect", &RatsNode::AddReconnect),
|
|
1047
|
+
InstanceMethod("removeReconnect", &RatsNode::RemoveReconnect),
|
|
1048
|
+
});
|
|
1049
|
+
|
|
1050
|
+
exports.Set("RatsNode", func);
|
|
1051
|
+
return exports;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
// ---------------------------------------------------------------------------
|
|
1055
|
+
// Module-level functions (process-global; no node required)
|
|
1056
|
+
// ---------------------------------------------------------------------------
|
|
1300
1057
|
|
|
1301
|
-
// Library utility functions
|
|
1302
1058
|
Napi::Value GetVersionString(const Napi::CallbackInfo& info) {
|
|
1303
|
-
Napi::
|
|
1304
|
-
const char* version = rats_get_version_string();
|
|
1305
|
-
return Napi::String::New(env, version);
|
|
1059
|
+
return Napi::String::New(info.Env(), rats_version_string());
|
|
1306
1060
|
}
|
|
1307
1061
|
|
|
1308
1062
|
Napi::Value GetVersion(const Napi::CallbackInfo& info) {
|
|
1309
1063
|
Napi::Env env = info.Env();
|
|
1310
|
-
int major, minor, patch, build;
|
|
1311
|
-
|
|
1312
|
-
|
|
1064
|
+
int major = 0, minor = 0, patch = 0, build = 0;
|
|
1065
|
+
rats_version(&major, &minor, &patch, &build);
|
|
1313
1066
|
Napi::Object version = Napi::Object::New(env);
|
|
1314
1067
|
version.Set("major", Napi::Number::New(env, major));
|
|
1315
1068
|
version.Set("minor", Napi::Number::New(env, minor));
|
|
1316
1069
|
version.Set("patch", Napi::Number::New(env, patch));
|
|
1317
1070
|
version.Set("build", Napi::Number::New(env, build));
|
|
1318
|
-
|
|
1319
1071
|
return version;
|
|
1320
1072
|
}
|
|
1321
1073
|
|
|
1322
|
-
|
|
1323
|
-
Napi::
|
|
1324
|
-
Napi::Env env = info.Env();
|
|
1325
|
-
|
|
1326
|
-
if (info.Length() < 1 || !info[0].IsBoolean()) {
|
|
1327
|
-
Napi::TypeError::New(env, "Boolean expected").ThrowAsJavaScriptException();
|
|
1328
|
-
return env.Undefined();
|
|
1329
|
-
}
|
|
1330
|
-
|
|
1331
|
-
bool enabled = info[0].As<Napi::Boolean>().Value();
|
|
1332
|
-
rats_set_console_logging_enabled(enabled ? 1 : 0);
|
|
1333
|
-
return env.Undefined();
|
|
1074
|
+
Napi::Value GetGitDescribe(const Napi::CallbackInfo& info) {
|
|
1075
|
+
return Napi::String::New(info.Env(), rats_git_describe());
|
|
1334
1076
|
}
|
|
1335
1077
|
|
|
1336
|
-
Napi::Value
|
|
1337
|
-
Napi::
|
|
1338
|
-
return Napi::Boolean::New(env, rats_is_console_logging_enabled() != 0);
|
|
1078
|
+
Napi::Value GetAbi(const Napi::CallbackInfo& info) {
|
|
1079
|
+
return Napi::Number::New(info.Env(), rats_abi());
|
|
1339
1080
|
}
|
|
1340
1081
|
|
|
1341
|
-
Napi::Value
|
|
1082
|
+
Napi::Value SetLogLevel(const Napi::CallbackInfo& info) {
|
|
1342
1083
|
Napi::Env env = info.Env();
|
|
1343
|
-
|
|
1344
|
-
|
|
1084
|
+
if (info.Length() < 1 || !info[0].IsNumber()) {
|
|
1085
|
+
Napi::TypeError::New(env, "Expected log level (number)").ThrowAsJavaScriptException();
|
|
1086
|
+
return env.Undefined();
|
|
1087
|
+
}
|
|
1088
|
+
rats_set_log_level(static_cast<rats_log_level_t>(info[0].As<Napi::Number>().Int32Value()));
|
|
1089
|
+
return env.Undefined();
|
|
1345
1090
|
}
|
|
1346
1091
|
|
|
1347
|
-
Napi::Value
|
|
1092
|
+
Napi::Value SetLogFile(const Napi::CallbackInfo& info) {
|
|
1348
1093
|
Napi::Env env = info.Env();
|
|
1349
|
-
|
|
1350
|
-
|
|
1094
|
+
if (info.Length() >= 1 && info[0].IsString()) {
|
|
1095
|
+
std::string path = info[0].As<Napi::String>().Utf8Value();
|
|
1096
|
+
rats_set_log_file(path.c_str());
|
|
1097
|
+
} else {
|
|
1098
|
+
rats_set_log_file(nullptr); // NULL/empty disables file logging
|
|
1099
|
+
}
|
|
1100
|
+
return env.Undefined();
|
|
1351
1101
|
}
|
|
1352
1102
|
|
|
1353
|
-
// Constants
|
|
1103
|
+
// Constants exposed to JS (security + log levels + error codes).
|
|
1354
1104
|
Napi::Object InitConstants(Napi::Env env) {
|
|
1355
1105
|
Napi::Object constants = Napi::Object::New(env);
|
|
1356
|
-
|
|
1357
|
-
|
|
1106
|
+
|
|
1107
|
+
Napi::Object security = Napi::Object::New(env);
|
|
1108
|
+
security.Set("NOISE", Napi::Number::New(env, RATS_SECURITY_NOISE));
|
|
1109
|
+
security.Set("PLAINTEXT", Napi::Number::New(env, RATS_SECURITY_PLAINTEXT));
|
|
1110
|
+
constants.Set("SECURITY", security);
|
|
1111
|
+
|
|
1112
|
+
Napi::Object transport = Napi::Object::New(env);
|
|
1113
|
+
transport.Set("TCP", Napi::Number::New(env, RATS_TRANSPORT_TCP));
|
|
1114
|
+
transport.Set("UDP", Napi::Number::New(env, RATS_TRANSPORT_UDP));
|
|
1115
|
+
constants.Set("TRANSPORT", transport);
|
|
1116
|
+
|
|
1117
|
+
Napi::Object transportMask = Napi::Object::New(env);
|
|
1118
|
+
transportMask.Set("TCP", Napi::Number::New(env, RATS_TRANSPORT_MASK_TCP));
|
|
1119
|
+
transportMask.Set("UDP", Napi::Number::New(env, RATS_TRANSPORT_MASK_UDP));
|
|
1120
|
+
constants.Set("TRANSPORT_MASK", transportMask);
|
|
1121
|
+
|
|
1122
|
+
Napi::Object nat = Napi::Object::New(env);
|
|
1123
|
+
nat.Set("UNKNOWN", Napi::Number::New(env, RATS_NAT_UNKNOWN));
|
|
1124
|
+
nat.Set("OPEN", Napi::Number::New(env, RATS_NAT_OPEN));
|
|
1125
|
+
nat.Set("ENDPOINT_INDEPENDENT", Napi::Number::New(env, RATS_NAT_ENDPOINT_INDEPENDENT));
|
|
1126
|
+
nat.Set("ENDPOINT_DEPENDENT", Napi::Number::New(env, RATS_NAT_ENDPOINT_DEPENDENT));
|
|
1127
|
+
constants.Set("NAT_MAPPING", nat);
|
|
1128
|
+
|
|
1129
|
+
Napi::Object logLevels = Napi::Object::New(env);
|
|
1130
|
+
logLevels.Set("DEBUG", Napi::Number::New(env, RATS_LOG_DEBUG));
|
|
1131
|
+
logLevels.Set("INFO", Napi::Number::New(env, RATS_LOG_INFO));
|
|
1132
|
+
logLevels.Set("WARN", Napi::Number::New(env, RATS_LOG_WARN));
|
|
1133
|
+
logLevels.Set("ERROR", Napi::Number::New(env, RATS_LOG_ERROR));
|
|
1134
|
+
constants.Set("LOG_LEVELS", logLevels);
|
|
1135
|
+
|
|
1358
1136
|
Napi::Object errors = Napi::Object::New(env);
|
|
1359
|
-
errors.Set("
|
|
1360
|
-
errors.Set("
|
|
1361
|
-
errors.Set("
|
|
1362
|
-
errors.Set("
|
|
1363
|
-
errors.Set("
|
|
1364
|
-
errors.Set("
|
|
1365
|
-
errors.Set("
|
|
1366
|
-
errors.Set("
|
|
1137
|
+
errors.Set("OK", Napi::Number::New(env, RATS_OK));
|
|
1138
|
+
errors.Set("INVALID_ARG", Napi::Number::New(env, RATS_ERR_INVALID_ARG));
|
|
1139
|
+
errors.Set("NOT_STARTED", Napi::Number::New(env, RATS_ERR_NOT_STARTED));
|
|
1140
|
+
errors.Set("ALREADY_STARTED", Napi::Number::New(env, RATS_ERR_ALREADY_STARTED));
|
|
1141
|
+
errors.Set("NOT_ENABLED", Napi::Number::New(env, RATS_ERR_NOT_ENABLED));
|
|
1142
|
+
errors.Set("NO_SUCH_PEER", Napi::Number::New(env, RATS_ERR_NO_SUCH_PEER));
|
|
1143
|
+
errors.Set("BIND", Napi::Number::New(env, RATS_ERR_BIND));
|
|
1144
|
+
errors.Set("INTERNAL", Napi::Number::New(env, RATS_ERR_INTERNAL));
|
|
1367
1145
|
constants.Set("ERRORS", errors);
|
|
1368
|
-
|
|
1369
|
-
// ICE connection states
|
|
1370
|
-
Napi::Object iceStates = Napi::Object::New(env);
|
|
1371
|
-
iceStates.Set("NEW", Napi::Number::New(env, RATS_ICE_STATE_NEW));
|
|
1372
|
-
iceStates.Set("GATHERING", Napi::Number::New(env, RATS_ICE_STATE_GATHERING));
|
|
1373
|
-
iceStates.Set("CHECKING", Napi::Number::New(env, RATS_ICE_STATE_CHECKING));
|
|
1374
|
-
iceStates.Set("CONNECTED", Napi::Number::New(env, RATS_ICE_STATE_CONNECTED));
|
|
1375
|
-
iceStates.Set("COMPLETED", Napi::Number::New(env, RATS_ICE_STATE_COMPLETED));
|
|
1376
|
-
iceStates.Set("FAILED", Napi::Number::New(env, RATS_ICE_STATE_FAILED));
|
|
1377
|
-
iceStates.Set("DISCONNECTED", Napi::Number::New(env, RATS_ICE_STATE_DISCONNECTED));
|
|
1378
|
-
iceStates.Set("CLOSED", Napi::Number::New(env, RATS_ICE_STATE_CLOSED));
|
|
1379
|
-
constants.Set("ICE_CONNECTION_STATES", iceStates);
|
|
1380
|
-
|
|
1381
|
-
// ICE gathering states
|
|
1382
|
-
Napi::Object iceGatheringStates = Napi::Object::New(env);
|
|
1383
|
-
iceGatheringStates.Set("NEW", Napi::Number::New(env, RATS_ICE_GATHERING_NEW));
|
|
1384
|
-
iceGatheringStates.Set("GATHERING", Napi::Number::New(env, RATS_ICE_GATHERING_GATHERING));
|
|
1385
|
-
iceGatheringStates.Set("COMPLETE", Napi::Number::New(env, RATS_ICE_GATHERING_COMPLETE));
|
|
1386
|
-
constants.Set("ICE_GATHERING_STATES", iceGatheringStates);
|
|
1387
|
-
|
|
1388
|
-
// ICE candidate types
|
|
1389
|
-
Napi::Object iceCandidateTypes = Napi::Object::New(env);
|
|
1390
|
-
iceCandidateTypes.Set("HOST", Napi::Number::New(env, RATS_ICE_CANDIDATE_HOST));
|
|
1391
|
-
iceCandidateTypes.Set("SRFLX", Napi::Number::New(env, RATS_ICE_CANDIDATE_SRFLX));
|
|
1392
|
-
iceCandidateTypes.Set("PRFLX", Napi::Number::New(env, RATS_ICE_CANDIDATE_PRFLX));
|
|
1393
|
-
iceCandidateTypes.Set("RELAY", Napi::Number::New(env, RATS_ICE_CANDIDATE_RELAY));
|
|
1394
|
-
constants.Set("ICE_CANDIDATE_TYPES", iceCandidateTypes);
|
|
1395
|
-
|
|
1146
|
+
|
|
1396
1147
|
return constants;
|
|
1397
1148
|
}
|
|
1398
1149
|
|
|
1399
|
-
// Module initialization
|
|
1400
1150
|
Napi::Object Init(Napi::Env env, Napi::Object exports) {
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
// Export utility functions
|
|
1151
|
+
RatsNode::Init(env, exports);
|
|
1152
|
+
|
|
1405
1153
|
exports.Set("getVersionString", Napi::Function::New(env, GetVersionString));
|
|
1406
1154
|
exports.Set("getVersion", Napi::Function::New(env, GetVersion));
|
|
1407
1155
|
exports.Set("getGitDescribe", Napi::Function::New(env, GetGitDescribe));
|
|
1408
1156
|
exports.Set("getAbi", Napi::Function::New(env, GetAbi));
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
exports.Set("setConsoleLoggingEnabled", Napi::Function::New(env, SetConsoleLoggingEnabled));
|
|
1412
|
-
exports.Set("isConsoleLoggingEnabled", Napi::Function::New(env, IsConsoleLoggingEnabled));
|
|
1413
|
-
|
|
1414
|
-
// Export constants
|
|
1157
|
+
exports.Set("setLogLevel", Napi::Function::New(env, SetLogLevel));
|
|
1158
|
+
exports.Set("setLogFile", Napi::Function::New(env, SetLogFile));
|
|
1415
1159
|
exports.Set("constants", InitConstants(env));
|
|
1416
|
-
|
|
1160
|
+
|
|
1417
1161
|
return exports;
|
|
1418
1162
|
}
|
|
1419
1163
|
|