Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(iroh-relay)!: Remove relay_endpoint config option & rename /derp route to /relay #2419

Merged
merged 8 commits into from
Jul 2, 2024
12 changes: 12 additions & 0 deletions iroh-net/src/relay/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@ pub(crate) const HTTP_UPGRADE_PROTOCOL: &str = "iroh derp http";
pub(crate) const WEBSOCKET_UPGRADE_PROTOCOL: &str = "websocket";
pub(crate) const SUPPORTED_WEBSOCKET_VERSION: &str = "13";

/// The HTTP path under which the relay accepts relaying connections
/// (over websockets and a custom upgrade protocol).
pub const RELAY_PATH: &str = "/relay";
/// The HTTP path under which the relay allows doing latency queries for testing.
pub const RELAY_PROBE_PATH: &str = "/relay/probe";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is just bikesheeding, but nesting /relay/probe is a bit odd, given the other handlers are all on the root (I appreciate this was copied from tailscale). I'd move this to /probe so it is on the same level as /generate_204 etc. We can definitely do this for the new route. We can even remove /derp/probe if we want as it's not yet used (hi https-probe that still lives in a half-dead branch 😓 ).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll keep it in this PR as a refactor instead of a change - let's talk about moving it when we're making use of it :)

/// The legacy HTTP path under which the relay used to accept relaying connections.
/// We keep this for backwards compatibility.
pub(crate) const LEGACY_RELAY_PATH: &str = "/derp";
/// The legacy HTTP path under which the relay used to allow latency queries.
/// We keep this for backwards compatibility.
pub(crate) const LEGACY_RELAY_PROBE_PATH: &str = "/derp/probe";

