use bytes::BytesMut;
use std::time::Duration;
pub const KEEPALIVE_PING: &[u8] = b"\r\n\r\n";
pub const KEEPALIVE_PONG: &[u8] = b"\r\n";
pub const DEFAULT_PING_INTERVAL: Duration = Duration::from_secs(95);
#[derive(Debug, Clone)]
pub struct KeepAliveConfig {
pub send_pings: bool,
pub ping_interval: Duration,
}
impl Default for KeepAliveConfig {
fn default() -> Self {
Self {
send_pings: false,
ping_interval: DEFAULT_PING_INTERVAL,
}
}
}
impl KeepAliveConfig {
pub fn enabled() -> Self {
Self {
send_pings: true,
..Default::default()
}
}
pub fn enabled_with_interval(interval: Duration) -> Self {
Self {
send_pings: true,
ping_interval: interval,
}
}
}
pub fn strip_leading_keepalives(buf: &mut BytesMut) -> usize {
let mut crlf_count = 0usize;
while buf.len() >= (crlf_count + 1) * 2 && &buf[crlf_count * 2..crlf_count * 2 + 2] == b"\r\n" {
crlf_count += 1;
}
if crlf_count > 0 {
let _ = buf.split_to(crlf_count * 2);
}
crlf_count / 2
}
#[cfg(test)]
mod tests {
use super::*;
fn buf(bytes: &[u8]) -> BytesMut {
BytesMut::from(bytes)
}
#[test]
fn empty_buffer_no_pings() {
let mut b = buf(b"");
assert_eq!(strip_leading_keepalives(&mut b), 0);
assert_eq!(&b[..], b"");
}
#[test]
fn no_leading_crlf_passes_through() {
let mut b = buf(b"INVITE sip:bob@example.com SIP/2.0\r\n");
assert_eq!(strip_leading_keepalives(&mut b), 0);
assert_eq!(&b[..], b"INVITE sip:bob@example.com SIP/2.0\r\n");
}
#[test]
fn single_crlf_is_pong_no_ping() {
let mut b = buf(b"\r\nINVITE sip:bob SIP/2.0\r\n");
assert_eq!(strip_leading_keepalives(&mut b), 0);
assert_eq!(&b[..], b"INVITE sip:bob SIP/2.0\r\n");
}
#[test]
fn double_crlf_is_one_ping() {
let mut b = buf(b"\r\n\r\nINVITE sip:bob SIP/2.0\r\n");
assert_eq!(strip_leading_keepalives(&mut b), 1);
assert_eq!(&b[..], b"INVITE sip:bob SIP/2.0\r\n");
}
#[test]
fn three_crlfs_is_one_ping_plus_pong() {
let mut b = buf(b"\r\n\r\n\r\nINVITE sip:bob SIP/2.0\r\n");
assert_eq!(strip_leading_keepalives(&mut b), 1);
assert_eq!(&b[..], b"INVITE sip:bob SIP/2.0\r\n");
}
#[test]
fn four_crlfs_is_two_pings() {
let mut b = buf(b"\r\n\r\n\r\n\r\nINVITE sip:bob SIP/2.0\r\n");
assert_eq!(strip_leading_keepalives(&mut b), 2);
assert_eq!(&b[..], b"INVITE sip:bob SIP/2.0\r\n");
}
#[test]
fn only_keepalives_drains_buffer() {
let mut b = buf(b"\r\n\r\n");
assert_eq!(strip_leading_keepalives(&mut b), 1);
assert!(b.is_empty());
}
#[test]
fn lone_cr_is_not_consumed() {
let mut b = buf(b"\rJUNK");
assert_eq!(strip_leading_keepalives(&mut b), 0);
assert_eq!(&b[..], b"\rJUNK");
}
#[test]
fn lf_alone_is_not_consumed() {
let mut b = buf(b"\nJUNK");
assert_eq!(strip_leading_keepalives(&mut b), 0);
assert_eq!(&b[..], b"\nJUNK");
}
#[test]
fn keepalive_interleaved_only_strips_leading() {
let mut b = buf(b"\r\n\r\nINVITE sip:bob SIP/2.0\r\nVia: ...\r\n\r\n");
assert_eq!(strip_leading_keepalives(&mut b), 1);
assert_eq!(&b[..], b"INVITE sip:bob SIP/2.0\r\nVia: ...\r\n\r\n");
}
#[test]
fn config_default_is_off() {
let cfg = KeepAliveConfig::default();
assert!(!cfg.send_pings);
assert_eq!(cfg.ping_interval, DEFAULT_PING_INTERVAL);
}
#[test]
fn config_enabled_helpers() {
let cfg = KeepAliveConfig::enabled();
assert!(cfg.send_pings);
assert_eq!(cfg.ping_interval, DEFAULT_PING_INTERVAL);
let cfg = KeepAliveConfig::enabled_with_interval(Duration::from_secs(30));
assert!(cfg.send_pings);
assert_eq!(cfg.ping_interval, Duration::from_secs(30));
}
}