librats 2.1.4 → 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 +9 -0
- package/lib/index.d.ts +21 -4
- package/lib/index.js +26 -2
- package/native-src/CMakeLists.txt +8 -0
- package/native-src/src/librats/bindings/rats.cpp +34 -3
- package/native-src/src/librats/bindings/rats.h +30 -2
- package/native-src/src/librats/core/types.cpp +3 -2
- package/native-src/src/librats/core/types.h +16 -6
- package/native-src/src/librats/node/circuit_service.h +84 -0
- package/native-src/src/librats/node/node.cpp +39 -0
- package/native-src/src/librats/node/node.h +12 -1
- package/native-src/src/librats/peer/peer_table.cpp +41 -8
- package/native-src/src/librats/subsystems/hole_punch.cpp +81 -8
- package/native-src/src/librats/subsystems/hole_punch.h +24 -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/reactor.cpp +56 -8
- package/native-src/src/librats/transport/reactor.h +38 -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/wire/frame.h +1 -0
- package/package.json +1 -1
- package/src/librats_node.cpp +26 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file relay_link.h
|
|
5
|
+
* @brief A peer connection carried inside another peer's connection.
|
|
6
|
+
*
|
|
7
|
+
* When two nodes cannot reach each other directly — a symmetric NAT on one side,
|
|
8
|
+
* a network that swallows UDP, a hole punch that simply did not land — the one
|
|
9
|
+
* thing left is to route their bytes through a node they can both reach. The
|
|
10
|
+
* question is *where* in the stack to do that, and this file is the answer: at the
|
|
11
|
+
* very bottom, as one more way of obtaining the ordered, reliable byte stream a
|
|
12
|
+
* Connection runs on (see link.h).
|
|
13
|
+
*
|
|
14
|
+
* That choice is what makes a relayed peer indistinguishable from a direct one
|
|
15
|
+
* everywhere above:
|
|
16
|
+
*
|
|
17
|
+
* - the Noise_XX handshake runs END TO END between the two peers, so the relay
|
|
18
|
+
* carries ciphertext it cannot read, cannot forge and cannot replay into
|
|
19
|
+
* anything. It is a pipe, not a party;
|
|
20
|
+
* - the self-certifying PeerId and the protocol_id bound into the handshake are
|
|
21
|
+
* checked exactly as usual, so a relay cannot impersonate either end;
|
|
22
|
+
* - block framing, the send queue and its high-water mark, the establish
|
|
23
|
+
* deadline, identify, and the peer table's duplicate resolution all apply
|
|
24
|
+
* unchanged, because there is nothing here for them to know about;
|
|
25
|
+
* - every subsystem — pub/sub, file transfer, PEX — works over a relayed peer
|
|
26
|
+
* without a line of its own.
|
|
27
|
+
*
|
|
28
|
+
* Relaying at the message level instead (a third node forwarding application
|
|
29
|
+
* frames) would have meant a second handshake, a second authentication scheme and
|
|
30
|
+
* a second backpressure story — and would have handed the relay the plaintext.
|
|
31
|
+
*
|
|
32
|
+
* ── The two halves ──────────────────────────────────────────────────────────
|
|
33
|
+
* `Circuit` is the state of one relayed connection: the bytes in flight in each
|
|
34
|
+
* direction and the credit windows that bound them. `RelayLink` is the paper-thin
|
|
35
|
+
* adapter that presents that state to a Connection as a Link. They are separate
|
|
36
|
+
* because the Connection owns its Link outright (unique_ptr, destroyed on the
|
|
37
|
+
* reactor thread) while the module driving the relay protocol has to keep hold of
|
|
38
|
+
* the same circuit to feed it — so the state is shared and only the adapter is
|
|
39
|
+
* owned.
|
|
40
|
+
*
|
|
41
|
+
* `CircuitCarrier` is the other direction: what the circuit needs from whoever
|
|
42
|
+
* speaks the relay protocol. It is deliberately tiny and knows nothing about the
|
|
43
|
+
* wire format, which stays entirely in the module that implements it.
|
|
44
|
+
*
|
|
45
|
+
* ── Flow control, and why it is not optional ────────────────────────────────
|
|
46
|
+
* A relay reads from one peer and writes to another, and it cannot stop reading:
|
|
47
|
+
* the reactor drains a readable connection to the end. So without a bound, a slow
|
|
48
|
+
* receiver makes the relay's send queue to it grow until that peer is dropped as a
|
|
49
|
+
* slow consumer — losing all of its traffic, not just the circuit's, and letting
|
|
50
|
+
* any pair of peers spend a stranger's memory at will.
|
|
51
|
+
*
|
|
52
|
+
* The bound is an end-to-end credit window, granted by the receiver and spent by
|
|
53
|
+
* the sender, exactly as in a stream multiplexer. Each end advertises how much it
|
|
54
|
+
* is willing to have in flight; the sender may not exceed it, and the receiver
|
|
55
|
+
* grants more as it consumes. Two things follow:
|
|
56
|
+
*
|
|
57
|
+
* - the relay can never hold more than about one window per circuit, whatever
|
|
58
|
+
* either end does, so its memory is bounded by configuration rather than by
|
|
59
|
+
* the goodwill of its users;
|
|
60
|
+
* - a slow path makes the circuit slow instead of making it fail. The window
|
|
61
|
+
* only ever binds when the far end is not keeping up, which is precisely when
|
|
62
|
+
* something ought to give.
|
|
63
|
+
*
|
|
64
|
+
* Credit is returned as the bytes are read OUT of the circuit, not as they arrive,
|
|
65
|
+
* and in batches (kCreditGrantFraction of the window) so the grant costs one small
|
|
66
|
+
* message per window-worth of data rather than one per chunk.
|
|
67
|
+
*
|
|
68
|
+
* ── Threading ───────────────────────────────────────────────────────────────
|
|
69
|
+
* A Circuit is touched only by the reactor thread that owns its Connection, which
|
|
70
|
+
* is by construction the same thread that owns the carrier's connection (see
|
|
71
|
+
* node/circuit_service.h). It therefore holds no locks and no atomics, like
|
|
72
|
+
* everything else on this path. The module that owns circuits may keep its table
|
|
73
|
+
* of them under a mutex; the state below must not be reached from off that one
|
|
74
|
+
* thread.
|
|
75
|
+
*/
|
|
76
|
+
|
|
77
|
+
#include "librats/util/rats_export.h"
|
|
78
|
+
#include "librats/core/bytes.h"
|
|
79
|
+
#include "librats/core/types.h"
|
|
80
|
+
#include "librats/transport/link.h"
|
|
81
|
+
|
|
82
|
+
#include <cstdint>
|
|
83
|
+
#include <memory>
|
|
84
|
+
|
|
85
|
+
namespace librats {
|
|
86
|
+
|
|
87
|
+
/// What a circuit needs from whoever speaks the relay protocol on the wire.
|
|
88
|
+
///
|
|
89
|
+
/// Implemented by the relay module; called by Circuit on the reactor thread. None
|
|
90
|
+
/// of these say anything about the wire format — the implementer frames the
|
|
91
|
+
/// message and hands it to the carrier peer — so this header stays a transport
|
|
92
|
+
/// header with no protocol in it.
|
|
93
|
+
class CircuitCarrier {
|
|
94
|
+
public:
|
|
95
|
+
virtual ~CircuitCarrier() = default;
|
|
96
|
+
|
|
97
|
+
/// Send `count` slices (together no larger than kMaxDataChunk) toward the far
|
|
98
|
+
/// end as one data message.
|
|
99
|
+
/// @return whether the carrier's own send queue still has room. False does NOT
|
|
100
|
+
/// mean the data was dropped — it was queued like any other message —
|
|
101
|
+
/// it means stop, and say so again through on_carrier_writable() once
|
|
102
|
+
/// the queue drains. Exactly what PeerNetwork::send answers, which is
|
|
103
|
+
/// what an implementation normally forwards.
|
|
104
|
+
virtual bool circuit_send_data(uint32_t circuit, const ByteView* slices, size_t count) = 0;
|
|
105
|
+
|
|
106
|
+
/// Grant the far end `bytes` more of our receive window.
|
|
107
|
+
virtual void circuit_send_credit(uint32_t circuit, uint32_t bytes) = 0;
|
|
108
|
+
|
|
109
|
+
/// Tell the far end (and the relay in between) that this circuit is over.
|
|
110
|
+
/// Called at most once per circuit.
|
|
111
|
+
virtual void circuit_send_close(uint32_t circuit, CloseReason reason) = 0;
|
|
112
|
+
|
|
113
|
+
/// The Link is gone: its Connection has been destroyed. The circuit will not
|
|
114
|
+
/// be read or written again and its bookkeeping can be dropped.
|
|
115
|
+
///
|
|
116
|
+
/// The only one of these that is NOT guaranteed to arrive on the reactor
|
|
117
|
+
/// thread. It normally does — a Connection is destroyed by its reactor — but a
|
|
118
|
+
/// link handed to a reactor that never ran the adoption (the node was stopping)
|
|
119
|
+
/// is released when the task queue is destroyed instead, on whichever thread
|
|
120
|
+
/// takes the reactor down. An implementation must therefore be safe to call
|
|
121
|
+
/// here from any thread, and must not assume the circuit ever opened.
|
|
122
|
+
virtual void circuit_released(uint32_t circuit) = 0;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
/// One relayed connection's byte stream and credit windows. See the file comment.
|
|
126
|
+
class RATS_API Circuit {
|
|
127
|
+
public:
|
|
128
|
+
/// Largest data message a circuit puts on the wire. Big enough that the
|
|
129
|
+
/// per-message overhead (a relay header, a frame header, an AEAD tag, and the
|
|
130
|
+
/// relay's own copy) is noise against the payload; small enough that one
|
|
131
|
+
/// circuit cannot monopolise the carrier's send queue for long, and that the
|
|
132
|
+
/// relay's per-message work stays granular.
|
|
133
|
+
static constexpr size_t kMaxDataChunk = 16 * 1024;
|
|
134
|
+
|
|
135
|
+
/// Receive window a circuit advertises unless told otherwise. This is the
|
|
136
|
+
/// memory ONE circuit can make the relay (and this node) hold, so it is the
|
|
137
|
+
/// knob that turns "how fast can a relayed peer go" into "how much is a relay
|
|
138
|
+
/// asked to carry": a 256 KiB window keeps a 100 ms round trip busy at roughly
|
|
139
|
+
/// 20 Mbit/s, which is far more than a fallback path is meant to be used for.
|
|
140
|
+
static constexpr uint32_t kDefaultWindow = 256 * 1024;
|
|
141
|
+
|
|
142
|
+
/// Credit is granted back once this much of the window has been consumed.
|
|
143
|
+
/// Halves mean one small grant per half-window of data, and leave the sender
|
|
144
|
+
/// the other half to work with while the grant is in flight.
|
|
145
|
+
static constexpr uint32_t kCreditGrantFraction = 2;
|
|
146
|
+
|
|
147
|
+
/// A circuit we are opening: the request is out, the far end has not answered.
|
|
148
|
+
/// Nothing may be written until on_accept() brings the window it will receive.
|
|
149
|
+
///
|
|
150
|
+
/// @param id the circuit's id on the carrier link (see the relay module).
|
|
151
|
+
/// @param carrier how outbound bytes leave; a shared_ptr because a Connection
|
|
152
|
+
/// can outlive the module that opened the circuit (subsystems
|
|
153
|
+
/// stop before the reactors do).
|
|
154
|
+
/// @param recv_window what we are willing to have in flight toward us.
|
|
155
|
+
static std::shared_ptr<Circuit> opening(uint32_t id, std::shared_ptr<CircuitCarrier> carrier,
|
|
156
|
+
uint32_t recv_window = kDefaultWindow);
|
|
157
|
+
|
|
158
|
+
/// A circuit opened TO us: the far end is already there and its request carried
|
|
159
|
+
/// the window it will accept, so the stream is open from the first instant.
|
|
160
|
+
///
|
|
161
|
+
/// Two factories rather than one flag plus a window that is sometimes ignored,
|
|
162
|
+
/// because "the circuit is open" and "we know what the far end will take" are
|
|
163
|
+
/// the same fact — an open circuit whose send window nobody set would be a
|
|
164
|
+
/// stream that silently refuses to carry anything.
|
|
165
|
+
static std::shared_ptr<Circuit> accepted(uint32_t id, std::shared_ptr<CircuitCarrier> carrier,
|
|
166
|
+
uint32_t peer_window,
|
|
167
|
+
uint32_t recv_window = kDefaultWindow);
|
|
168
|
+
|
|
169
|
+
Circuit(const Circuit&) = delete;
|
|
170
|
+
Circuit& operator=(const Circuit&) = delete;
|
|
171
|
+
|
|
172
|
+
uint32_t id() const noexcept { return id_; }
|
|
173
|
+
/// What we advertise to the far end as our receive window.
|
|
174
|
+
uint32_t recv_window() const noexcept { return recv_window_; }
|
|
175
|
+
bool is_open() const noexcept { return state_ == State::Open; }
|
|
176
|
+
bool is_closed() const noexcept { return state_ == State::Closed; }
|
|
177
|
+
|
|
178
|
+
// ── Fed by the relay module (reactor thread) ────────────────────────────
|
|
179
|
+
//
|
|
180
|
+
// Each of these returns the poll-equivalent events the circuit's Connection
|
|
181
|
+
// should now be given (PollIn / PollOut / PollErr), or 0 for none. The caller
|
|
182
|
+
// dispatches them — the circuit deliberately knows nothing about reactors.
|
|
183
|
+
// Returning the mask rather than raising it keeps that dependency out of the
|
|
184
|
+
// transport layer and lets the caller batch several changes into one wake.
|
|
185
|
+
|
|
186
|
+
/// Bytes arrived from the far end. A peer that overruns the window it was
|
|
187
|
+
/// granted is a protocol error and the circuit is failed rather than allowed
|
|
188
|
+
/// to grow: that window is the whole bound on what a circuit can cost.
|
|
189
|
+
uint32_t on_data(ByteView bytes);
|
|
190
|
+
|
|
191
|
+
/// The far end accepted the circuit and advertised `peer_window` bytes of
|
|
192
|
+
/// receive window. No-op unless the circuit is still pending.
|
|
193
|
+
uint32_t on_accept(uint32_t peer_window);
|
|
194
|
+
|
|
195
|
+
/// The far end granted `bytes` more of send window.
|
|
196
|
+
uint32_t on_credit(uint32_t bytes);
|
|
197
|
+
|
|
198
|
+
/// The circuit is over. `orderly` means the far end closed cleanly, so
|
|
199
|
+
/// whatever it already sent is still delivered before the end of stream is
|
|
200
|
+
/// reported; anything else fails the connection at once.
|
|
201
|
+
uint32_t on_closed(CloseReason reason, bool orderly);
|
|
202
|
+
|
|
203
|
+
/// The carrier's send queue, which had filled up, has room again.
|
|
204
|
+
uint32_t on_carrier_writable();
|
|
205
|
+
|
|
206
|
+
// ── Driven by the Connection through RelayLink (reactor thread) ─────────
|
|
207
|
+
|
|
208
|
+
Link::IoResult read(ByteSpan into);
|
|
209
|
+
Link::IoResult write(const ByteView* slices, size_t count);
|
|
210
|
+
void want_write(bool on) noexcept { want_write_ = on; }
|
|
211
|
+
CloseReason close_reason() const noexcept { return reason_; }
|
|
212
|
+
/// Begin teardown from our side: tell the far end, then refuse further I/O.
|
|
213
|
+
void shutdown(CloseReason reason);
|
|
214
|
+
/// The Link is being destroyed; release the carrier's bookkeeping. Idempotent.
|
|
215
|
+
void release();
|
|
216
|
+
|
|
217
|
+
/// Bytes waiting to be read out (diagnostics and tests).
|
|
218
|
+
size_t pending_input() const noexcept { return inbox_.size() - inbox_read_; }
|
|
219
|
+
/// Send window still available (diagnostics and tests).
|
|
220
|
+
uint32_t send_credit() const noexcept { return send_credit_; }
|
|
221
|
+
|
|
222
|
+
private:
|
|
223
|
+
Circuit(uint32_t id, std::shared_ptr<CircuitCarrier> carrier, bool open,
|
|
224
|
+
uint32_t recv_window, uint32_t peer_window);
|
|
225
|
+
|
|
226
|
+
enum class State : uint8_t {
|
|
227
|
+
Pending, ///< outbound: opened, waiting for the far end to accept
|
|
228
|
+
Open, ///< bytes may flow
|
|
229
|
+
Closed, ///< no further I/O; read() drains what is left, then reports the end
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
/// Consumed prefix tolerated before the inbox is compacted. The connection
|
|
233
|
+
/// normally drains the inbox to empty on every readable event, which resets it
|
|
234
|
+
/// for free; this only bounds the pathological case where it does not.
|
|
235
|
+
static constexpr size_t kCompactThreshold = 64 * 1024;
|
|
236
|
+
|
|
237
|
+
bool blocked() const noexcept { return credit_blocked_ || carrier_blocked_; }
|
|
238
|
+
/// PollOut, but only if anyone is waiting for it — see want_write().
|
|
239
|
+
uint32_t writable_event() const noexcept;
|
|
240
|
+
|
|
241
|
+
uint32_t id_;
|
|
242
|
+
std::shared_ptr<CircuitCarrier> carrier_;
|
|
243
|
+
State state_;
|
|
244
|
+
CloseReason reason_ = CloseReason::PeerReset;
|
|
245
|
+
bool orderly_ = false; ///< the far end closed cleanly
|
|
246
|
+
bool released_ = false;
|
|
247
|
+
bool close_sent_ = false;
|
|
248
|
+
|
|
249
|
+
// — inbound —
|
|
250
|
+
Bytes inbox_;
|
|
251
|
+
size_t inbox_read_ = 0; ///< consumed prefix of inbox_
|
|
252
|
+
uint32_t recv_window_; ///< what we advertised to the far end
|
|
253
|
+
uint32_t recv_in_flight_ = 0; ///< delivered to us and not yet granted back
|
|
254
|
+
uint32_t recv_ungranted_ = 0; ///< consumed, owed back to the far end
|
|
255
|
+
|
|
256
|
+
// — outbound —
|
|
257
|
+
uint32_t send_credit_ = 0; ///< bytes the far end still allows us to send
|
|
258
|
+
bool want_write_ = false; ///< the Connection has more to write
|
|
259
|
+
bool credit_blocked_ = false; ///< a write stopped for want of credit
|
|
260
|
+
bool carrier_blocked_ = false;///< a write stopped on the carrier's queue
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
/// The Link a Connection holds for a relayed circuit.
|
|
264
|
+
///
|
|
265
|
+
/// Deliberately empty of logic: the state has to be reachable by the module that
|
|
266
|
+
/// drives the relay protocol, which the Connection's unique_ptr would not allow,
|
|
267
|
+
/// so everything lives in the shared Circuit and this is the handle.
|
|
268
|
+
class RATS_API RelayLink final : public Link {
|
|
269
|
+
public:
|
|
270
|
+
explicit RelayLink(std::shared_ptr<Circuit> circuit) : circuit_(std::move(circuit)) {}
|
|
271
|
+
~RelayLink() override { circuit_->release(); }
|
|
272
|
+
|
|
273
|
+
TransportKind kind() const noexcept override { return TransportKind::Relay; }
|
|
274
|
+
|
|
275
|
+
IoResult read(ByteSpan into) override { return circuit_->read(into); }
|
|
276
|
+
IoResult write(const ByteView* slices, size_t count) override {
|
|
277
|
+
return circuit_->write(slices, count);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/// A circuit is "connected" once the far end has accepted it. The Connection
|
|
281
|
+
/// only asks after a PollOut, which the relay module raises on acceptance; a
|
|
282
|
+
/// refusal arrives as PollErr instead and never reaches here.
|
|
283
|
+
bool connect_completed() override { return circuit_->is_open(); }
|
|
284
|
+
|
|
285
|
+
void want_write(bool on) override { circuit_->want_write(on); }
|
|
286
|
+
|
|
287
|
+
/// No endpoint of its own, and deliberately not the relay's: identify pairs the
|
|
288
|
+
/// address a link reports with the peer's advertised listen port to build a
|
|
289
|
+
/// dialable address, and NatStatus reads it as our own NAT mapping. Both would
|
|
290
|
+
/// be wrong here — the address on this path belongs to the node in the middle.
|
|
291
|
+
/// Reporting nothing is what keeps a relayed peer out of that machinery.
|
|
292
|
+
std::optional<Address> remote_endpoint() const override { return std::nullopt; }
|
|
293
|
+
|
|
294
|
+
CloseReason error_reason() const override { return circuit_->close_reason(); }
|
|
295
|
+
void close(CloseReason reason) override { circuit_->shutdown(reason); }
|
|
296
|
+
|
|
297
|
+
Circuit& circuit() noexcept { return *circuit_; }
|
|
298
|
+
|
|
299
|
+
private:
|
|
300
|
+
std::shared_ptr<Circuit> circuit_;
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
} // namespace librats
|
|
@@ -46,6 +46,7 @@ enum class MessageType : uint8_t {
|
|
|
46
46
|
Typed = 7, ///< typed JSON message exchange (MessageJson)
|
|
47
47
|
Pex = 8, ///< peer exchange — gossip of known peer addresses (PeerExchange)
|
|
48
48
|
Punch = 9, ///< NAT hole-punch rendezvous, relayed peer→peer (HolePunch)
|
|
49
|
+
Relay = 10, ///< relayed circuits: a peer's connection carried through a third node (Relay)
|
|
49
50
|
};
|
|
50
51
|
|
|
51
52
|
/// Fixed header of an inner message.
|
package/package.json
CHANGED
package/src/librats_node.cpp
CHANGED
|
@@ -132,6 +132,8 @@ private:
|
|
|
132
132
|
void EnablePortMapping(const Napi::CallbackInfo& info);
|
|
133
133
|
void EnableHolePunch(const Napi::CallbackInfo& info);
|
|
134
134
|
Napi::Value PunchPeer(const Napi::CallbackInfo& info);
|
|
135
|
+
void EnableRelay(const Napi::CallbackInfo& info);
|
|
136
|
+
Napi::Value ConnectViaRelay(const Napi::CallbackInfo& info);
|
|
135
137
|
Napi::Value GetNatMapping(const Napi::CallbackInfo& info);
|
|
136
138
|
|
|
137
139
|
// ---- pub/sub ----
|
|
@@ -570,6 +572,28 @@ Napi::Value RatsNode::PunchPeer(const Napi::CallbackInfo& info) {
|
|
|
570
572
|
return env.Undefined();
|
|
571
573
|
}
|
|
572
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();
|
|
591
|
+
}
|
|
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
|
+
|
|
573
597
|
// What the mesh has shown about this node's own NAT (a RATS_NAT_* value).
|
|
574
598
|
Napi::Value RatsNode::GetNatMapping(const Napi::CallbackInfo& info) {
|
|
575
599
|
RATS_REQUIRE_NODE(info.Env().Undefined());
|
|
@@ -988,6 +1012,8 @@ Napi::Object RatsNode::Init(Napi::Env env, Napi::Object exports) {
|
|
|
988
1012
|
InstanceMethod("enablePortMapping", &RatsNode::EnablePortMapping),
|
|
989
1013
|
InstanceMethod("enableHolePunch", &RatsNode::EnableHolePunch),
|
|
990
1014
|
InstanceMethod("punchPeer", &RatsNode::PunchPeer),
|
|
1015
|
+
InstanceMethod("enableRelay", &RatsNode::EnableRelay),
|
|
1016
|
+
InstanceMethod("connectViaRelay", &RatsNode::ConnectViaRelay),
|
|
991
1017
|
InstanceMethod("natMapping", &RatsNode::GetNatMapping),
|
|
992
1018
|
// pub/sub
|
|
993
1019
|
InstanceMethod("enablePubsub", &RatsNode::EnablePubsub),
|