aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar cel 🌸 <cel@bunny.garden>2025-04-30 16:18:05 +0100
committerLibravatar cel 🌸 <cel@bunny.garden>2025-04-30 16:18:05 +0100
commitccae86c3b38f829828adc40ab1695b137dc2b070 (patch)
tree5b87d2599297cf1bd5140246eeb9d9d6b19a0d19
parent74e5ab6702bbbe292fcb38ccb97036ce5b376956 (diff)
downloadluz-ccae86c3b38f829828adc40ab1695b137dc2b070.tar.gz
luz-ccae86c3b38f829828adc40ab1695b137dc2b070.tar.bz2
luz-ccae86c3b38f829828adc40ab1695b137dc2b070.zip
feat(luz): xep-0156: discovering websocket connection url
-rw-r--r--luz/Cargo.toml9
-rw-r--r--luz/src/connection/ws.rs71
-rw-r--r--luz/src/error.rs12
3 files changed, 81 insertions, 11 deletions
diff --git a/luz/Cargo.toml b/luz/Cargo.toml
index 0543768..4ab1a6e 100644
--- a/luz/Cargo.toml
+++ b/luz/Cargo.toml
@@ -24,7 +24,7 @@ rsasl = { version = "2.0.1", default-features = false, features = [
tokio = { workspace = true, features = ["io-util"] }
tracing = "0.1.40"
try_map = "0.3.1"
-stanza = { version = "0.1.0", path = "../stanza" }
+stanza = { version = "0.1.0", path = "../stanza", features = ["xep_0156"] }
peanuts = { version = "0.1.0", git = "https://bunny.garden/peanuts" }
# peanuts = { version = "0.1.0", path = "../../peanuts" }
jid = { version = "0.1.0", path = "../jid" }
@@ -35,13 +35,14 @@ pin-project = "1.1.7"
thiserror = "2.0.11"
[target.'cfg(target_arch = "wasm32")'.dependencies]
-tokio = { workspace = true, features = ["io-util", "sync"] }
+tokio = { workspace = true, features = ["io-util", "sync", "macros"] }
uuid = { version = "1.13.1", features = ["js", "v4"] }
getrandom = { version = "0.2.15", features = ["js"] }
-stanza = { version = "0.1.0", path = "../stanza", features = ["rfc_7395"] }
-web-sys = { version = "0.3", features = ["Request", "WebSocket"] }
+stanza = { version = "0.1.0", path = "../stanza", features = ["rfc_7395", "xep_0156"] }
+web-sys = { version = "0.3", features = ["Request", "WebSocket", "RequestInit", "Request", "Window", "Response", "ErrorEvent"] }
js-sys = "0.3"
wasm-bindgen = "0.2"
+wasm-bindgen-futures = "0.4.50"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tokio = { workspace = true, features = ["io-util", "sync"] }
diff --git a/luz/src/connection/ws.rs b/luz/src/connection/ws.rs
index 74e3223..caecf4a 100644
--- a/luz/src/connection/ws.rs
+++ b/luz/src/connection/ws.rs
@@ -1,8 +1,12 @@
+use js_sys::Error;
+use peanuts::{reader::ReadableString, Reader};
+use stanza::xep_0156::XRD;
use tokio::sync::mpsc;
use tracing::debug;
use wasm_bindgen::closure::Closure;
use wasm_bindgen::JsCast;
-use web_sys::{ErrorEvent, WebSocket};
+use wasm_bindgen_futures::JsFuture;
+use web_sys::{ErrorEvent, Request, RequestInit, Response, WebSocket};
use crate::error::ConnectionError;
@@ -17,11 +21,62 @@ impl Connection {
) -> Result<Self, ConnectionError> {
// TODO: get the connection url here
debug!("creating websocket");
- let url = String::from("wss://xmpp.bunny.garden/ws");
- let ws = WebSocket::new_with_str(&url, "xmpp").map_err(|error| {
+
+ let opts = RequestInit::new();
+ opts.set_method("GET");
+ let host_meta_url = format!("https://{}/.well-known/host-meta", server.as_ref());
+
+ let request = Request::new_with_str_and_init(&host_meta_url, &opts).map_err(|e| {
+ let error: Error = e.into();
+ ConnectionError::CreateHostMeta(error.message().as_string().unwrap())
+ })?;
+
+ let window = web_sys::window().unwrap();
+ let resp_value = JsFuture::from(window.fetch_with_request(&request))
+ .await
+ .map_err(|e| {
+ let error: Error = e.into();
+ ConnectionError::FetchHostMeta(error.message().as_string().unwrap())
+ })?;
+
+ let resp: Response = resp_value.dyn_into().unwrap();
+
+ let text = JsFuture::from(resp.text().map_err(|e| {
+ let error: Error = e.into();
+ ConnectionError::FetchHostMeta(error.message().as_string().unwrap())
+ })?)
+ .await
+ .map_err(|e| {
+ let error: Error = e.into();
+ ConnectionError::FetchHostMeta(error.message().as_string().unwrap())
+ })?;
+
+ let host_meta = text.as_string().ok_or(ConnectionError::HostMetaText)?;
+
+ let readable_string = ReadableString(host_meta);
+ let mut reader = Reader::new(readable_string);
+
+ reader.read_prolog().await.unwrap();
+
+ let xrd: XRD = reader.read().await?;
+
+ let link = xrd
+ .links
+ .iter()
+ .find(|link| link.rel.as_deref() == Some("urn:xmpp:alt-connections:websocket"))
+ .ok_or(ConnectionError::MissingWebsocketLink)?;
+
+ let url = link.href.as_ref().ok_or(ConnectionError::LinkMissingHref)?;
+
+ Connection::connect_url(url).await
+ }
+
+ pub async fn connect_url(url: impl AsRef<str>) -> Result<Self, ConnectionError> {
+ let url = url.as_ref();
+ let ws = WebSocket::new_with_str(url, "xmpp").map_err(|error| {
let error: js_sys::Error = error.into();
let message = error.message().as_string().unwrap();
- ConnectionError::Create(url.clone(), message)
+ ConnectionError::Create(url.to_string(), message)
})?;
debug!("created websocket");
let (send, mut open_recv) = mpsc::unbounded_channel();
@@ -50,11 +105,13 @@ impl Connection {
ws.set_onerror(Some(onerror.as_ref().unchecked_ref()));
tokio::select! {
- _error = error_recv.recv() => { Err(ConnectionError::Connect(url))? },
+ _error = error_recv.recv() => { Err(ConnectionError::Connect(url.to_string()))? },
Some(open) = open_recv.recv() => { },
- else => { Err(ConnectionError::Connect(url))? }
+ else => { Err(ConnectionError::Connect(url.to_string()))? }
}
- debug!("um what");
+
+ ws.set_onopen(None);
+ ws.set_onerror(None);
// TODO: check reply if it's xmpp too
Ok(Self(ws))
diff --git a/luz/src/error.rs b/luz/src/error.rs
index 3fcca57..fcb32a0 100644
--- a/luz/src/error.rs
+++ b/luz/src/error.rs
@@ -68,4 +68,16 @@ pub enum ConnectionError {
Create(String, String),
#[error("failed to connect to \"{0}\"")]
Connect(String),
+ #[error("failed to create host-meta request: {0}")]
+ CreateHostMeta(String),
+ #[error("fetching host meta: {0}")]
+ FetchHostMeta(String),
+ #[error("getting host meta text")]
+ HostMetaText,
+ #[error("parsing host meta xrd: {0}")]
+ XRDParse(#[from] peanuts::Error),
+ #[error("XRD missing websocket Link")]
+ MissingWebsocketLink,
+ #[error("XRD websocket Link missing href")]
+ LinkMissingHref,
}