use crate::{Error, coding::*};
use super::{DataType, Message, Parameters, Version};
const PARAM_PATH: u64 = 0x2;
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct Setup {
pub path: Option<String>,
}
impl Message for Setup {
fn encode_msg<W: bytes::BufMut>(&self, w: &mut W, version: Version) -> Result<(), EncodeError> {
if !version.has_setup_stream() {
return Err(EncodeError::Version);
}
let mut params = Parameters::default();
if let Some(path) = &self.path {
if !path.starts_with('/') {
return Err(EncodeError::InvalidState);
}
params.set(PARAM_PATH, path.clone().into_bytes());
}
params.encode(w, version)
}
fn decode_msg<R: bytes::Buf>(r: &mut R, version: Version) -> Result<Self, DecodeError> {
if !version.has_setup_stream() {
return Err(DecodeError::Version);
}
let params = Parameters::decode(r, version)?;
let path = match params.get(PARAM_PATH) {
Some(bytes) => {
let s = std::str::from_utf8(bytes).map_err(|_| DecodeError::InvalidValue)?;
if !s.starts_with('/') {
return Err(DecodeError::InvalidValue);
}
Some(s.to_string())
}
None => None,
};
Ok(Self { path })
}
}
pub(crate) async fn send_setup<S: web_transport_trait::Session>(
session: &S,
version: Version,
setup: Setup,
) -> Result<(), Error> {
let stream = session.open_uni().await.map_err(Error::from_transport)?;
let mut writer = Writer::new(stream, version);
writer.encode(&DataType::Setup).await?;
writer.encode(&setup).await?;
writer.finish()?;
Ok(())
}
pub(crate) async fn accept_setup<S: web_transport_trait::Session>(
session: &S,
version: Version,
) -> Result<Setup, Error> {
loop {
let recv = session.accept_uni().await.map_err(Error::from_transport)?;
let mut reader = Reader::new(recv, version);
match reader.decode::<DataType>().await? {
DataType::Setup => return reader.decode().await,
DataType::Group => reader.abort(&Error::Cancel),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use bytes::BytesMut;
fn roundtrip(setup: &Setup) -> Setup {
let mut buf = BytesMut::new();
setup.encode(&mut buf, Version::Lite05Wip).unwrap();
let mut slice = &buf[..];
let decoded = Setup::decode(&mut slice, Version::Lite05Wip).unwrap();
assert!(bytes::Buf::remaining(&slice) == 0, "trailing bytes after decode");
decoded
}
#[test]
fn empty() {
assert_eq!(roundtrip(&Setup::default()), Setup::default());
}
#[test]
fn with_path() {
let setup = Setup {
path: Some("/live/room".to_string()),
};
assert_eq!(roundtrip(&setup), setup);
}
#[test]
fn rejects_relative_path() {
let mut buf = BytesMut::new();
assert!(matches!(
Setup {
path: Some("foo".into())
}
.encode(&mut buf, Version::Lite05Wip),
Err(EncodeError::InvalidState)
));
let mut params = Parameters::default();
params.set(PARAM_PATH, b"foo".to_vec());
let mut body = BytesMut::new();
params.encode(&mut body, Version::Lite05Wip).unwrap();
let mut framed = BytesMut::new();
(body.len() as u64).encode(&mut framed, Version::Lite05Wip).unwrap();
framed.extend_from_slice(&body);
assert!(matches!(
Setup::decode(&mut framed, Version::Lite05Wip),
Err(DecodeError::InvalidValue)
));
}
#[test]
fn rejects_before_lite05() {
let mut buf = BytesMut::new();
assert!(matches!(
Setup::default().encode(&mut buf, Version::Lite04),
Err(EncodeError::Version)
));
}
#[test]
fn ignores_unknown_parameters() {
let mut params = Parameters::default();
params.set(PARAM_PATH, b"/foo".to_vec());
params.set(0xbeef, b"whatever".to_vec());
let mut body = BytesMut::new();
params.encode(&mut body, Version::Lite05Wip).unwrap();
let mut buf = BytesMut::new();
(body.len() as u64).encode(&mut buf, Version::Lite05Wip).unwrap();
buf.extend_from_slice(&body);
let decoded = Setup::decode(&mut buf, Version::Lite05Wip).unwrap();
assert_eq!(decoded.path.as_deref(), Some("/foo"));
}
}