use crate::status::Status;
#[cfg_attr(not(target_arch = "wasm32"), allow(dead_code))]
pub(crate) fn websocket_url(base_url: &str) -> Result<(String, String), Status> {
let trimmed = base_url.trim_end_matches('/');
let (scheme, rest) = trimmed
.split_once("://")
.ok_or_else(|| Status::unavailable(format!("base URL has no scheme: {base_url}")))?;
let ws_scheme = match scheme {
"https" | "wss" => "wss",
"http" | "ws" => "ws",
other => {
return Err(Status::unavailable(format!("unsupported URL scheme: {other}")));
}
};
let authority = rest.split('/').next().unwrap_or(rest).to_string();
Ok((format!("{ws_scheme}://{rest}"), authority))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn maps_scheme_and_extracts_authority() {
for (input, ws, authority) in [
("http://localhost:8080", "ws://localhost:8080", "localhost:8080"),
("https://api.example.com", "wss://api.example.com", "api.example.com"),
("https://api.example.com/grpc/", "wss://api.example.com/grpc", "api.example.com"),
("http://127.0.0.1:8080/a/b", "ws://127.0.0.1:8080/a/b", "127.0.0.1:8080"),
("ws://localhost:9090", "ws://localhost:9090", "localhost:9090"),
("wss://api.example.com", "wss://api.example.com", "api.example.com"),
] {
let (got_ws, got_authority) = websocket_url(input).unwrap();
assert_eq!((got_ws.as_str(), got_authority.as_str()), (ws, authority), "{input}");
}
}
#[test]
fn rejects_a_url_it_cannot_map() {
assert!(websocket_url("localhost:8080").is_err());
assert!(websocket_url("ftp://host").is_err());
assert!(websocket_url("").is_err());
}
#[test]
fn a_trailing_slash_does_not_become_an_empty_path_segment() {
let (ws, authority) = websocket_url("https://host/").unwrap();
assert_eq!(ws, "wss://host");
assert_eq!(authority, "host");
}
}