diff --git a/src/api.rs b/src/api.rs index 442d2f2b..b19322b8 100644 --- a/src/api.rs +++ b/src/api.rs @@ -2,3 +2,4 @@ pub(crate) mod appservice_server; pub(crate) mod client_server; pub(crate) mod ruma_wrapper; pub(crate) mod server_server; +pub(crate) mod well_known; diff --git a/src/api/well_known.rs b/src/api/well_known.rs new file mode 100644 index 00000000..e32d45cb --- /dev/null +++ b/src/api/well_known.rs @@ -0,0 +1,58 @@ +#![warn(missing_docs, clippy::missing_docs_in_private_items)] + +//! Handle requests for `/.well-known/matrix/...` files + +use http::StatusCode; +use ruma::api::{ + client::discovery::discover_homeserver as client, + federation::discovery::discover_homeserver as server, +}; + +use crate::{services, Ar, Ra}; + +/// Handler for `/.well-known/matrix/server` +pub(crate) async fn server( + _: Ar, +) -> Result, StatusCode> { + let Some(authority) = + services().globals.config.server_discovery.server.authority.clone() + else { + return Err(StatusCode::NOT_FOUND); + }; + + if authority == services().globals.config.server_name { + // Delegation isn't needed in this case + return Err(StatusCode::NOT_FOUND); + } + + Ok(Ra(server::Response::new(authority))) +} + +/// Handler for `/.well-known/matrix/client` +pub(crate) async fn client(_: Ar) -> Ra { + let authority = services() + .globals + .config + .server_discovery + .client + .authority + .clone() + .unwrap_or_else(|| services().globals.config.server_name.clone()); + + let scheme = if services().globals.config.server_discovery.client.insecure { + "http" + } else { + "https" + }; + + let base_url = format!("{scheme}://{authority}"); + + // I wish ruma used an actual URL type instead of `String` + Ra(client::Response { + homeserver: client::HomeserverInfo::new(base_url.clone()), + identity_server: None, + sliding_sync_proxy: Some(client::SlidingSyncProxyInfo { + url: base_url, + }), + }) +} diff --git a/src/config.rs b/src/config.rs index a69e2394..e331364f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -30,7 +30,13 @@ pub(crate) struct Config { pub(crate) listen: Vec, pub(crate) tls: Option, + /// The name of this homeserver + /// + /// This is the value that will appear e.g. in user IDs and room aliases. pub(crate) server_name: OwnedServerName, + + #[serde(default)] + pub(crate) server_discovery: ServerDiscovery, pub(crate) database: DatabaseConfig, #[serde(default)] pub(crate) federation: FederationConfig, @@ -63,6 +69,35 @@ pub(crate) struct Config { pub(crate) emergency_password: Option, } +#[derive(Debug, Default, Deserialize)] +pub(crate) struct ServerDiscovery { + /// Server-server discovery configuration + #[serde(default)] + pub(crate) server: ServerServerDiscovery, + + /// Client-server discovery configuration + #[serde(default)] + pub(crate) client: ClientServerDiscovery, +} + +/// Server-server discovery configuration +#[derive(Debug, Default, Deserialize)] +pub(crate) struct ServerServerDiscovery { + /// The alternative authority to make server-server API requests to + pub(crate) authority: Option, +} + +/// Client-server discovery configuration +#[derive(Debug, Default, Deserialize)] +pub(crate) struct ClientServerDiscovery { + /// The alternative authority to make client-server API requests to + pub(crate) authority: Option, + + /// Controls whether HTTPS is used + #[serde(default)] + pub(crate) insecure: bool, +} + #[derive(Debug, Deserialize)] pub(crate) struct TlsConfig { pub(crate) certs: String, diff --git a/src/main.rs b/src/main.rs index 8af1825c..4a683b49 100644 --- a/src/main.rs +++ b/src/main.rs @@ -50,7 +50,7 @@ mod service; mod utils; pub(crate) use api::ruma_wrapper::{Ar, Ra}; -use api::{client_server, server_server}; +use api::{client_server, server_server, well_known}; pub(crate) use config::{Config, ListenConfig}; pub(crate) use database::KeyValueDatabase; pub(crate) use service::{pdu::PduEvent, Services}; @@ -491,6 +491,10 @@ fn routes(config: &Config) -> Router { .route("/", get(it_works)) .fallback(not_found); + let router = router + .route("/.well-known/matrix/client", get(well_known::client)) + .route("/.well-known/matrix/server", get(well_known::server)); + if config.federation.enable { router .ruma_route(s2s::get_server_version_route)