Skip to main content

simulator_client/
urls.rs

1use thiserror::Error;
2
3/// Error converting a URL scheme.
4#[derive(Debug, Error)]
5pub enum UrlError {
6    #[error("cannot derive WebSocket URL from `{url}`: expected http:// or https:// scheme")]
7    InvalidScheme { url: String },
8}
9
10/// Convert an `http(s)://` RPC endpoint URL to a `ws(s)://` URL.
11pub fn http_to_ws_url(url: &str) -> Result<String, UrlError> {
12    let scheme = if url.starts_with("https://") {
13        "wss"
14    } else if url.starts_with("http://") {
15        "ws"
16    } else {
17        return Err(UrlError::InvalidScheme {
18            url: url.to_string(),
19        });
20    };
21    let rest = url.split_once("://").map(|x| x.1).unwrap_or(url);
22    Ok(format!("{scheme}://{rest}"))
23}
24
25/// Derive an HTTP base URL from a WebSocket URL.
26/// `wss://host:port/path` → `https://host:port`
27pub(crate) fn http_base_from_ws_url(ws_url: &str) -> String {
28    let http = ws_url
29        .replacen("wss://", "https://", 1)
30        .replacen("ws://", "http://", 1);
31    if let Some(start) = http.find("://").map(|i| i + 3)
32        && let Some(slash) = http[start..].find('/')
33    {
34        return http[..start + slash].to_string();
35    }
36    http
37}
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42
43    #[test]
44    fn http_to_ws_converts_http() {
45        assert_eq!(
46            http_to_ws_url("http://localhost:8899").unwrap(),
47            "ws://localhost:8899"
48        );
49    }
50
51    #[test]
52    fn http_to_ws_converts_https() {
53        assert_eq!(
54            http_to_ws_url("https://api.mainnet-beta.solana.com").unwrap(),
55            "wss://api.mainnet-beta.solana.com"
56        );
57    }
58
59    #[test]
60    fn http_to_ws_rejects_other_schemes() {
61        assert!(matches!(
62            http_to_ws_url("ws://example.com"),
63            Err(UrlError::InvalidScheme { .. })
64        ));
65        assert!(matches!(
66            http_to_ws_url("ftp://example.com"),
67            Err(UrlError::InvalidScheme { .. })
68        ));
69        assert!(matches!(
70            http_to_ws_url("example.com"),
71            Err(UrlError::InvalidScheme { .. })
72        ));
73    }
74
75    #[test]
76    fn http_base_strips_path() {
77        assert_eq!(
78            http_base_from_ws_url("wss://host:8900/backtest"),
79            "https://host:8900"
80        );
81        assert_eq!(
82            http_base_from_ws_url("ws://localhost:8900/backtest"),
83            "http://localhost:8900"
84        );
85    }
86
87    #[test]
88    fn http_base_no_path() {
89        assert_eq!(
90            http_base_from_ws_url("wss://host:8900"),
91            "https://host:8900"
92        );
93    }
94}