Skip to content

Commit 2c63c49

Browse files
kixelatedclaude
andauthored
Fix iroh test and add noq backend tests (#1093)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 2723bd2 commit 2c63c49

3 files changed

Lines changed: 50 additions & 6 deletions

File tree

rs/moq-native/src/client.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ pub struct Client {
129129
quiche: Option<crate::quiche::QuicheClient>,
130130
#[cfg(feature = "iroh")]
131131
iroh: Option<web_transport_iroh::iroh::Endpoint>,
132+
#[cfg(feature = "iroh")]
133+
iroh_addrs: Vec<std::net::SocketAddr>,
132134
}
133135

134136
impl Client {
@@ -236,6 +238,8 @@ impl Client {
236238
quiche,
237239
#[cfg(feature = "iroh")]
238240
iroh: None,
241+
#[cfg(feature = "iroh")]
242+
iroh_addrs: Vec::new(),
239243
})
240244
}
241245

@@ -245,6 +249,16 @@ impl Client {
245249
self
246250
}
247251

252+
/// Set direct IP addresses for connecting to iroh peers.
253+
///
254+
/// This is useful when the peer's IP addresses are known ahead of time,
255+
/// bypassing the need for peer discovery (e.g. in tests or local networks).
256+
#[cfg(feature = "iroh")]
257+
pub fn with_iroh_addrs(mut self, addrs: Vec<std::net::SocketAddr>) -> Self {
258+
self.iroh_addrs = addrs;
259+
self
260+
}
261+
248262
pub fn with_publish(mut self, publish: impl Into<Option<moq_lite::OriginConsumer>>) -> Self {
249263
self.moq = self.moq.with_publish(publish);
250264
self
@@ -265,7 +279,7 @@ impl Client {
265279
#[cfg(feature = "iroh")]
266280
if url.scheme() == "iroh" {
267281
let endpoint = self.iroh.as_ref().context("Iroh support is not enabled")?;
268-
let session = crate::iroh::connect(endpoint, url).await?;
282+
let session = crate::iroh::connect(endpoint, url, self.iroh_addrs.iter().copied()).await?;
269283
let session = self.moq.connect(session).await?;
270284
return Ok(session);
271285
}

rs/moq-native/src/iroh.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,20 @@ impl IrohRequest {
152152
}
153153
}
154154

155-
pub(crate) async fn connect(endpoint: &IrohEndpoint, url: Url) -> anyhow::Result<web_transport_iroh::Session> {
155+
pub(crate) async fn connect(
156+
endpoint: &IrohEndpoint,
157+
url: Url,
158+
addrs: impl IntoIterator<Item = std::net::SocketAddr>,
159+
) -> anyhow::Result<web_transport_iroh::Session> {
156160
let host = url.host().context("Invalid URL: missing host")?.to_string();
157161
let endpoint_id: iroh::EndpointId = host.parse().context("Invalid URL: host is not an iroh endpoint id")?;
158162

163+
// Build an EndpointAddr with any direct IP addresses provided.
164+
let mut endpoint_addr = iroh::EndpointAddr::new(endpoint_id);
165+
for addr in addrs {
166+
endpoint_addr = endpoint_addr.with_ip_addr(addr);
167+
}
168+
159169
// We need to use this API to provide multiple ALPNs.
160170
// H3 is last because it requires WebTransport framing which not all H3 endpoints support.
161171
let alpn = moq_lite::ALPNS[0].as_bytes();
@@ -166,7 +176,7 @@ pub(crate) async fn connect(endpoint: &IrohEndpoint, url: Url) -> anyhow::Result
166176
additional.push(b"h3".to_vec());
167177
let opts = iroh::endpoint::ConnectOptions::new().with_additional_alpns(additional);
168178

169-
let mut connecting = endpoint.connect_with_opts(endpoint_id, alpn, opts).await?;
179+
let mut connecting = endpoint.connect_with_opts(endpoint_addr, alpn, opts).await?;
170180
let alpn = connecting.alpn().await?;
171181
let alpn = String::from_utf8(alpn).context("failed to decode ALPN")?;
172182

rs/moq-native/tests/backend.rs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const TIMEOUT: Duration = Duration::from_secs(10);
1111

1212
/// Publish a broadcast on the server, subscribe on the client, and verify
1313
/// the data arrives correctly using the specified QUIC backend and URL scheme.
14-
#[cfg(any(feature = "quinn", feature = "quiche"))]
14+
#[cfg(any(feature = "quinn", feature = "quiche", feature = "noq"))]
1515
async fn backend_test(scheme: &str, backend: moq_native::QuicBackend) {
1616
// ── publisher (server) ──────────────────────────────────────────
1717
let pub_origin = Origin::produce();
@@ -132,7 +132,6 @@ async fn quiche_webtransport() {
132132
#[cfg(feature = "iroh")]
133133
#[tracing_test::traced_test]
134134
#[tokio::test]
135-
#[ignore = "iroh peer discovery requires network; needs direct addressing support in moq-native"]
136135
async fn iroh_connect() {
137136
use moq_native::IrohEndpointConfig;
138137

@@ -156,6 +155,10 @@ async fn iroh_connect() {
156155
.expect("failed to bind server iroh endpoint")
157156
.expect("server iroh endpoint not enabled");
158157

158+
// Get the server's direct addresses before moving it into the server.
159+
let server_addr = server_endpoint.addr();
160+
let server_addrs: Vec<std::net::SocketAddr> = server_addr.ip_addrs().copied().collect();
161+
159162
let server_endpoint_id = server_endpoint.id();
160163

161164
// Server still needs a QUIC bind for init, but we'll connect via iroh
@@ -187,7 +190,8 @@ async fn iroh_connect() {
187190
let client = client_config
188191
.init()
189192
.expect("failed to init client")
190-
.with_iroh(Some(client_endpoint));
193+
.with_iroh(Some(client_endpoint))
194+
.with_iroh_addrs(server_addrs);
191195

192196
let url: url::Url = format!("iroh://{server_endpoint_id}").parse().unwrap();
193197

@@ -241,3 +245,19 @@ async fn iroh_connect() {
241245
.expect("server task panicked")
242246
.expect("server task failed");
243247
}
248+
249+
// ── Noq backend ─────────────────────────────────────────────────────
250+
251+
#[cfg(feature = "noq")]
252+
#[tracing_test::traced_test]
253+
#[tokio::test]
254+
async fn noq_raw_quic() {
255+
backend_test("moqt", moq_native::QuicBackend::Noq).await;
256+
}
257+
258+
#[cfg(feature = "noq")]
259+
#[tracing_test::traced_test]
260+
#[tokio::test]
261+
async fn noq_webtransport() {
262+
backend_test("https", moq_native::QuicBackend::Noq).await;
263+
}

0 commit comments

Comments
 (0)