mod whep;
mod whip;
use std::net::SocketAddr;
use std::time::Duration;
use url::Url;
#[derive(Clone, Debug, Default)]
#[non_exhaustive]
pub struct Config {
pub ice_candidates: Vec<SocketAddr>,
pub max_age: Option<Duration>,
pub bandwidth: moq_net::bandwidth::Allocator,
}
#[derive(Clone)]
pub struct Client {
config: Config,
http: reqwest::Client,
}
impl Client {
pub fn new(config: Config) -> Self {
Self {
config,
http: reqwest::Client::new(),
}
}
pub(crate) fn config(&self) -> &Config {
&self.config
}
pub(crate) fn http(&self) -> &reqwest::Client {
&self.http
}
pub async fn subscribe(&self, url: Url, broadcast: moq_net::broadcast::Producer) -> crate::Result<()> {
whep::dial(self, url, broadcast).await
}
pub async fn publish(
&self,
url: Url,
origin: moq_net::origin::Consumer,
path: impl moq_net::AsPath,
) -> crate::Result<()> {
whip::dial(self, url, origin, path).await
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn http_error_redacts_url() {
let listener = std::net::TcpListener::bind("127.0.0.1:0").expect("bind");
let address = listener.local_addr().expect("local address");
drop(listener);
let origin = moq_tokio::origin::spawn();
let broadcast = origin.create_broadcast("test").expect("create broadcast");
let url = format!("http://user:pass@{address}/whep/test?jwt=secret")
.parse()
.expect("WHEP URL");
let err = Client::new(Config::default())
.subscribe(url, broadcast)
.await
.expect_err("dial a closed port");
assert!(matches!(err, crate::Error::Http(_)), "unexpected error: {err}");
let printed = format!("{err} {err:?}");
for secret in ["jwt", "secret", "user:pass"] {
assert!(!printed.contains(secret), "error leaked {secret}: {printed}");
}
}
}