use super::*;
use bytes::{Buf, BufMut, Bytes, BytesMut};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ErrorFrame {
stream_id: u32,
code: u32,
data: Option<Bytes>,
}
impl ErrorFrame {
pub const INVALID_SETUP: u32 = 0x00000001;
pub const UNSUPPORTED_SETUP: u32 = 0x00000002;
pub const REJECTED_SETUP: u32 = 0x00000003;
pub const REJECTED_RESUME: u32 = 0x00000004;
pub const CONNECTION_ERROR: u32 = 0x00000101;
pub const CONNECTION_CLOSE: u32 = 0x00000102;
pub const APPLICATION_ERROR: u32 = 0x00000201;
pub const REJECTED: u32 = 0x00000202;
pub const CANCELED: u32 = 0x00000203;
pub const INVALID: u32 = 0x00000204;
pub const MIN_APPLICATION_ERROR_CODE: u32 = 0x00000301;
pub const MAX_APPLICATION_ERROR_CODE: u32 = 0xFFFFFFFE;
pub const TYPE: FrameType = FrameType::ERROR;
}
impl ErrorFrame {
pub fn new(stream_id: u32, error_code: u32, data: Option<Bytes>) -> Self {
debug_assert_max_u31!(stream_id);
debug_assert_max_u31!(error_code);
ErrorFrame {
stream_id: stream_id & MAX_U31,
code: error_code & MAX_U31,
data,
}
}
pub fn stream_id(&self) -> u32 {
self.stream_id
}
pub fn error_code(&self) -> u32 {
self.code
}
pub fn data(&self) -> Option<&Bytes> {
self.data.as_ref()
}
pub fn data_utf8(&self) -> Option<&str> {
self.data.as_ref().map(|data| std::str::from_utf8(data).ok()).flatten()
}
}
impl Encode for ErrorFrame {
fn encode(&self, buf: &mut BytesMut) {
buf.put_u32(self.stream_id);
buf.put_u16(FrameType::ERROR.bits());
buf.put_u32(self.code);
if let Some(bytes) = &self.data {
buf.put_slice(bytes);
}
}
fn len(&self) -> usize {
let mut len = 10;
if let Some(bytes) = &self.data {
len += bytes.len()
}
len
}
}
impl Decode for ErrorFrame {
type Value = Self;
fn decode<B: Buf>(
buf: &mut B,
stream_id: u32,
_flags: Flags,
) -> Result<Self::Value> {
let code = eat_u32(buf)?;
validate_stream_id(stream_id, code)?;
let data = match buf.remaining() {
0 => None,
len => Some(eat_bytes(buf, len)?),
};
Ok(ErrorFrame { stream_id, code, data })
}
}
fn validate_stream_id(stream_id: u32, code: u32) -> Result<()> {
match code {
ErrorFrame::INVALID_SETUP
| ErrorFrame::UNSUPPORTED_SETUP
| ErrorFrame::REJECTED_SETUP
| ErrorFrame::REJECTED_RESUME
| ErrorFrame::CONNECTION_ERROR
| ErrorFrame::CONNECTION_CLOSE => {
if stream_id != 0 {
return Err(DecodeError::InvalidStreamId {
expected: "0",
found: stream_id,
});
}
}
ErrorFrame::APPLICATION_ERROR
| ErrorFrame::REJECTED
| ErrorFrame::CANCELED
| ErrorFrame::INVALID => {
if stream_id == 0 {
return Err(DecodeError::InvalidStreamId {
expected: "> 0",
found: stream_id,
});
}
}
_ => (),
}
Ok(())
}