use bytes::{Bytes, BytesMut};
use crate::helpers::qpack;
use crate::protocol::common::Error;
use crate::protocol::quic::Varint;
pub struct Code;
impl Code {
pub const NO_ERROR: u64 = 0x0100;
pub const GENERAL_PROTOCOL_ERROR: u64 = 0x0101;
pub const INTERNAL_ERROR: u64 = 0x0102;
pub const STREAM_CREATION_ERROR: u64 = 0x0103;
pub const CLOSED_CRITICAL_STREAM: u64 = 0x0104;
pub const FRAME_UNEXPECTED: u64 = 0x0105;
pub const FRAME_ERROR: u64 = 0x0106;
pub const EXCESSIVE_LOAD: u64 = 0x0107;
pub const ID_ERROR: u64 = 0x0108;
pub const SETTINGS_ERROR: u64 = 0x0109;
pub const MISSING_SETTINGS: u64 = 0x010a;
pub const REQUEST_REJECTED: u64 = 0x010b;
pub const REQUEST_CANCELLED: u64 = 0x010c;
pub const REQUEST_INCOMPLETE: u64 = 0x010d;
pub const MESSAGE_ERROR: u64 = 0x010e;
pub const CONNECT_ERROR: u64 = 0x010f;
pub const VERSION_FALLBACK: u64 = 0x0110;
pub const QPACK_DECOMPRESSION_FAILED: u64 = 0x0200;
pub const QPACK_ENCODER_STREAM_ERROR: u64 = 0x0201;
pub const QPACK_DECODER_STREAM_ERROR: u64 = 0x0202;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FrameType {
Data,
Headers,
CancelPush,
Settings,
PushPromise,
GoAway,
MaxPushID,
}
impl FrameType {
pub const RESERVED: &[u64] = &[0x02, 0x06, 0x08, 0x09];
pub fn code(&self) -> u64 {
match self {
Self::Data => 0x00,
Self::Headers => 0x01,
Self::CancelPush => 0x03,
Self::Settings => 0x04,
Self::PushPromise => 0x05,
Self::GoAway => 0x07,
Self::MaxPushID => 0x0d,
}
}
pub fn from_code(code: u64) -> Option<Self> {
match code {
0x00 => Some(Self::Data),
0x01 => Some(Self::Headers),
0x03 => Some(Self::CancelPush),
0x04 => Some(Self::Settings),
0x05 => Some(Self::PushPromise),
0x07 => Some(Self::GoAway),
0x0d => Some(Self::MaxPushID),
_ => None,
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum Frame {
Data(Bytes),
Headers(Bytes),
CancelPush {
push_id: u64,
},
Settings(Vec<(u64, u64)>),
PushPromise {
push_id: u64,
block: Bytes,
},
GoAway {
id: u64,
},
MaxPushID {
push_id: u64,
},
}
impl Frame {
pub fn parse(buffer: &mut BytesMut) -> Result<Option<Frame>, Error> {
loop {
let (consumed, code, length) = {
let data = &buffer[..];
let (taken, code) = Varint::decode(data);
let (took, length) = Varint::decode(&data[taken.min(data.len())..]);
if taken == 0 || took == 0 || data.len() < taken + took + length as usize {
(0, 0, 0)
} else {
(taken + took, code, length)
}
};
if consumed == 0 {
return Ok(None);
}
if FrameType::RESERVED.contains(&code) {
return Err(Error::Protocol(format!("frame type {code:#x} is reserved")));
}
let mut frame = buffer.split_to(consumed + length as usize).freeze();
let payload = frame.split_off(consumed);
match FrameType::from_code(code) {
Some(kind) => return Frame::decode_shared(kind, &payload).map(Some),
None => continue,
}
}
}
pub fn kind(&self) -> FrameType {
match self {
Self::Data(_) => FrameType::Data,
Self::Headers(_) => FrameType::Headers,
Self::CancelPush { .. } => FrameType::CancelPush,
Self::Settings(_) => FrameType::Settings,
Self::PushPromise { .. } => FrameType::PushPromise,
Self::GoAway { .. } => FrameType::GoAway,
Self::MaxPushID { .. } => FrameType::MaxPushID,
}
}
pub fn write_payload(&self, out: &mut BytesMut) {
match self {
Self::Data(data) | Self::Headers(data) => out.extend_from_slice(data),
Self::CancelPush { push_id } | Self::MaxPushID { push_id } => Varint::encode(out, *push_id),
Self::GoAway { id } => Varint::encode(out, *id),
Self::Settings(params) => {
for (id, value) in params {
Varint::encode(out, *id);
Varint::encode(out, *value);
}
}
Self::PushPromise { push_id, block } => {
Varint::encode(out, *push_id);
out.extend_from_slice(block);
}
}
}
pub fn payload_len(&self) -> usize {
match self {
Self::Data(data) | Self::Headers(data) => data.len(),
Self::CancelPush { push_id } | Self::MaxPushID { push_id } => Varint::len(*push_id),
Self::GoAway { id } => Varint::len(*id),
Self::Settings(params) => {
params.iter().map(|(id, value)| Varint::len(*id) + Varint::len(*value)).sum()
}
Self::PushPromise { push_id, block } => Varint::len(*push_id) + block.len(),
}
}
pub fn payload(&self) -> Vec<u8> {
let mut out = BytesMut::with_capacity(self.payload_len());
self.write_payload(&mut out);
out.into()
}
pub fn encode_into(&self, out: &mut BytesMut) {
let length = self.payload_len();
out.reserve(length + 2 * Varint::len(Varint::MAXIMUM));
Varint::encode(out, self.kind().code());
Varint::encode(out, length as u64);
let start = out.len();
self.write_payload(out);
debug_assert_eq!(out.len() - start, length, "payload_len disagreed with write_payload");
}
pub fn write(kind: FrameType, payload: &[u8], out: &mut BytesMut) {
out.reserve(payload.len() + 2 * Varint::len(Varint::MAXIMUM));
Varint::encode(out, kind.code());
Varint::encode(out, payload.len() as u64);
out.extend_from_slice(payload);
}
pub fn encode(&self) -> Vec<u8> {
let mut out = BytesMut::with_capacity(self.payload_len() + 2 * Varint::len(Varint::MAXIMUM));
self.encode_into(&mut out);
out.into()
}
pub fn decode(kind: FrameType, payload: &[u8]) -> Result<Self, Error> {
Self::assemble(kind, payload, None)
}
pub fn decode_shared(kind: FrameType, payload: &Bytes) -> Result<Self, Error> {
Self::assemble(kind, payload.as_ref(), Some(payload))
}
pub fn assemble(kind: FrameType, payload: &[u8], shared: Option<&Bytes>) -> Result<Self, Error> {
let borrow = |slice: &[u8]| match shared {
Some(whole) => whole.slice_ref(slice),
None => Bytes::copy_from_slice(slice),
};
match kind {
FrameType::Data => Ok(Self::Data(borrow(payload))),
FrameType::Headers => Ok(Self::Headers(borrow(payload))),
FrameType::CancelPush => Ok(Self::CancelPush { push_id: Varint::only(payload, "CANCEL_PUSH")? }),
FrameType::MaxPushID => Ok(Self::MaxPushID { push_id: Varint::only(payload, "MAX_PUSH_ID")? }),
FrameType::GoAway => Ok(Self::GoAway { id: Varint::only(payload, "GOAWAY")? }),
FrameType::Settings => {
let mut rest = payload;
let mut params = Vec::new();
while !rest.is_empty() {
let (consumed, id) = Varint::decode(rest);
let (taken, value) = Varint::decode(&rest[consumed..]);
if consumed == 0 || taken == 0 {
return Err(Error::Protocol("SETTINGS ends inside a parameter".into()));
}
if params.iter().any(|(other, _)| *other == id) {
return Err(Error::Protocol(format!("setting {id:#x} is repeated")));
}
params.push((id, value));
rest = &rest[consumed + taken..];
}
Ok(Self::Settings(params))
}
FrameType::PushPromise => {
let (consumed, push_id) = Varint::decode(payload);
if consumed == 0 {
return Err(Error::Protocol("PUSH_PROMISE has no push identifier".into()));
}
Ok(Self::PushPromise { push_id, block: borrow(&payload[consumed..]) })
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StreamKind {
Control,
Push,
QPACKEncoder,
QPACKDecoder,
Request,
}
impl StreamKind {
pub fn code(&self) -> Option<u64> {
match self {
Self::Control => Some(0x00),
Self::Push => Some(0x01),
Self::QPACKEncoder => Some(0x02),
Self::QPACKDecoder => Some(0x03),
Self::Request => None,
}
}
pub fn from_code(code: u64) -> Option<Self> {
match code {
0x00 => Some(Self::Control),
0x01 => Some(Self::Push),
0x02 => Some(Self::QPACKEncoder),
0x03 => Some(Self::QPACKDecoder),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Settings {
pub qpack_max_table_capacity: u64,
pub qpack_blocked_streams: u64,
pub max_field_section_size: Option<u64>,
pub enable_connect_protocol: bool,
}
impl Settings {
pub const QPACK_MAX_TABLE_CAPACITY: u64 = 0x01;
pub const MAX_FIELD_SECTION_SIZE: u64 = 0x06;
pub const QPACK_BLOCKED_STREAMS: u64 = 0x07;
pub const ENABLE_CONNECT_PROTOCOL: u64 = 0x08;
pub const RESERVED: &[u64] = &[0x00, 0x02, 0x03, 0x04, 0x05];
pub fn parameters(&self) -> Vec<(u64, u64)> {
let mut params = vec![
(Settings::QPACK_MAX_TABLE_CAPACITY, self.qpack_max_table_capacity),
(Settings::QPACK_BLOCKED_STREAMS, self.qpack_blocked_streams),
(Settings::ENABLE_CONNECT_PROTOCOL, u64::from(self.enable_connect_protocol)),
];
if let Some(size) = self.max_field_section_size {
params.push((Settings::MAX_FIELD_SECTION_SIZE, size));
}
params
}
pub fn peer() -> Self {
Self {
qpack_max_table_capacity: qpack::DynamicTable::DEFAULT_CAPACITY as u64,
qpack_blocked_streams: 0,
max_field_section_size: None,
enable_connect_protocol: false,
}
}
pub fn apply(&mut self, id: u64, value: u64) -> Result<(), Error> {
if Settings::RESERVED.contains(&id) {
return Err(Error::Protocol(format!("setting {id:#x} is reserved")));
}
match id {
Settings::QPACK_MAX_TABLE_CAPACITY => self.qpack_max_table_capacity = value,
Settings::QPACK_BLOCKED_STREAMS => self.qpack_blocked_streams = value,
Settings::MAX_FIELD_SECTION_SIZE => self.max_field_section_size = Some(value),
Settings::ENABLE_CONNECT_PROTOCOL => {
if value > 1 {
return Err(Error::Protocol("SETTINGS_ENABLE_CONNECT_PROTOCOL is not a flag".into()));
}
self.enable_connect_protocol = value == 1;
}
_ => {}
}
Ok(())
}
}
impl Default for Settings {
fn default() -> Self {
Self {
qpack_max_table_capacity: qpack::Decoder::DEFAULT_MAX_CAPACITY as u64,
qpack_blocked_streams: qpack::Decoder::DEFAULT_MAX_BLOCKED_STREAMS as u64,
max_field_section_size: None,
enable_connect_protocol: true,
}
}
}