pub use frame::MAX_FRAME_SIZE;
pub mod frame;
mod macros;
pub mod routes;
pub mod prelude {
pub use super::frame::{Frame, MAX_FRAME_SIZE, Status, read_frame, write_frame};
pub use super::routes::{
Priority, RouteCategory, RouteInfo, RouteRegistry, register_core_routes, route_id,
};
pub use super::{ALPN_PROTOCOL, PROTOCOL_VERSION, protocol_info};
pub use super::{define_routes, protected_route_def, public_route_def, single_route};
}
pub const ALPN_PROTOCOL: &[u8] = b"quicburn/1";
pub const ALPN_PROTOCOL_STR: &str = "quicburn/1";
pub const PROTOCOL_VERSION: &str = env!("CARGO_PKG_VERSION");
pub fn protocol_info() -> String {
format!("quicburn/{} ({})", PROTOCOL_VERSION, ALPN_PROTOCOL_STR)
}
pub fn supports_alpn(protocol: &[u8]) -> bool {
protocol == ALPN_PROTOCOL
}
pub fn supported_alpn_protocols() -> &'static [&'static [u8]] {
&[ALPN_PROTOCOL]
}
pub fn parse_protocol_version(version_str: &str) -> Option<(u32, u32, u32)> {
let parts: Vec<&str> = version_str.split('.').collect();
if parts.len() != 3 {
return None;
}
let major = parts[0].parse::<u32>().ok()?;
let minor = parts[1].parse::<u32>().ok()?;
let patch = parts[2].parse::<u32>().ok()?;
Some((major, minor, patch))
}
pub fn is_compatible_version(version_str: &str) -> bool {
if let Some((major, _minor, _patch)) = parse_protocol_version(version_str) {
if let Some((our_major, _, _)) = parse_protocol_version(PROTOCOL_VERSION) {
return major == our_major;
}
}
false
}
pub const MAX_UDP_PAYLOAD_SIZE: usize = 1200;
pub const MAX_STREAMS: u64 = 100;
pub const DEFAULT_IDLE_TIMEOUT_MS: u64 = 30000;
pub const KEEP_ALIVE_INTERVAL_MS: u64 = 5000;
pub type Result<T> = anyhow::Result<T>;
#[cfg(test)]
mod tests {
pub use super::frame::{Frame, MAX_FRAME_SIZE, Status, read_frame, write_frame};
use super::*;
pub use super::routes::{
Priority, RouteCategory, RouteInfo, RouteRegistry, register_core_routes, route_id,
};
#[test]
fn test_protocol_info() {
let info = protocol_info();
assert!(info.contains("quicburn"));
assert!(info.contains(ALPN_PROTOCOL_STR));
}
#[test]
fn test_alpn_protocol() {
assert_eq!(ALPN_PROTOCOL, b"quicburn/1");
assert!(supports_alpn(b"quicburn/1"));
assert!(!supports_alpn(b"http/1.1"));
assert!(!supports_alpn(b"h3"));
}
#[test]
fn test_supported_alpn_protocols() {
let protocols = supported_alpn_protocols();
assert_eq!(protocols.len(), 1);
assert_eq!(protocols[0], ALPN_PROTOCOL);
}
#[test]
fn test_parse_protocol_version() {
assert_eq!(parse_protocol_version("1.0.0"), Some((1, 0, 0)));
assert_eq!(parse_protocol_version("1.2.3"), Some((1, 2, 3)));
assert_eq!(parse_protocol_version("0.1.0"), Some((0, 1, 0)));
assert_eq!(parse_protocol_version("invalid"), None);
assert_eq!(parse_protocol_version("1.0"), None);
assert_eq!(parse_protocol_version("1.0.0.0"), None);
}
#[test]
fn test_is_compatible_version() {
assert!(is_compatible_version(PROTOCOL_VERSION));
let current = PROTOCOL_VERSION;
if let Some((major, _, _)) = parse_protocol_version(current) {
let compat = format!("{}.{}.{}", major, 99, 99);
assert!(is_compatible_version(&compat));
let incompat = format!("{}.{}.{}", major + 1, 0, 0);
assert!(!is_compatible_version(&incompat));
}
}
#[test]
fn test_route_reexports() {
let mut registry = RouteRegistry::new();
register_core_routes(&mut registry);
assert!(!registry.is_empty());
}
#[test]
fn test_route_id_is_deterministic() {
assert_eq!(route_id("echo"), route_id("echo"));
assert_eq!(route_id("kv_get"), route_id("kv_get"));
}
#[test]
fn test_route_id_short_name() {
assert_eq!(route_id("hi"), route_id("hi"));
assert_ne!(route_id("hi"), route_id("ih"));
}
#[test]
fn test_route_registry_lookup_by_name_matches_by_id() {
let mut registry = RouteRegistry::new();
register_core_routes(&mut registry);
let by_name = registry
.get_by_name("health")
.expect("health route registered");
let by_id = registry
.get(route_id("health"))
.expect("health route registered");
assert_eq!(by_name.name, "health");
assert_eq!(by_name.id, by_id.id);
}
#[test]
fn test_route_registry_by_category() {
let mut registry = RouteRegistry::new();
register_core_routes(&mut registry);
let file_routes = registry.by_category(RouteCategory::File);
assert!(file_routes.iter().any(|r| r.name == "file_upload"));
}
#[test]
#[should_panic(expected = "route id collision")]
fn test_route_registry_rejects_duplicate_ids() {
let mut registry = RouteRegistry::new();
registry.register(
"aa",
RouteCategory::Other("test"),
Priority::Normal,
false,
None,
);
registry.register(
"aa",
RouteCategory::Other("test"),
Priority::Normal,
false,
None,
);
}
#[test]
fn test_frame_reexports() {
let frame = Frame::ok(0x01);
assert_eq!(frame.route, 0x01);
assert!(frame.is_ok());
}
#[test]
fn test_status_reexports() {
assert_eq!(Status::Ok.as_byte(), 0);
assert_eq!(Status::Error.as_byte(), 1);
}
#[test]
fn test_protocol_constants() {
assert!(MAX_UDP_PAYLOAD_SIZE > 0);
assert!(MAX_STREAMS > 0);
assert!(DEFAULT_IDLE_TIMEOUT_MS > 0);
assert!(KEEP_ALIVE_INTERVAL_MS > 0);
assert!(KEEP_ALIVE_INTERVAL_MS < DEFAULT_IDLE_TIMEOUT_MS);
}
}