Skip to content

Commit

Permalink
feat(iroh-net)!: Make relay protocol configurable on ClientBuilder
Browse files Browse the repository at this point in the history
…instead of defined by the relay url scheme (#2446)

## Description

Details in #2442 

## Breaking Changes

- `iroh_net::relay::http::ClientBuilder`: Added `.protocol()` function
to the builder for choosing whether to connect to the relay via
websockets or not

## Change checklist

- [X] Self-review.
- [X] Documentation updates if relevant.
- [X] Tests if relevant.
- [X] All breaking changes documented.
  • Loading branch information
matheus23 authored Jul 3, 2024
1 parent b5dc795 commit ab2c7ea
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 35 deletions.
22 changes: 18 additions & 4 deletions iroh-net/src/relay/http/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ struct Actor {
address_family_selector: Option<Box<dyn Fn() -> BoxFuture<bool> + Send + Sync + 'static>>,
conn_gen: usize,
url: RelayUrl,
protocol: Protocol,
#[debug("TlsConnector")]
tls_connector: tokio_rustls::TlsConnector,
pings: PingTracker,
Expand Down Expand Up @@ -215,6 +216,8 @@ pub struct ClientBuilder {
server_public_key: Option<PublicKey>,
/// Server url.
url: RelayUrl,
/// Relay protocol
protocol: Protocol,
/// Allow self-signed certificates from relay servers
#[cfg(any(test, feature = "test-utils"))]
insecure_skip_cert_verify: bool,
Expand Down Expand Up @@ -242,6 +245,7 @@ impl ClientBuilder {
is_prober: false,
server_public_key: None,
url: url.into(),
protocol: Protocol::Relay,
#[cfg(any(test, feature = "test-utils"))]
insecure_skip_cert_verify: false,
proxy_url: None,
Expand All @@ -254,6 +258,13 @@ impl ClientBuilder {
self
}

/// Sets whether to connect to the relay via websockets or not.
/// Set to use non-websocket, normal relaying by default.
pub fn protocol(mut self, protocol: Protocol) -> Self {
self.protocol = protocol;
self
}

/// Returns if we should prefer ipv6
/// it replaces the relayhttp.AddressFamilySelector we pass
/// It provides the hint as to whether in an IPv4-vs-IPv6 race that
Expand Down Expand Up @@ -341,6 +352,7 @@ impl ClientBuilder {
pings: PingTracker::default(),
ping_tasks: Default::default(),
url: self.url,
protocol: self.protocol,
tls_connector,
dns_resolver,
proxy_url: self.proxy_url,
Expand Down Expand Up @@ -588,10 +600,7 @@ impl Actor {
}

async fn connect_0(&self) -> Result<(RelayClient, RelayClientReceiver), ClientError> {
// We determine which protocol to use for relays via the URL scheme: ws(s) vs. http(s)
let protocol = Protocol::from_url_scheme(&self.url);

let (reader, writer, local_addr) = match &protocol {
let (reader, writer, local_addr) = match self.protocol {
Protocol::Websocket => {
let (reader, writer) = self.connect_ws().await?;
let local_addr = None;
Expand Down Expand Up @@ -621,6 +630,11 @@ impl Actor {
async fn connect_ws(&self) -> Result<(ConnReader, ConnWriter), ClientError> {
let mut dial_url = (*self.url).clone();
dial_url.set_path("/derp");
// The relay URL is exchanged with the http(s) scheme in tickets and similar.
// We need to use the ws:// or wss:// schemes when connecting with websockets, though.
dial_url
.set_scheme(if self.use_tls() { "wss" } else { "ws" })
.map_err(|()| ClientError::InvalidUrl(self.url.to_string()))?;

debug!(%dial_url, "Dialing relay by websocket");

Expand Down
15 changes: 0 additions & 15 deletions iroh-net/src/relay/http/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ use hyper::header::{HeaderValue, UPGRADE};
use hyper::service::Service;
use hyper::upgrade::Upgraded;
use hyper::{HeaderMap, Method, Request, Response, StatusCode};
use iroh_base::node_addr::RelayUrl;
use tokio::net::{TcpListener, TcpStream};
use tokio::task::JoinHandle;
use tokio_rustls_acme::AcmeAcceptor;
Expand Down Expand Up @@ -81,20 +80,6 @@ impl Protocol {
}
}

/// Determines which protocol to use depending on a URL.
///
/// `ws(s)` parses as websockets, `http(s)` parses to the custom relay protocol.
pub fn from_url_scheme(url: &RelayUrl) -> Self {
match url.scheme() {
"ws" => Protocol::Websocket,
"wss" => Protocol::Websocket,
"http" => Protocol::Relay,
"https" => Protocol::Relay,
// We default to relay in case of weird URLs.
_ => Protocol::Relay,
}
}

/// Tries to match the value of an HTTP upgrade header to figure out which protocol should be initiated.
pub fn parse_header(header: &HeaderValue) -> Option<Self> {
let header_bytes = header.as_bytes();
Expand Down
31 changes: 15 additions & 16 deletions iroh-net/src/relay/iroh_relay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -712,7 +712,7 @@ mod tests {
use http::header::UPGRADE;
use iroh_base::node_addr::RelayUrl;

use crate::relay::http::{ClientBuilder, HTTP_UPGRADE_PROTOCOL};
use crate::relay::http::{ClientBuilder, Protocol, HTTP_UPGRADE_PROTOCOL};

use self::relay::ReceivedMessage;

Expand Down Expand Up @@ -887,16 +887,17 @@ mod tests {
async fn test_relay_clients_both_websockets() {
let _guard = iroh_test::logging::setup();
let server = spawn_local_relay().await.unwrap();
// NOTE: Using `ws://` URL scheme to trigger websockets.
let relay_url = format!("ws://{}", server.http_addr().unwrap());

let relay_url = format!("http://{}", server.http_addr().unwrap());
let relay_url: RelayUrl = relay_url.parse().unwrap();

// set up client a
let a_secret_key = SecretKey::generate();
let a_key = a_secret_key.public();
let resolver = crate::dns::default_resolver().clone();
let (client_a, mut client_a_receiver) =
ClientBuilder::new(relay_url.clone()).build(a_secret_key, resolver);
let (client_a, mut client_a_receiver) = ClientBuilder::new(relay_url.clone())
.protocol(Protocol::Websocket)
.build(a_secret_key, resolver);
let connect_client = client_a.clone();

// give the relay server some time to accept connections
Expand All @@ -920,8 +921,9 @@ mod tests {
let b_secret_key = SecretKey::generate();
let b_key = b_secret_key.public();
let resolver = crate::dns::default_resolver().clone();
let (client_b, mut client_b_receiver) =
ClientBuilder::new(relay_url.clone()).build(b_secret_key, resolver);
let (client_b, mut client_b_receiver) = ClientBuilder::new(relay_url.clone())
.protocol(Protocol::Websocket) // another websocket client
.build(b_secret_key, resolver);
client_b.connect().await.unwrap();

// send message from a to b
Expand Down Expand Up @@ -954,19 +956,15 @@ mod tests {
let _guard = iroh_test::logging::setup();
let server = spawn_local_relay().await.unwrap();

let derp_relay_url = format!("http://{}", server.http_addr().unwrap());
let derp_relay_url: RelayUrl = derp_relay_url.parse().unwrap();

// NOTE: Using `ws://` URL scheme to trigger websockets.
let ws_relay_url = format!("ws://{}", server.http_addr().unwrap());
let ws_relay_url: RelayUrl = ws_relay_url.parse().unwrap();
let relay_url = format!("http://{}", server.http_addr().unwrap());
let relay_url: RelayUrl = relay_url.parse().unwrap();

// set up client a
let a_secret_key = SecretKey::generate();
let a_key = a_secret_key.public();
let resolver = crate::dns::default_resolver().clone();
let (client_a, mut client_a_receiver) =
ClientBuilder::new(derp_relay_url.clone()).build(a_secret_key, resolver);
ClientBuilder::new(relay_url.clone()).build(a_secret_key, resolver);
let connect_client = client_a.clone();

// give the relay server some time to accept connections
Expand All @@ -990,8 +988,9 @@ mod tests {
let b_secret_key = SecretKey::generate();
let b_key = b_secret_key.public();
let resolver = crate::dns::default_resolver().clone();
let (client_b, mut client_b_receiver) =
ClientBuilder::new(ws_relay_url.clone()).build(b_secret_key, resolver);
let (client_b, mut client_b_receiver) = ClientBuilder::new(relay_url.clone())
.protocol(Protocol::Websocket) // Use websockets
.build(b_secret_key, resolver);
client_b.connect().await.unwrap();

// send message from a to b
Expand Down

0 comments on commit ab2c7ea

Please sign in to comment.