@cc-remote/iroh 1.0.0-rc.3

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.
@@ -0,0 +1,1018 @@
1
+ use std::{str::FromStr, sync::Arc};
2
+
3
+ use iroh::endpoint::{self, presets, presets::Preset as _};
4
+ use napi::{bindgen_prelude::*, threadsafe_function::ThreadsafeFunction};
5
+ use napi_derive::napi;
6
+ use tokio::sync::Mutex;
7
+
8
+ use crate::{EndpointAddr, RelayMode, SecretKey, WatchHandle, path, watch};
9
+
10
+ /// A mutable handle to an endpoint builder.
11
+ ///
12
+ /// Mirrors the uniffi `EndpointBuilder`. A "preset" in JS is simply any
13
+ /// function `(builder: EndpointBuilder) => void` — call one of the built-in
14
+ /// [`presetN0`] / [`presetMinimal`] / [`presetN0DisableRelay`] helpers (which
15
+ /// install the crypto provider) and then layer on your own configuration.
16
+ ///
17
+ /// ```js
18
+ /// const b = Endpoint.builder()
19
+ /// presetMinimal(b)
20
+ /// b.alpns([alpn])
21
+ /// const ep = await b.bind()
22
+ /// ```
23
+ #[napi]
24
+ pub struct EndpointBuilder {
25
+ inner: std::sync::Mutex<Option<iroh::endpoint::Builder>>,
26
+ }
27
+
28
+ impl EndpointBuilder {
29
+ fn new(builder: iroh::endpoint::Builder) -> Self {
30
+ Self {
31
+ inner: std::sync::Mutex::new(Some(builder)),
32
+ }
33
+ }
34
+
35
+ fn map<F>(&self, f: F)
36
+ where
37
+ F: FnOnce(iroh::endpoint::Builder) -> iroh::endpoint::Builder,
38
+ {
39
+ let mut guard = self.inner.lock().unwrap();
40
+ let b = guard.take().expect("EndpointBuilder consumed");
41
+ *guard = Some(f(b));
42
+ }
43
+ }
44
+
45
+ #[napi]
46
+ impl EndpointBuilder {
47
+ /// Replay the n0 production preset (relays + discovery + crypto provider).
48
+ #[napi]
49
+ pub fn apply_n0(&self) {
50
+ self.map(|b| presets::N0.apply(b));
51
+ }
52
+
53
+ /// Replay the minimal preset (crypto provider only, no external deps).
54
+ #[napi]
55
+ pub fn apply_minimal(&self) {
56
+ self.map(|b| presets::Minimal.apply(b));
57
+ }
58
+
59
+ /// Replay the n0 preset with relays disabled.
60
+ #[napi]
61
+ pub fn apply_n0_disable_relay(&self) {
62
+ self.map(|b| presets::N0DisableRelay.apply(b));
63
+ }
64
+
65
+ /// Set the endpoint secret key (32 bytes).
66
+ #[napi]
67
+ pub fn secret_key(&self, bytes: Vec<u8>) -> Result<()> {
68
+ let key: [u8; 32] = bytes
69
+ .try_into()
70
+ .map_err(|_| anyhow::anyhow!("secret_key must be 32 bytes"))?;
71
+ self.map(|b| b.secret_key(iroh::SecretKey::from_bytes(&key)));
72
+ Ok(())
73
+ }
74
+
75
+ /// Set the advertised ALPNs.
76
+ #[napi]
77
+ pub fn alpns(&self, alpns: Vec<Vec<u8>>) {
78
+ self.map(|b| b.alpns(alpns));
79
+ }
80
+
81
+ /// Set the relay mode.
82
+ #[napi]
83
+ pub fn relay_mode(&self, mode: &RelayMode) {
84
+ let mode = mode.0.clone();
85
+ self.map(|b| b.relay_mode(mode));
86
+ }
87
+
88
+ /// Remove all IP based transports, forcing all traffic over relays.
89
+ ///
90
+ /// This must be called *after* any preset (e.g. [`applyN0`]) that
91
+ /// installs IP transports, otherwise the preset will re-add them.
92
+ #[napi]
93
+ pub fn clear_ip_transports(&self) {
94
+ self.map(|b| b.clear_ip_transports());
95
+ }
96
+
97
+ /// Only publish relay addresses (no direct IPs) to discovery.
98
+ ///
99
+ /// Installs `AddrFilter::relay_only()` so the addresses published to
100
+ /// discovery (pkarr/DNS) are limited to relay URLs.
101
+ #[napi]
102
+ pub fn addr_filter_relay_only(&self) {
103
+ self.map(|b| b.addr_filter(iroh::address_lookup::AddrFilter::relay_only()));
104
+ }
105
+
106
+ /// Set the address the endpoint binds to (`host:port`).
107
+ #[napi]
108
+ pub fn bind_addr(&self, addr: String) -> Result<()> {
109
+ let socket = std::net::SocketAddr::from_str(&addr).map_err(anyhow::Error::from)?;
110
+ let mut guard = self.inner.lock().unwrap();
111
+ let b = guard.take().expect("EndpointBuilder consumed");
112
+ *guard = Some(b.bind_addr(socket).map_err(anyhow::Error::from)?);
113
+ Ok(())
114
+ }
115
+
116
+ /// Bind the endpoint.
117
+ #[napi]
118
+ pub async fn bind(&self) -> Result<Endpoint> {
119
+ let builder = self
120
+ .inner
121
+ .lock()
122
+ .unwrap()
123
+ .take()
124
+ .ok_or_else(|| anyhow::anyhow!("EndpointBuilder already consumed"))?;
125
+ let endpoint = builder.bind().await.map_err(|e| anyhow::anyhow!("{e:?}"))?;
126
+ Ok(Endpoint { inner: endpoint })
127
+ }
128
+ }
129
+
130
+ /// The n0 production preset (relays + discovery).
131
+ #[napi]
132
+ pub fn preset_n0(builder: &EndpointBuilder) {
133
+ builder.apply_n0();
134
+ }
135
+
136
+ /// The minimal preset (no external dependencies; good for tests / offline).
137
+ #[napi]
138
+ pub fn preset_minimal(builder: &EndpointBuilder) {
139
+ builder.apply_minimal();
140
+ }
141
+
142
+ /// The n0 preset with relays disabled.
143
+ #[napi]
144
+ pub fn preset_n0_disable_relay(builder: &EndpointBuilder) {
145
+ builder.apply_n0_disable_relay();
146
+ }
147
+
148
+ /// Which side of a connection we are.
149
+ #[derive(Debug, Clone, Copy)]
150
+ #[napi(string_enum)]
151
+ pub enum Side {
152
+ Client,
153
+ Server,
154
+ }
155
+
156
+ impl From<iroh::endpoint::Side> for Side {
157
+ fn from(s: iroh::endpoint::Side) -> Self {
158
+ match s {
159
+ iroh::endpoint::Side::Client => Side::Client,
160
+ iroh::endpoint::Side::Server => Side::Server,
161
+ }
162
+ }
163
+ }
164
+
165
+ /// Where an incoming connection came from.
166
+ #[derive(Debug, Clone)]
167
+ #[napi(object)]
168
+ pub struct IncomingAddr {
169
+ /// One of "ip" | "relay" | "custom".
170
+ pub kind: String,
171
+ /// `ip:port` for ip, relay URL for relay.
172
+ pub addr: Option<String>,
173
+ /// Remote endpoint id (relay only).
174
+ pub endpoint_id: Option<String>,
175
+ /// Debug description (custom only).
176
+ pub description: Option<String>,
177
+ }
178
+
179
+ impl From<iroh::endpoint::IncomingAddr> for IncomingAddr {
180
+ fn from(addr: iroh::endpoint::IncomingAddr) -> Self {
181
+ match addr {
182
+ iroh::endpoint::IncomingAddr::Ip(socket) => IncomingAddr {
183
+ kind: "ip".into(),
184
+ addr: Some(socket.to_string()),
185
+ endpoint_id: None,
186
+ description: None,
187
+ },
188
+ iroh::endpoint::IncomingAddr::Relay { url, endpoint_id } => IncomingAddr {
189
+ kind: "relay".into(),
190
+ addr: Some(url.to_string()),
191
+ endpoint_id: Some(endpoint_id.to_string()),
192
+ description: None,
193
+ },
194
+ iroh::endpoint::IncomingAddr::Custom(c) => IncomingAddr {
195
+ kind: "custom".into(),
196
+ addr: None,
197
+ endpoint_id: None,
198
+ description: Some(format!("{c:?}")),
199
+ },
200
+ _ => IncomingAddr {
201
+ kind: "custom".into(),
202
+ addr: None,
203
+ endpoint_id: None,
204
+ description: Some("unknown".into()),
205
+ },
206
+ }
207
+ }
208
+ }
209
+
210
+ /// The local address that received an incoming connection.
211
+ #[derive(Debug, Clone)]
212
+ #[napi(object)]
213
+ pub struct IncomingLocalAddr {
214
+ /// One of "ip" | "relay" | "custom".
215
+ pub kind: String,
216
+ pub addr: Option<String>,
217
+ pub description: Option<String>,
218
+ }
219
+
220
+ impl From<iroh::endpoint::LocalTransportAddr> for IncomingLocalAddr {
221
+ fn from(value: iroh::endpoint::LocalTransportAddr) -> Self {
222
+ match value {
223
+ iroh::endpoint::LocalTransportAddr::Ip(ip) => IncomingLocalAddr {
224
+ kind: "ip".into(),
225
+ addr: ip.map(|i| i.to_string()),
226
+ description: None,
227
+ },
228
+ iroh::endpoint::LocalTransportAddr::Relay(url) => IncomingLocalAddr {
229
+ kind: "relay".into(),
230
+ addr: Some(url.to_string()),
231
+ description: None,
232
+ },
233
+ iroh::endpoint::LocalTransportAddr::Custom(c) => IncomingLocalAddr {
234
+ kind: "custom".into(),
235
+ addr: None,
236
+ description: c.map(|c| format!("{c:?}")),
237
+ },
238
+ _ => IncomingLocalAddr {
239
+ kind: "custom".into(),
240
+ addr: None,
241
+ description: None,
242
+ },
243
+ }
244
+ }
245
+ }
246
+
247
+ /// A snapshot value for a single endpoint metric.
248
+ #[derive(Debug, Clone)]
249
+ #[napi(object)]
250
+ pub struct CounterStats {
251
+ pub value: u32,
252
+ pub description: String,
253
+ }
254
+
255
+ /// Flat snapshot of headline connection statistics.
256
+ #[derive(Debug, Clone)]
257
+ #[napi(object)]
258
+ pub struct ConnectionStats {
259
+ pub udp_tx_datagrams: i64,
260
+ pub udp_tx_bytes: i64,
261
+ pub udp_rx_datagrams: i64,
262
+ pub udp_rx_bytes: i64,
263
+ pub lost_packets: i64,
264
+ pub lost_bytes: i64,
265
+ }
266
+
267
+ /// Options passed to [`Endpoint::bind`].
268
+ ///
269
+ /// `bind` applies the n0 preset by default. For a custom preset use
270
+ /// [`Endpoint::builder`] + the `EndpointBuilder` surface.
271
+ #[derive(Debug, Default)]
272
+ #[napi(object)]
273
+ pub struct EndpointOptions {
274
+ pub bind_addr: Option<String>,
275
+ pub secret_key: Option<Vec<u8>>,
276
+ pub alpns: Option<Vec<Vec<u8>>>,
277
+ /// Force all traffic over relays ("relay-only" mode).
278
+ ///
279
+ /// When `true`, removes all IP based transports (no direct connections or
280
+ /// hole punching) and limits the addresses published to discovery to relay
281
+ /// URLs only. Defaults to `false` (n0 preset behaviour unchanged).
282
+ pub relay_only: Option<bool>,
283
+ }
284
+
285
+ /// An iroh endpoint.
286
+ #[napi]
287
+ pub struct Endpoint {
288
+ inner: endpoint::Endpoint,
289
+ }
290
+
291
+ impl Endpoint {
292
+ pub(crate) fn raw(&self) -> &endpoint::Endpoint {
293
+ &self.inner
294
+ }
295
+ }
296
+
297
+ #[napi]
298
+ impl Endpoint {
299
+ /// Create an endpoint builder (starts empty — apply a preset).
300
+ #[napi]
301
+ pub fn builder() -> EndpointBuilder {
302
+ EndpointBuilder::new(iroh::endpoint::Builder::empty())
303
+ }
304
+
305
+ /// Bind a new endpoint. Applies the n0 preset, then the given options.
306
+ /// For a custom preset use [`Endpoint::builder`].
307
+ #[napi(factory)]
308
+ pub async fn bind(
309
+ options: Option<EndpointOptions>,
310
+ relay_mode: Option<&RelayMode>,
311
+ ) -> Result<Self> {
312
+ let options = options.unwrap_or_default();
313
+ let wrapper = EndpointBuilder::new(iroh::endpoint::Builder::empty());
314
+ wrapper.apply_n0();
315
+
316
+ if let Some(secret_key) = options.secret_key {
317
+ wrapper.secret_key(secret_key)?;
318
+ }
319
+ if let Some(alpns) = options.alpns {
320
+ wrapper.alpns(alpns);
321
+ }
322
+ if let Some(relay_mode) = relay_mode {
323
+ wrapper.relay_mode(relay_mode);
324
+ }
325
+ if let Some(addr) = options.bind_addr {
326
+ wrapper.bind_addr(addr)?;
327
+ }
328
+ // Apply relay-only last so it also clears any IP transport that the
329
+ // n0 preset or `bind_addr` installed above.
330
+ if options.relay_only.unwrap_or(false) {
331
+ wrapper.clear_ip_transports();
332
+ wrapper.addr_filter_relay_only();
333
+ }
334
+
335
+ wrapper.bind().await
336
+ }
337
+
338
+ /// This endpoint's id.
339
+ #[napi]
340
+ pub fn id(&self) -> crate::EndpointId {
341
+ self.inner.id().into()
342
+ }
343
+
344
+ /// The [`EndpointAddr`] for this endpoint.
345
+ #[napi]
346
+ pub fn addr(&self) -> EndpointAddr {
347
+ self.inner.addr().into()
348
+ }
349
+
350
+ /// Look up cached information about a remote endpoint, if any.
351
+ #[napi]
352
+ pub async fn remote_addr(&self, id: &crate::EndpointId) -> Result<Option<EndpointAddr>> {
353
+ let id: iroh::EndpointId = id.into();
354
+ let info = match self.inner.remote_info(id).await {
355
+ Some(i) => i,
356
+ None => return Ok(None),
357
+ };
358
+ let id = info.id();
359
+ let addrs = info.into_addrs().map(|a| a.into_addr());
360
+ Ok(Some(iroh::EndpointAddr::from_parts(id, addrs).into()))
361
+ }
362
+
363
+ /// Current statistics for this endpoint.
364
+ #[napi]
365
+ pub fn stats(&self) -> std::collections::HashMap<String, CounterStats> {
366
+ use iroh_metrics::{MetricValue, MetricsGroupSet};
367
+ self.inner
368
+ .metrics()
369
+ .iter()
370
+ .map(|(group, item)| {
371
+ let name = format!("{}:{}", group, item.name());
372
+ let value = match item.value() {
373
+ MetricValue::Counter(v) => u32::try_from(v).unwrap_or(u32::MAX),
374
+ MetricValue::Gauge(v) => u32::try_from(v.max(0)).unwrap_or(u32::MAX),
375
+ _ => 0,
376
+ };
377
+ (
378
+ name,
379
+ CounterStats {
380
+ value,
381
+ description: item.help().to_string(),
382
+ },
383
+ )
384
+ })
385
+ .collect()
386
+ }
387
+
388
+ /// The secret key backing this endpoint's identity.
389
+ #[napi]
390
+ pub fn secret_key(&self) -> SecretKey {
391
+ self.inner.secret_key().clone().into()
392
+ }
393
+
394
+ /// Replace the set of advertised ALPNs.
395
+ #[napi]
396
+ pub fn set_alpns(&self, alpns: Vec<Vec<u8>>) {
397
+ self.inner.set_alpns(alpns);
398
+ }
399
+
400
+ /// Add an external (manually-known) socket address.
401
+ #[napi]
402
+ pub async fn add_external_addr(&self, addr: String) -> Result<()> {
403
+ let socket = std::net::SocketAddr::from_str(&addr).map_err(anyhow::Error::from)?;
404
+ self.inner.add_external_addr(socket).await;
405
+ Ok(())
406
+ }
407
+
408
+ /// Remove a previously-added external address.
409
+ #[napi]
410
+ pub async fn remove_external_addr(&self, addr: String) -> Result<bool> {
411
+ let socket = std::net::SocketAddr::from_str(&addr).map_err(anyhow::Error::from)?;
412
+ Ok(self.inner.remove_external_addr(&socket).await)
413
+ }
414
+
415
+ /// The local socket addresses this endpoint is bound to.
416
+ #[napi]
417
+ pub fn bound_sockets(&self) -> Vec<String> {
418
+ self.inner
419
+ .bound_sockets()
420
+ .into_iter()
421
+ .map(|a| a.to_string())
422
+ .collect()
423
+ }
424
+
425
+ /// Resolves once the endpoint has a usable home relay.
426
+ #[napi]
427
+ pub async fn online(&self) {
428
+ self.inner.online().await;
429
+ }
430
+
431
+ /// Insert (or replace) a relay configuration at runtime.
432
+ #[napi]
433
+ pub async fn insert_relay(&self, config: crate::RelayConfig) -> Result<()> {
434
+ let config: iroh::RelayConfig = config.try_into()?;
435
+ let url = config.url.clone();
436
+ self.inner.insert_relay(url, Arc::new(config)).await;
437
+ Ok(())
438
+ }
439
+
440
+ /// Remove a relay configuration at runtime.
441
+ #[napi]
442
+ pub async fn remove_relay(&self, url: String) -> Result<bool> {
443
+ let url = iroh::RelayUrl::from_str(&url).map_err(anyhow::Error::from)?;
444
+ Ok(self.inner.remove_relay(&url).await.is_some())
445
+ }
446
+
447
+ /// Connect to a remote endpoint via the given ALPN.
448
+ #[napi]
449
+ pub async fn connect(&self, addr: &EndpointAddr, alpn: Vec<u8>) -> Result<Connection> {
450
+ let addr: iroh::EndpointAddr = addr.try_into()?;
451
+ let conn = self
452
+ .inner
453
+ .connect(addr, &alpn)
454
+ .await
455
+ .map_err(|e| anyhow::anyhow!("{e:?}"))?;
456
+ Ok(Connection(Arc::new(conn)))
457
+ }
458
+
459
+ /// Begin a connection attempt, returning the in-progress handle.
460
+ #[napi]
461
+ pub async fn connect_pending(&self, addr: &EndpointAddr, alpn: Vec<u8>) -> Result<Connecting> {
462
+ let addr: iroh::EndpointAddr = addr.try_into()?;
463
+ let connecting = self
464
+ .inner
465
+ .connect_with_opts(addr, &alpn, iroh::endpoint::ConnectOptions::default())
466
+ .await
467
+ .map_err(|e| anyhow::anyhow!("{e:?}"))?;
468
+ Ok(Connecting(Mutex::new(Some(connecting))))
469
+ }
470
+
471
+ /// Pull the next incoming connection attempt.
472
+ #[napi]
473
+ pub async fn accept_next(&self) -> Option<Incoming> {
474
+ let inner = self.inner.accept().await?;
475
+ Some(Incoming(Mutex::new(Some(inner))))
476
+ }
477
+
478
+ /// Watch for changes to this endpoint's address.
479
+ #[napi(ts_args_type = "callback: (addr: EndpointAddr) => void")]
480
+ pub fn watch_addr(&self, callback: ThreadsafeFunction<EndpointAddr>) -> WatchHandle {
481
+ watch::spawn_watch_addr(self.inner.clone(), callback)
482
+ }
483
+
484
+ /// Watch for changes to the connected relays.
485
+ #[napi(ts_args_type = "callback: (relayUrls: Array<string>) => void")]
486
+ pub fn watch_home_relay(&self, callback: ThreadsafeFunction<Vec<String>>) -> WatchHandle {
487
+ watch::spawn_home_relay_watch(self.inner.clone(), callback)
488
+ }
489
+
490
+ /// Watch for network-stack changes.
491
+ #[napi(ts_args_type = "callback: () => void")]
492
+ pub fn watch_network_change(&self, callback: ThreadsafeFunction<()>) -> WatchHandle {
493
+ watch::spawn_network_change_watch(self.inner.clone(), callback)
494
+ }
495
+
496
+ /// Close the endpoint.
497
+ #[napi]
498
+ pub async fn close(&self) {
499
+ self.inner.close().await;
500
+ }
501
+
502
+ /// True if the endpoint is closed.
503
+ #[napi]
504
+ pub fn is_closed(&self) -> bool {
505
+ self.inner.is_closed()
506
+ }
507
+ }
508
+
509
+ /// An incoming connection that has not yet begun its server-side handshake.
510
+ #[napi]
511
+ pub struct Incoming(Mutex<Option<iroh::endpoint::Incoming>>);
512
+
513
+ #[napi]
514
+ impl Incoming {
515
+ #[napi]
516
+ pub async fn accept(&self) -> Result<Accepting> {
517
+ let inner = self
518
+ .0
519
+ .lock()
520
+ .await
521
+ .take()
522
+ .ok_or_else(|| anyhow::anyhow!("Incoming already consumed"))?;
523
+ let accepting = inner.accept().map_err(|e| anyhow::anyhow!("{e:?}"))?;
524
+ Ok(Accepting(Mutex::new(Some(accepting))))
525
+ }
526
+
527
+ #[napi]
528
+ pub async fn refuse(&self) -> Result<()> {
529
+ let inner = self
530
+ .0
531
+ .lock()
532
+ .await
533
+ .take()
534
+ .ok_or_else(|| anyhow::anyhow!("Incoming already consumed"))?;
535
+ inner.refuse();
536
+ Ok(())
537
+ }
538
+
539
+ #[napi]
540
+ pub async fn retry(&self) -> Result<()> {
541
+ let inner = self
542
+ .0
543
+ .lock()
544
+ .await
545
+ .take()
546
+ .ok_or_else(|| anyhow::anyhow!("Incoming already consumed"))?;
547
+ inner
548
+ .retry()
549
+ .map_err(|e| anyhow::anyhow!("retry failed: {e:?}").into())
550
+ }
551
+
552
+ #[napi]
553
+ pub async fn ignore(&self) -> Result<()> {
554
+ let inner = self
555
+ .0
556
+ .lock()
557
+ .await
558
+ .take()
559
+ .ok_or_else(|| anyhow::anyhow!("Incoming already consumed"))?;
560
+ inner.ignore();
561
+ Ok(())
562
+ }
563
+
564
+ #[napi]
565
+ pub async fn local_addr(&self) -> Result<IncomingLocalAddr> {
566
+ let guard = self.0.lock().await;
567
+ let inner = guard
568
+ .as_ref()
569
+ .ok_or_else(|| anyhow::anyhow!("Incoming already consumed"))?;
570
+ Ok(inner.local_addr().into())
571
+ }
572
+
573
+ #[napi]
574
+ pub async fn remote_addr(&self) -> Result<IncomingAddr> {
575
+ let guard = self.0.lock().await;
576
+ let inner = guard
577
+ .as_ref()
578
+ .ok_or_else(|| anyhow::anyhow!("Incoming already consumed"))?;
579
+ Ok(inner.remote_addr().into())
580
+ }
581
+
582
+ #[napi]
583
+ pub async fn remote_addr_validated(&self) -> Result<bool> {
584
+ let guard = self.0.lock().await;
585
+ let inner = guard
586
+ .as_ref()
587
+ .ok_or_else(|| anyhow::anyhow!("Incoming already consumed"))?;
588
+ Ok(inner.remote_addr_validated())
589
+ }
590
+ }
591
+
592
+ /// A server-side handshake in progress.
593
+ #[napi]
594
+ pub struct Accepting(Mutex<Option<iroh::endpoint::Accepting>>);
595
+
596
+ #[napi]
597
+ impl Accepting {
598
+ #[napi]
599
+ pub async fn connect(&self) -> Result<Connection> {
600
+ let inner = self
601
+ .0
602
+ .lock()
603
+ .await
604
+ .take()
605
+ .ok_or_else(|| anyhow::anyhow!("Accepting already consumed"))?;
606
+ let conn = inner.await.map_err(|e| anyhow::anyhow!("{e:?}"))?;
607
+ Ok(Connection(Arc::new(conn)))
608
+ }
609
+
610
+ #[napi]
611
+ pub async fn alpn(&self) -> Result<Vec<u8>> {
612
+ let mut guard = self.0.lock().await;
613
+ let inner = guard
614
+ .as_mut()
615
+ .ok_or_else(|| anyhow::anyhow!("Accepting already consumed"))?;
616
+ inner
617
+ .alpn()
618
+ .await
619
+ .map_err(|e| anyhow::anyhow!("{e:?}").into())
620
+ }
621
+ }
622
+
623
+ /// A client-side handshake in progress.
624
+ #[napi]
625
+ pub struct Connecting(Mutex<Option<iroh::endpoint::Connecting>>);
626
+
627
+ #[napi]
628
+ impl Connecting {
629
+ #[napi]
630
+ pub async fn connect(&self) -> Result<Connection> {
631
+ let inner = self
632
+ .0
633
+ .lock()
634
+ .await
635
+ .take()
636
+ .ok_or_else(|| anyhow::anyhow!("Connecting already consumed"))?;
637
+ let conn = inner.await.map_err(|e| anyhow::anyhow!("{e:?}"))?;
638
+ Ok(Connection(Arc::new(conn)))
639
+ }
640
+
641
+ #[napi]
642
+ pub async fn alpn(&self) -> Result<Vec<u8>> {
643
+ let mut guard = self.0.lock().await;
644
+ let inner = guard
645
+ .as_mut()
646
+ .ok_or_else(|| anyhow::anyhow!("Connecting already consumed"))?;
647
+ inner
648
+ .alpn()
649
+ .await
650
+ .map_err(|e| anyhow::anyhow!("{e:?}").into())
651
+ }
652
+
653
+ #[napi]
654
+ pub async fn remote_id(&self) -> Result<crate::EndpointId> {
655
+ let guard = self.0.lock().await;
656
+ let inner = guard
657
+ .as_ref()
658
+ .ok_or_else(|| anyhow::anyhow!("Connecting already consumed"))?;
659
+ Ok(inner.remote_id().into())
660
+ }
661
+ }
662
+
663
+ /// An active QUIC connection.
664
+ #[napi]
665
+ pub struct Connection(Arc<endpoint::Connection>);
666
+
667
+ #[napi]
668
+ impl Connection {
669
+ #[napi]
670
+ pub fn alpn(&self) -> Vec<u8> {
671
+ self.0.alpn().to_vec()
672
+ }
673
+
674
+ #[napi]
675
+ pub fn remote_id(&self) -> crate::EndpointId {
676
+ self.0.remote_id().into()
677
+ }
678
+
679
+ #[napi]
680
+ pub fn side(&self) -> Side {
681
+ self.0.side().into()
682
+ }
683
+
684
+ #[napi]
685
+ pub async fn open_uni(&self) -> Result<SendStream> {
686
+ let s = self
687
+ .0
688
+ .open_uni()
689
+ .await
690
+ .map_err(|e| anyhow::anyhow!("{e:?}"))?;
691
+ Ok(SendStream::new(s))
692
+ }
693
+
694
+ #[napi]
695
+ pub async fn accept_uni(&self) -> Result<RecvStream> {
696
+ let r = self
697
+ .0
698
+ .accept_uni()
699
+ .await
700
+ .map_err(|e| anyhow::anyhow!("{e:?}"))?;
701
+ Ok(RecvStream::new(r))
702
+ }
703
+
704
+ #[napi]
705
+ pub async fn open_bi(&self) -> Result<BiStream> {
706
+ let (s, r) = self
707
+ .0
708
+ .open_bi()
709
+ .await
710
+ .map_err(|e| anyhow::anyhow!("{e:?}"))?;
711
+ Ok(BiStream {
712
+ send: SendStream::new(s),
713
+ recv: RecvStream::new(r),
714
+ })
715
+ }
716
+
717
+ #[napi]
718
+ pub async fn accept_bi(&self) -> Result<BiStream> {
719
+ let (s, r) = self
720
+ .0
721
+ .accept_bi()
722
+ .await
723
+ .map_err(|e| anyhow::anyhow!("{e:?}"))?;
724
+ Ok(BiStream {
725
+ send: SendStream::new(s),
726
+ recv: RecvStream::new(r),
727
+ })
728
+ }
729
+
730
+ #[napi]
731
+ pub async fn read_datagram(&self) -> Result<Vec<u8>> {
732
+ let res = self
733
+ .0
734
+ .read_datagram()
735
+ .await
736
+ .map_err(|e| anyhow::anyhow!("{e:?}"))?;
737
+ Ok(res.to_vec())
738
+ }
739
+
740
+ #[napi]
741
+ pub async fn closed(&self) -> String {
742
+ self.0.closed().await.to_string()
743
+ }
744
+
745
+ #[napi]
746
+ pub fn close_reason(&self) -> Option<String> {
747
+ self.0.close_reason().map(|s| s.to_string())
748
+ }
749
+
750
+ #[napi]
751
+ pub fn close(&self, error_code: BigInt, reason: Vec<u8>) -> Result<()> {
752
+ let code =
753
+ endpoint::VarInt::from_u64(error_code.get_u64().1).map_err(anyhow::Error::from)?;
754
+ self.0.close(code, &reason);
755
+ Ok(())
756
+ }
757
+
758
+ #[napi]
759
+ pub fn send_datagram(&self, data: Vec<u8>) -> Result<()> {
760
+ self.0
761
+ .send_datagram(data.into())
762
+ .map_err(|e| anyhow::anyhow!("{e:?}"))?;
763
+ Ok(())
764
+ }
765
+
766
+ #[napi]
767
+ pub async fn send_datagram_wait(&self, data: Vec<u8>) -> Result<()> {
768
+ self.0
769
+ .send_datagram_wait(data.into())
770
+ .await
771
+ .map_err(|e| anyhow::anyhow!("{e:?}"))?;
772
+ Ok(())
773
+ }
774
+
775
+ #[napi]
776
+ pub fn max_datagram_size(&self) -> Option<i64> {
777
+ self.0.max_datagram_size().map(|s| s as i64)
778
+ }
779
+
780
+ #[napi]
781
+ pub fn datagram_send_buffer_space(&self) -> i64 {
782
+ self.0.datagram_send_buffer_space() as i64
783
+ }
784
+
785
+ #[napi]
786
+ pub fn stable_id(&self) -> u32 {
787
+ self.0.stable_id() as _
788
+ }
789
+
790
+ #[napi]
791
+ pub fn rtt(&self) -> Option<i64> {
792
+ self.0
793
+ .paths()
794
+ .iter()
795
+ .find(|p| p.is_selected())
796
+ .map(|p| p.rtt().as_millis() as i64)
797
+ }
798
+
799
+ #[napi]
800
+ pub fn stats(&self) -> ConnectionStats {
801
+ let s = self.0.stats();
802
+ ConnectionStats {
803
+ udp_tx_datagrams: s.udp_tx.datagrams as i64,
804
+ udp_tx_bytes: s.udp_tx.bytes as i64,
805
+ udp_rx_datagrams: s.udp_rx.datagrams as i64,
806
+ udp_rx_bytes: s.udp_rx.bytes as i64,
807
+ lost_packets: s.lost_packets as i64,
808
+ lost_bytes: s.lost_bytes as i64,
809
+ }
810
+ }
811
+
812
+ #[napi]
813
+ pub fn paths(&self) -> Vec<path::PathSnapshot> {
814
+ path::snapshot_paths(&self.0)
815
+ }
816
+
817
+ #[napi(ts_args_type = "callback: (paths: Array<PathSnapshot>) => void")]
818
+ pub fn watch_paths(
819
+ &self,
820
+ callback: ThreadsafeFunction<Vec<path::PathSnapshot>>,
821
+ ) -> WatchHandle {
822
+ watch::spawn_paths_watch((*self.0).clone(), callback)
823
+ }
824
+
825
+ #[napi(ts_args_type = "callback: (event: PathEvent) => void")]
826
+ pub fn watch_path_events(&self, callback: ThreadsafeFunction<path::PathEvent>) -> WatchHandle {
827
+ watch::spawn_path_events_watch((*self.0).clone(), callback)
828
+ }
829
+
830
+ #[napi]
831
+ pub fn set_max_concurrent_uni_streams(&self, count: BigInt) -> Result<()> {
832
+ let n = endpoint::VarInt::from_u64(count.get_u64().1).map_err(anyhow::Error::from)?;
833
+ self.0.set_max_concurrent_uni_streams(n);
834
+ Ok(())
835
+ }
836
+
837
+ #[napi]
838
+ pub fn set_receive_window(&self, count: BigInt) -> Result<()> {
839
+ let n = endpoint::VarInt::from_u64(count.get_u64().1).map_err(anyhow::Error::from)?;
840
+ self.0.set_receive_window(n);
841
+ Ok(())
842
+ }
843
+
844
+ #[napi]
845
+ pub fn set_max_concurrent_bi_streams(&self, count: BigInt) -> Result<()> {
846
+ let n = endpoint::VarInt::from_u64(count.get_u64().1).map_err(anyhow::Error::from)?;
847
+ self.0.set_max_concurrent_bi_streams(n);
848
+ Ok(())
849
+ }
850
+ }
851
+
852
+ /// A bidirectional QUIC stream pair.
853
+ #[napi]
854
+ pub struct BiStream {
855
+ send: SendStream,
856
+ recv: RecvStream,
857
+ }
858
+
859
+ #[napi]
860
+ impl BiStream {
861
+ #[napi(getter)]
862
+ pub fn send(&self) -> SendStream {
863
+ self.send.clone()
864
+ }
865
+
866
+ #[napi(getter)]
867
+ pub fn recv(&self) -> RecvStream {
868
+ self.recv.clone()
869
+ }
870
+ }
871
+
872
+ /// The outgoing half of a QUIC stream.
873
+ #[derive(Clone)]
874
+ #[napi]
875
+ pub struct SendStream(Arc<Mutex<endpoint::SendStream>>);
876
+
877
+ impl SendStream {
878
+ fn new(s: endpoint::SendStream) -> Self {
879
+ SendStream(Arc::new(Mutex::new(s)))
880
+ }
881
+ }
882
+
883
+ #[napi]
884
+ impl SendStream {
885
+ #[napi]
886
+ pub async fn write(&self, buf: Vec<u8>) -> Result<i64> {
887
+ let mut s = self.0.lock().await;
888
+ let n = s.write(&buf).await.map_err(|e| anyhow::anyhow!("{e:?}"))?;
889
+ Ok(n as i64)
890
+ }
891
+
892
+ #[napi]
893
+ pub async fn write_all(&self, buf: Vec<u8>) -> Result<()> {
894
+ let mut s = self.0.lock().await;
895
+ s.write_all(&buf)
896
+ .await
897
+ .map_err(|e| anyhow::anyhow!("{e:?}"))?;
898
+ Ok(())
899
+ }
900
+
901
+ #[napi]
902
+ pub async fn finish(&self) -> Result<()> {
903
+ let mut s = self.0.lock().await;
904
+ s.finish().map_err(|e| anyhow::anyhow!("{e:?}"))?;
905
+ Ok(())
906
+ }
907
+
908
+ #[napi]
909
+ pub async fn reset(&self, error_code: BigInt) -> Result<()> {
910
+ let code =
911
+ endpoint::VarInt::from_u64(error_code.get_u64().1).map_err(anyhow::Error::from)?;
912
+ let mut s = self.0.lock().await;
913
+ s.reset(code).map_err(|e| anyhow::anyhow!("{e:?}"))?;
914
+ Ok(())
915
+ }
916
+
917
+ #[napi]
918
+ pub async fn set_priority(&self, p: i32) -> Result<()> {
919
+ let s = self.0.lock().await;
920
+ s.set_priority(p).map_err(|e| anyhow::anyhow!("{e:?}"))?;
921
+ Ok(())
922
+ }
923
+
924
+ #[napi]
925
+ pub async fn priority(&self) -> Result<i32> {
926
+ let s = self.0.lock().await;
927
+ s.priority().map_err(|e| anyhow::anyhow!("{e:?}").into())
928
+ }
929
+
930
+ #[napi]
931
+ pub async fn stopped(&self) -> Result<Option<i64>> {
932
+ let s = self.0.lock().await;
933
+ let res = s.stopped().await.map_err(|e| anyhow::anyhow!("{e:?}"))?;
934
+ Ok(res.map(|r| r.into_inner() as i64))
935
+ }
936
+
937
+ #[napi]
938
+ pub async fn id(&self) -> String {
939
+ self.0.lock().await.id().to_string()
940
+ }
941
+ }
942
+
943
+ /// The incoming half of a QUIC stream.
944
+ #[derive(Clone)]
945
+ #[napi]
946
+ pub struct RecvStream(Arc<Mutex<endpoint::RecvStream>>);
947
+
948
+ impl RecvStream {
949
+ fn new(s: endpoint::RecvStream) -> Self {
950
+ RecvStream(Arc::new(Mutex::new(s)))
951
+ }
952
+ }
953
+
954
+ #[napi]
955
+ impl RecvStream {
956
+ #[napi]
957
+ pub async fn read(&self, size_limit: u32) -> Result<Vec<u8>> {
958
+ let mut buf = vec![0u8; size_limit as usize];
959
+ let mut r = self.0.lock().await;
960
+ let res = r
961
+ .read(&mut buf)
962
+ .await
963
+ .map_err(|e| anyhow::anyhow!("{e:?}"))?;
964
+ let len = res.unwrap_or(0);
965
+ buf.truncate(len);
966
+ Ok(buf)
967
+ }
968
+
969
+ #[napi]
970
+ pub async fn read_exact(&self, size: u32) -> Result<Vec<u8>> {
971
+ let mut buf = vec![0u8; size as usize];
972
+ let mut r = self.0.lock().await;
973
+ r.read_exact(&mut buf)
974
+ .await
975
+ .map_err(|e| anyhow::anyhow!("{e:?}"))?;
976
+ Ok(buf)
977
+ }
978
+
979
+ #[napi]
980
+ pub async fn read_to_end(&self, size_limit: u32) -> Result<Vec<u8>> {
981
+ let mut r = self.0.lock().await;
982
+ let res = r
983
+ .read_to_end(size_limit as usize)
984
+ .await
985
+ .map_err(|e| anyhow::anyhow!("{e:?}"))?;
986
+ Ok(res)
987
+ }
988
+
989
+ #[napi]
990
+ pub async fn id(&self) -> String {
991
+ self.0.lock().await.id().to_string()
992
+ }
993
+
994
+ #[napi]
995
+ pub async fn bytes_read(&self) -> Result<i64> {
996
+ let r = self.0.lock().await;
997
+ Ok(r.bytes_read().map_err(|e| anyhow::anyhow!("{e:?}"))? as i64)
998
+ }
999
+
1000
+ #[napi]
1001
+ pub async fn stop(&self, error_code: BigInt) -> Result<()> {
1002
+ let code =
1003
+ endpoint::VarInt::from_u64(error_code.get_u64().1).map_err(anyhow::Error::from)?;
1004
+ let mut r = self.0.lock().await;
1005
+ r.stop(code).map_err(|e| anyhow::anyhow!("{e:?}"))?;
1006
+ Ok(())
1007
+ }
1008
+
1009
+ #[napi]
1010
+ pub async fn received_reset(&self) -> Result<Option<i64>> {
1011
+ let mut r = self.0.lock().await;
1012
+ let code = r
1013
+ .received_reset()
1014
+ .await
1015
+ .map_err(|e| anyhow::anyhow!("{e:?}"))?;
1016
+ Ok(code.map(|c| c.into_inner() as i64))
1017
+ }
1018
+ }