Skip to content

Commit 0776687

Browse files
authored
refactor(iroh,iroh-relay): Remove legacy relay path, make websocket connections default (#3384)
## Description Split out from #3331. Switch to websockets and remove the old code path. Also: - I've removed `governor` for rate limiting - We used to guess the amount of bytes a frame was encoded as after we've decoded it, because that's how governor works (you do it on `Stream`s, not the underlying `AsyncRead`). - Instead we have our own couple of lines of rate-limiting logic (with tests) on the `AsyncRead` level. - The reason is we can't guess the size of websocket frames as easily anymore. ## Breaking Changes In iroh: - Removed `iroh::endpoint::Builder::relay_conn_protocol`. It's now always websockets - Removed the `iroh::RelayProtocol` re-export (the type was removed in `iroh-relay`) In iroh-relay: - Removed `ClientBuilder::protocol` - Removed `http::Protocol` type - Renamed `frames_rx_ratelimited_total` metric to `bytes_rx_ratelimited_total`, which now tracks bytes not frames - Removed `relay_accepts` and `websocket_accepts` metrics ## Change checklist <!-- Remove any that are not relevant. --> - [x] Self-review.
1 parent 9c023bf commit 0776687

File tree

18 files changed

+768
-1429
lines changed

18 files changed

+768
-1429
lines changed

Cargo.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

iroh-relay/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ rustls-cert-file-reader = { version = "0.4.1", optional = true }
8989
rustls-pemfile = { version = "2.1", optional = true }
9090
time = { version = "0.3.37", optional = true }
9191
tokio-rustls-acme = { version = "0.7.1", optional = true }
92-
tokio-websockets = { version = "0.11.3", features = ["rustls-bring-your-own-connector", "ring", "getrandom", "rand", "server"], optional = true } # server-side websocket implementation
92+
tokio-websockets = { version = "0.12", features = ["rustls-bring-your-own-connector", "ring", "getrandom", "rand", "server"], optional = true } # server-side websocket implementation
9393
simdutf8 = { version = "0.1.5", optional = true } # minimal version fix
9494
toml = { version = "0.8", optional = true }
9595
tracing-subscriber = { version = "0.3", features = [
@@ -110,7 +110,7 @@ tokio = { version = "1", features = [
110110
"signal",
111111
"process",
112112
] }
113-
tokio-websockets = { version = "0.11.3", features = ["rustls-bring-your-own-connector", "ring", "getrandom", "rand", "client"] }
113+
tokio-websockets = { version = "0.12", features = ["rustls-bring-your-own-connector", "ring", "getrandom", "rand", "client"] }
114114

115115
# wasm-in-browser dependencies
116116
[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies]

iroh-relay/src/client.rs

Lines changed: 93 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,7 @@ use url::Url;
2525
pub use self::conn::{ReceivedMessage, RecvError, SendError, SendMessage};
2626
#[cfg(not(wasm_browser))]
2727
use crate::dns::{DnsError, DnsResolver};
28-
use crate::{
29-
http::{Protocol, RELAY_PATH},
30-
protos::relay::SendError as SendRelayError,
31-
KeyCache,
32-
};
28+
use crate::{http::RELAY_PATH, protos::relay::SendError as SendRelayError, KeyCache};
3329

3430
pub(crate) mod conn;
3531
#[cfg(not(wasm_browser))]
@@ -125,8 +121,6 @@ pub struct ClientBuilder {
125121
is_prober: bool,
126122
/// Server url.
127123
url: RelayUrl,
128-
/// Relay protocol
129-
protocol: Protocol,
130124
/// Allow self-signed certificates from relay servers
131125
#[cfg(any(test, feature = "test-utils"))]
132126
insecure_skip_cert_verify: bool,
@@ -153,9 +147,6 @@ impl ClientBuilder {
153147
is_prober: false,
154148
url: url.into(),
155149

156-
// Resolves to websockets in browsers and relay otherwise
157-
protocol: Protocol::default(),
158-
159150
#[cfg(any(test, feature = "test-utils"))]
160151
insecure_skip_cert_verify: false,
161152

@@ -167,13 +158,6 @@ impl ClientBuilder {
167158
}
168159
}
169160

170-
/// Sets whether to connect to the relay via websockets or not.
171-
/// Set to use non-websocket, normal relaying by default.
172-
pub fn protocol(mut self, protocol: Protocol) -> Self {
173-
self.protocol = protocol;
174-
self
175-
}
176-
177161
/// Returns if we should prefer ipv6
178162
/// it replaces the relayhttp.AddressFamilySelector we pass
179163
/// It provides the hint as to whether in an IPv4-vs-IPv6 race that
@@ -216,41 +200,97 @@ impl ClientBuilder {
216200
}
217201

218202
/// Establishes a new connection to the relay server.
203+
#[cfg(not(wasm_browser))]
219204
pub async fn connect(&self) -> Result<Client, ConnectError> {
220-
let (conn, local_addr) = match self.protocol {
221-
#[cfg(wasm_browser)]
222-
Protocol::Websocket => {
223-
let conn = self.connect_ws().await?;
224-
let local_addr = None;
225-
(conn, local_addr)
226-
}
227-
#[cfg(not(wasm_browser))]
228-
Protocol::Websocket => {
229-
let (conn, local_addr) = self.connect_ws().await?;
230-
(conn, Some(local_addr))
231-
}
232-
#[cfg(not(wasm_browser))]
233-
Protocol::Relay => {
234-
let (conn, local_addr) = self.connect_relay().await?;
235-
(conn, Some(local_addr))
205+
use tls::MaybeTlsStreamBuilder;
206+
207+
use crate::protos::relay::MAX_FRAME_SIZE;
208+
209+
let mut dial_url = (*self.url).clone();
210+
dial_url.set_path(RELAY_PATH);
211+
// The relay URL is exchanged with the http(s) scheme in tickets and similar.
212+
// We need to use the ws:// or wss:// schemes when connecting with websockets, though.
213+
dial_url
214+
.set_scheme(match self.url.scheme() {
215+
"http" => "ws",
216+
"ws" => "ws",
217+
_ => "wss",
218+
})
219+
.map_err(|_| {
220+
InvalidWebsocketUrlSnafu {
221+
url: dial_url.clone(),
222+
}
223+
.build()
224+
})?;
225+
226+
debug!(%dial_url, "Dialing relay by websocket");
227+
228+
#[allow(unused_mut)]
229+
let mut builder = MaybeTlsStreamBuilder::new(dial_url.clone(), self.dns_resolver.clone())
230+
.prefer_ipv6(self.prefer_ipv6())
231+
.proxy_url(self.proxy_url.clone());
232+
233+
#[cfg(any(test, feature = "test-utils"))]
234+
if self.insecure_skip_cert_verify {
235+
builder = builder.insecure_skip_cert_verify(self.insecure_skip_cert_verify);
236+
}
237+
238+
let stream = builder.connect().await?;
239+
let local_addr = stream
240+
.as_ref()
241+
.local_addr()
242+
.map_err(|_| NoLocalAddrSnafu.build())?;
243+
let (conn, response) = tokio_websockets::ClientBuilder::new()
244+
.uri(dial_url.as_str())
245+
.map_err(|_| {
246+
InvalidRelayUrlSnafu {
247+
url: dial_url.clone(),
248+
}
249+
.build()
250+
})?
251+
.limits(tokio_websockets::Limits::default().max_payload_len(Some(MAX_FRAME_SIZE)))
252+
.connect_on(stream)
253+
.await?;
254+
255+
if response.status() != hyper::StatusCode::SWITCHING_PROTOCOLS {
256+
UnexpectedUpgradeStatusSnafu {
257+
code: response.status(),
236258
}
237-
#[cfg(wasm_browser)]
238-
Protocol::Relay => return Err(RelayProtoNotAvailableSnafu.build()),
239-
};
259+
.fail()?;
260+
}
261+
262+
let conn = Conn::new(conn, self.key_cache.clone(), &self.secret_key).await?;
240263

241264
event!(
242265
target: "events.net.relay.connected",
243266
Level::DEBUG,
244267
url = %self.url,
245-
protocol = ?self.protocol,
246268
);
247269

248270
trace!("connect done");
249-
Ok(Client { conn, local_addr })
271+
272+
Ok(Client {
273+
conn,
274+
local_addr: Some(local_addr),
275+
})
276+
}
277+
278+
/// Reports whether IPv4 dials should be slightly
279+
/// delayed to give IPv6 a better chance of winning dial races.
280+
/// Implementations should only return true if IPv6 is expected
281+
/// to succeed. (otherwise delaying IPv4 will delay the connection
282+
/// overall)
283+
#[cfg(not(wasm_browser))]
284+
fn prefer_ipv6(&self) -> bool {
285+
match self.address_family_selector {
286+
Some(ref selector) => selector(),
287+
None => false,
288+
}
250289
}
251290

291+
/// Establishes a new connection to the relay server.
252292
#[cfg(wasm_browser)]
253-
async fn connect_ws(&self) -> Result<Conn, ConnectError> {
293+
pub async fn connect(&self) -> Result<Client, ConnectError> {
254294
let mut dial_url = (*self.url).clone();
255295
dial_url.set_path(RELAY_PATH);
256296
// The relay URL is exchanged with the http(s) scheme in tickets and similar.
@@ -271,9 +311,20 @@ impl ClientBuilder {
271311
debug!(%dial_url, "Dialing relay by websocket");
272312

273313
let (_, ws_stream) = ws_stream_wasm::WsMeta::connect(dial_url.as_str(), None).await?;
274-
let conn =
275-
Conn::new_ws_browser(ws_stream, self.key_cache.clone(), &self.secret_key).await?;
276-
Ok(conn)
314+
let conn = Conn::new(ws_stream, self.key_cache.clone(), &self.secret_key).await?;
315+
316+
event!(
317+
target: "events.net.relay.connected",
318+
Level::DEBUG,
319+
url = %self.url,
320+
);
321+
322+
trace!("connect done");
323+
324+
Ok(Client {
325+
conn,
326+
local_addr: None,
327+
})
277328
}
278329
}
279330

0 commit comments

Comments
 (0)