arcly-stream 0.1.7

An open-extensible live-media streaming kernel: lock-free zero-copy frame fan-out, instant-start GOP cache, a pluggable multi-protocol ingestion layer (RTMP, RTSP, SRT, WHIP/WHEP shipped), and a feature-gated pure-Rust media plane (MPEG-TS/HLS/fMP4) — runtime, config, and metrics free.
Documentation
//! Small URL helpers shared by the media-protocol egress clients.
//!
//! The RTMP relay, RTSP server, and future scheme-addressed clients all open a
//! `scheme://host[:port]/path…` URL. The path semantics differ per protocol
//! (RTMP needs exactly `app/stream`; RTSP strips track/control and query
//! suffixes), so those stay local — but splitting off the scheme and parsing the
//! `host[:port]` authority is identical, and lives here once.

use crate::{Result, StreamError};

/// Strip `scheme://` and split the remainder into `(authority, path)` at the
/// first `/`. Returns `None` if the scheme prefix is absent or there is no path.
pub(crate) fn split_scheme<'a>(url: &'a str, scheme: &str) -> Option<(&'a str, &'a str)> {
    url.strip_prefix(scheme)?.split_once('/')
}

/// Parse a `host[:port]` authority, falling back to `default_port` when the
/// port is omitted. Errors on an empty host or an unparseable port.
pub(crate) fn parse_authority(authority: &str, default_port: u16) -> Result<(String, u16)> {
    let (host, port) = match authority.split_once(':') {
        Some((h, p)) => (
            h,
            p.parse()
                .map_err(|_| StreamError::protocol("bad port in url authority"))?,
        ),
        None => (authority, default_port),
    };
    if host.is_empty() {
        return Err(StreamError::protocol("empty host in url authority"));
    }
    Ok((host.to_string(), port))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn splits_scheme_and_path() {
        assert_eq!(
            split_scheme("rtmp://h:1936/live/cam", "rtmp://"),
            Some(("h:1936", "live/cam"))
        );
        assert_eq!(split_scheme("http://h/live/cam", "rtmp://"), None);
        assert_eq!(split_scheme("rtmp://hostonly", "rtmp://"), None);
    }

    #[test]
    fn parses_authority_with_and_without_port() {
        assert_eq!(
            parse_authority("cdn.example.com", 1935).unwrap(),
            ("cdn.example.com".to_string(), 1935)
        );
        assert_eq!(
            parse_authority("10.0.0.5:1936", 1935).unwrap(),
            ("10.0.0.5".to_string(), 1936)
        );
        assert!(parse_authority("host:notaport", 1935).is_err());
        assert!(parse_authority(":1935", 1935).is_err());
    }
}