#[cfg(test)]
mod tests {
use super::*;
Expand Down
30 changes: 8 additions & 22 deletions iroh-net/src/relay/http/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ use crate::relay::http::{
use crate::relay::server::{ClientConnHandler, MaybeTlsStream};
use crate::relay::MaybeTlsStreamServer;

use super::{LEGACY_RELAY_PATH, RELAY_PATH};

type BytesBody = http_body_util::Full<hyper::body::Bytes>;
type HyperError = Box<dyn std::error::Error + Send + Sync>;
type HyperResult<T> = std::result::Result<T, HyperError>;
Expand Down Expand Up @@ -226,8 +228,6 @@ pub struct ServerBuilder {
/// Used when certain routes in your server should be made available at the same port as
/// the relay server, and so must be handled along side requests to the relay endpoint.
handlers: Handlers,
/// Defaults to `GET` request at "/derp".
relay_endpoint: &'static str,
/// Use a custom relay response handler.
///
/// Typically used when you want to disable any relay connections.
Expand All @@ -250,7 +250,6 @@ impl ServerBuilder {
addr,
tls_config: None,
handlers: Default::default(),
relay_endpoint: "/derp",
relay_override: None,
headers: HeaderMap::new(),
not_found_fn: None,
Expand Down Expand Up @@ -296,14 +295,6 @@ impl ServerBuilder {
self
}

/// Sets a custom endpoint for the relay handler.
///
/// The default is `/derp`.
pub fn relay_endpoint(mut self, endpoint: &'static str) -> Self {
self.relay_endpoint = endpoint;
self
}

/// Adds HTTP headers to responses.
pub fn headers(mut self, headers: HeaderMap) -> Self {
for (k, v) in headers.iter() {
Expand Down Expand Up @@ -347,13 +338,7 @@ impl ServerBuilder {
}),
};

let service = RelayService::new(
self.handlers,
relay_handler,
self.relay_endpoint,
not_found_fn,
self.headers,
);
let service = RelayService::new(self.handlers, relay_handler, not_found_fn, self.headers);

let server_state = ServerState {
addr: self.addr,
Expand Down Expand Up @@ -564,7 +549,11 @@ impl Service<Request<Incoming>> for RelayService {

fn call(&self, req: Request<Incoming>) -> Self::Future {
// if the request hits the relay endpoint
if req.method() == hyper::Method::GET && req.uri().path() == self.0.relay_endpoint {
// or /derp for backwards compat
if matches!(
(req.method(), req.uri().path()),
(&hyper::Method::GET, LEGACY_RELAY_PATH | RELAY_PATH)
) {
match &self.0.relay_handler {
RelayHandler::Override(f) => {
// see if we have some override response
Expand Down Expand Up @@ -597,7 +586,6 @@ struct RelayService(Arc<Inner>);
#[derive(derive_more::Debug)]
struct Inner {
pub relay_handler: RelayHandler,
pub relay_endpoint: &'static str,
#[debug("Box<Fn(ResponseBuilder) -> Result<Response<BytesBody>> + Send + Sync + 'static>")]
pub not_found_fn: HyperHandler,
pub handlers: Handlers,
Expand Down Expand Up @@ -645,14 +633,12 @@ impl RelayService {
fn new(
handlers: Handlers,
relay_handler: RelayHandler,
relay_endpoint: &'static str,
not_found_fn: HyperHandler,
headers: HeaderMap,
) -> Self {
Self(Arc::new(Inner {
relay_handler,
handlers,
relay_endpoint,
not_found_fn,
headers,
}))
Expand Down
113 changes: 49 additions & 64 deletions iroh-net/src/relay/iroh_relay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ use tracing::{debug, error, info, info_span, instrument, trace, warn, Instrument

use crate::key::SecretKey;
use crate::relay;
use crate::relay::http::{ServerBuilder as RelayServerBuilder, TlsAcceptor};
use crate::relay::http::{
ServerBuilder as RelayServerBuilder, TlsAcceptor, LEGACY_RELAY_PROBE_PATH, RELAY_PROBE_PATH,
};
use crate::stun;
use crate::util::AbortingJoinHandle;

Expand Down Expand Up @@ -76,7 +78,7 @@ pub struct ServerConfig<EC: fmt::Debug, EA: fmt::Debug = EC> {

/// Configuration for the Relay HTTP and HTTPS server.
///
/// This includes the HTTP services hosted by the Relay server, the Relay `/derp` HTTP
/// This includes the HTTP services hosted by the Relay server, the Relay `/relay` HTTP
/// endpoint is only one of the services served.
#[derive(Debug)]
pub struct RelayConfig<EC: fmt::Debug, EA: fmt::Debug = EC> {
Expand Down Expand Up @@ -235,7 +237,12 @@ impl Server {
.relay_override(Box::new(relay_disabled_handler))
.request_handler(Method::GET, "/", Box::new(root_handler))
.request_handler(Method::GET, "/index.html", Box::new(root_handler))
.request_handler(Method::GET, "/derp/probe", Box::new(probe_handler))
.request_handler(
Method::GET,
LEGACY_RELAY_PROBE_PATH,
Box::new(probe_handler),
) // backwards compat
.request_handler(Method::GET, RELAY_PROBE_PATH, Box::new(probe_handler))
.request_handler(Method::GET, "/robots.txt", Box::new(robots_handler));
let http_addr = match relay_config.tls {
Some(tls_config) => {
Expand Down Expand Up @@ -702,14 +709,29 @@ mod tests {
use std::time::Duration;

use bytes::Bytes;
use http::header::UPGRADE;
use iroh_base::node_addr::RelayUrl;

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

use self::relay::ReceivedMessage;

use super::*;

async fn spawn_local_relay() -> Result<Server> {
Server::spawn(ServerConfig::<(), ()> {
relay: Some(RelayConfig {
secret_key: SecretKey::generate(),
http_bind_addr: (Ipv4Addr::LOCALHOST, 0).into(),
tls: None,
limits: Default::default(),
}),
stun: None,
metrics_addr: None,
})
.await
}

#[tokio::test]
async fn test_no_services() {
let _guard = iroh_test::logging::setup();
Expand Down Expand Up @@ -748,18 +770,7 @@ mod tests {
#[tokio::test]
async fn test_root_handler() {
let _guard = iroh_test::logging::setup();
let server = Server::spawn(ServerConfig::<(), ()> {
relay: Some(RelayConfig {
secret_key: SecretKey::generate(),
http_bind_addr: (Ipv4Addr::LOCALHOST, 0).into(),
tls: None,
limits: Default::default(),
}),
stun: None,
metrics_addr: None,
})
.await
.unwrap();
let server = spawn_local_relay().await.unwrap();
let url = format!("http://{}", server.http_addr().unwrap());

let response = reqwest::get(&url).await.unwrap();
Expand All @@ -771,18 +782,7 @@ mod tests {
#[tokio::test]
async fn test_captive_portal_service() {
let _guard = iroh_test::logging::setup();
let server = Server::spawn(ServerConfig::<(), ()> {
relay: Some(RelayConfig {
secret_key: SecretKey::generate(),
http_bind_addr: (Ipv4Addr::LOCALHOST, 0).into(),
tls: None,
limits: Default::default(),
}),
stun: None,
metrics_addr: None,
})
.await
.unwrap();
let server = spawn_local_relay().await.unwrap();
let url = format!("http://{}/generate_204", server.http_addr().unwrap());
let challenge = "123az__.";

Expand All @@ -800,21 +800,28 @@ mod tests {
assert!(body.is_empty());
}

#[tokio::test]
async fn test_relay_client_legacy_route() {
let _guard = iroh_test::logging::setup();
let server = spawn_local_relay().await.unwrap();
// We're testing the legacy endpoint at `/derp`
let endpoint_url = format!("http://{}/derp", server.http_addr().unwrap());

let client = reqwest::Client::new();
let result = client
.get(endpoint_url)
.header(UPGRADE, HTTP_UPGRADE_PROTOCOL)
.send()
.await
.unwrap();

assert_eq!(result.status(), StatusCode::SWITCHING_PROTOCOLS);
}

#[tokio::test]
async fn test_relay_clients_both_derp() {
let _guard = iroh_test::logging::setup();
let server = Server::spawn(ServerConfig::<(), ()> {
relay: Some(RelayConfig {
secret_key: SecretKey::generate(),
http_bind_addr: (Ipv4Addr::LOCALHOST, 0).into(),
tls: None,
limits: Default::default(),
}),
stun: None,
metrics_addr: None,
})
.await
.unwrap();
let server = spawn_local_relay().await.unwrap();
let relay_url = format!("http://{}", server.http_addr().unwrap());
let relay_url: RelayUrl = relay_url.parse().unwrap();

Expand Down Expand Up @@ -879,18 +886,7 @@ mod tests {
#[tokio::test]
async fn test_relay_clients_both_websockets() {
let _guard = iroh_test::logging::setup();
let server = Server::spawn(ServerConfig::<(), ()> {
relay: Some(RelayConfig {
secret_key: SecretKey::generate(),
http_bind_addr: (Ipv4Addr::LOCALHOST, 0).into(),
tls: None,
limits: Default::default(),
}),
stun: None,
metrics_addr: None,
})
.await
.unwrap();
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: RelayUrl = relay_url.parse().unwrap();
Expand Down Expand Up @@ -956,18 +952,7 @@ mod tests {
#[tokio::test]
async fn test_relay_clients_websocket_and_derp() {
let _guard = iroh_test::logging::setup();
let server = Server::spawn(ServerConfig::<(), ()> {
relay: Some(RelayConfig {
secret_key: SecretKey::generate(),
http_bind_addr: (Ipv4Addr::LOCALHOST, 0).into(),
tls: None,
limits: Default::default(),
}),
stun: None,
metrics_addr: None,
})
.await
.unwrap();
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();
Expand Down
Loading