wtx 0.44.3

A collection of different transport implementations and related tools focused primarily on web technologies.
Documentation
use crate::{
  http::{HttpRecvParams, MAX_FRAME_LEN_LOWER_BOUND, MAX_FRAME_LEN_UPPER_BOUND, u31::U31},
  http2::{
    Http2Error, Http2ErrorCode,
    common_flags::CommonFlags,
    frame_init::{FrameInit, FrameInitTy},
    misc::protocol_err,
  },
};

#[derive(Debug, Eq, PartialEq)]
pub(crate) struct SettingsFrame {
  cf: CommonFlags,
  enable_connect_protocol: Option<bool>,
  header_table_size: Option<u32>,
  initial_window_size: Option<U31>,
  len: u8,
  max_concurrent_streams: Option<u32>,
  max_frame_size: Option<u32>,
  max_header_list_size: Option<u32>,
}

impl SettingsFrame {
  pub(crate) const fn ack() -> Self {
    SettingsFrame { cf: CommonFlags::ack(), ..SettingsFrame::empty() }
  }

  pub(crate) const fn empty() -> Self {
    Self {
      cf: CommonFlags::empty(),
      enable_connect_protocol: None,
      header_table_size: None,
      initial_window_size: None,
      len: 0,
      max_concurrent_streams: None,
      max_frame_size: None,
      max_header_list_size: None,
    }
  }
  pub(crate) fn from_hp(hp: HttpRecvParams) -> Self {
    let mut settings_frame = SettingsFrame::empty();
    settings_frame.set_enable_connect_protocol(Some(hp.enable_connect_protocol()));
    settings_frame.set_header_table_size(Some(hp.max_hpack_len().0));
    settings_frame.set_initial_window_size(Some(U31::from_u32(hp.initial_window_len())));
    settings_frame.set_max_concurrent_streams(Some(hp.max_concurrent_streams_num()));
    settings_frame.set_max_frame_size(Some(hp.max_frame_len()));
    settings_frame.set_max_header_list_size(Some(hp.max_headers_len()));
    settings_frame
  }

  pub(crate) fn bytes<'buffer>(&self, buffer: &'buffer mut [u8; 45]) -> &'buffer [u8] {
    macro_rules! copy_bytes {
      ($buffer:expr, $bytes:expr, $idx:expr) => {{
        let next_idx = $idx.wrapping_add(6);
        if let Some([a, b, c, d, e, f]) = $buffer.get_mut($idx..next_idx) {
          if let Some([g, h, i, j, k, l]) = $bytes {
            *a = g;
            *b = h;
            *c = i;
            *d = j;
            *e = k;
            *f = l;
            $idx = next_idx;
          }
        }
      }};
    }

    #[inline] // INLINE!
    const fn bytes(ty: u16, value: u32) -> [u8; 6] {
      let [a, b] = ty.to_be_bytes();
      let [c, d, e, f] = value.to_be_bytes();
      [a, b, c, d, e, f]
    }

    {
      let fi = FrameInit::new(self.cf, self.len.into(), U31::ZERO, FrameInitTy::Settings);
      let [a, b, c, d, e, f, g, h, i, ..] = buffer;
      let [j, k, l, m, n, o, p, q, r] = fi.bytes();
      *a = j;
      *b = k;
      *c = l;
      *d = m;
      *e = n;
      *f = o;
      *g = p;
      *h = q;
      *i = r;
    }
    let Self {
      cf: _,
      enable_connect_protocol,
      header_table_size,
      initial_window_size,
      len: _,
      max_concurrent_streams,
      max_frame_size,
      max_header_list_size,
    } = self;
    let mut idx: usize = 9;
    copy_bytes!(buffer, header_table_size.map(|el| bytes(1, el)), idx);
    copy_bytes!(buffer, max_concurrent_streams.map(|el| bytes(3, el)), idx);
    copy_bytes!(buffer, initial_window_size.map(|el| bytes(4, el.u32())), idx);
    copy_bytes!(buffer, max_frame_size.map(|el| bytes(5, el)), idx);
    copy_bytes!(buffer, max_header_list_size.map(|el| bytes(6, el)), idx);
    copy_bytes!(buffer, enable_connect_protocol.map(|el| bytes(8, u32::from(el))), idx);
    buffer.get(..idx).unwrap_or_default()
  }

  pub(crate) const fn enable_connect_protocol(&self) -> Option<bool> {
    self.enable_connect_protocol
  }

  pub(crate) const fn has_ack(&self) -> bool {
    self.cf.has_ack()
  }

  pub(crate) const fn header_table_size(&self) -> Option<u32> {
    self.header_table_size
  }

  pub(crate) const fn initial_window_size(&self) -> Option<U31> {
    self.initial_window_size
  }

  pub(crate) const fn max_concurrent_streams(&self) -> Option<u32> {
    self.max_concurrent_streams
  }

  pub(crate) const fn max_frame_size(&self) -> Option<u32> {
    self.max_frame_size
  }

  pub(crate) const fn max_header_list_size(&self) -> Option<u32> {
    self.max_header_list_size
  }

  pub(crate) fn read(bytes: &[u8], mut fi: FrameInit) -> crate::Result<Self> {
    if fi.stream_id.is_not_zero() {
      return Err(protocol_err(Http2Error::InvalidSettingsFrameNonZeroId));
    }

    fi.cf.only_ack();
    let mut settings_frame = SettingsFrame { cf: fi.cf, ..Self::empty() };

    if settings_frame.cf.has_ack() {
      if !bytes.is_empty() {
        return Err(crate::Error::Http2ErrorGoAway(
          Http2ErrorCode::FrameSizeError,
          Http2Error::InvalidSettingsFrameNonEmptyAck,
        ));
      }
      return Ok(settings_frame);
    }

    if !bytes.len().is_multiple_of(6) {
      return Err(crate::Error::Http2ErrorGoAway(
        Http2ErrorCode::FrameSizeError,
        Http2Error::InvalidSettingsFrameLength,
      ));
    }

    let Self {
      cf: _,
      enable_connect_protocol,
      header_table_size,
      initial_window_size,
      len,
      max_concurrent_streams,
      max_frame_size,
      max_header_list_size,
    } = &mut settings_frame;

    for [a, b, c, d, e, f] in bytes.as_chunks().0 {
      let Ok(setting) = Setting::new([*a, *b, *c, *d, *e, *f]) else {
        continue;
      };
      match setting {
        Setting::EnableConnectProtocol(elem) => {
          *enable_connect_protocol = Some(elem);
        }
        Setting::HeaderTableSize(elem) => {
          *header_table_size = Some(elem);
        }
        Setting::InitialWindowSize(elem) => {
          if elem > U31::MAX {
            return Err(protocol_err(Http2Error::OutOfBoundsWindowSize));
          }
          *initial_window_size = Some(U31::from_u32(elem));
        }
        Setting::MaxConcurrentStreams(elem) => {
          *max_concurrent_streams = Some(elem);
        }
        Setting::MaxFrameSize(elem) => {
          if (MAX_FRAME_LEN_LOWER_BOUND..=MAX_FRAME_LEN_UPPER_BOUND).contains(&elem) {
            *max_frame_size = Some(elem);
          } else {
            return Err(protocol_err(Http2Error::OutOfBoundsMaxFrameSize));
          }
        }
        Setting::MaxHeaderListSize(elem) => {
          *max_header_list_size = Some(elem);
        }
      }
    }

    if enable_connect_protocol.is_some() {
      *len = len.wrapping_add(6);
    }
    if header_table_size.is_some() {
      *len = len.wrapping_add(6);
    }
    if initial_window_size.is_some() {
      *len = len.wrapping_add(6);
    }
    if max_concurrent_streams.is_some() {
      *len = len.wrapping_add(6);
    }
    if max_frame_size.is_some() {
      *len = len.wrapping_add(6);
    }
    if max_header_list_size.is_some() {
      *len = len.wrapping_add(6);
    }
    Ok(settings_frame)
  }

  pub(crate) fn set_enable_connect_protocol(&mut self, elem: Option<bool>) {
    Self::update_len(&mut self.len, self.enable_connect_protocol, elem);
    self.enable_connect_protocol = elem;
  }

  pub(crate) fn set_header_table_size(&mut self, elem: Option<u32>) {
    Self::update_len(&mut self.len, self.header_table_size, elem);
    self.header_table_size = elem;
  }

  pub(crate) fn set_initial_window_size(&mut self, elem: Option<U31>) {
    Self::update_len(&mut self.len, self.initial_window_size, elem);
    self.initial_window_size = elem;
  }

  pub(crate) fn set_max_concurrent_streams(&mut self, elem: Option<u32>) {
    Self::update_len(&mut self.len, self.max_concurrent_streams, elem);
    self.max_concurrent_streams = elem;
  }

  pub(crate) fn set_max_frame_size(&mut self, elem: Option<u32>) {
    Self::update_len(&mut self.len, self.max_frame_size, elem);
    self.max_frame_size =
      elem.map(|val| val.clamp(MAX_FRAME_LEN_LOWER_BOUND, MAX_FRAME_LEN_UPPER_BOUND));
  }

  pub(crate) fn set_max_header_list_size(&mut self, elem: Option<u32>) {
    Self::update_len(&mut self.len, self.max_header_list_size, elem);
    self.max_header_list_size = elem;
  }

  fn update_len<T>(len: &mut u8, a: Option<T>, b: Option<T>) {
    match (a, b) {
      (None, Some(_)) => {
        *len = len.wrapping_add(6);
      }
      (Some(_), None) => {
        *len = len.wrapping_sub(6);
      }
      _ => {}
    }
  }
}

#[derive(Debug)]
enum Setting {
  EnableConnectProtocol(bool),
  HeaderTableSize(u32),
  InitialWindowSize(u32),
  MaxConcurrentStreams(u32),
  MaxFrameSize(u32),
  MaxHeaderListSize(u32),
}

impl Setting {
  pub(crate) fn new(array: [u8; 6]) -> crate::Result<Setting> {
    let [a, b, c, d, e, f] = array;
    Setting::from_id((u16::from(a) << 8) | u16::from(b), u32::from_be_bytes([c, d, e, f]))
  }

  pub(crate) const fn from_id(id: u16, value: u32) -> crate::Result<Setting> {
    Ok(match id {
      1 => Self::HeaderTableSize(value),
      3 => Self::MaxConcurrentStreams(value),
      4 => Self::InitialWindowSize(value),
      5 => Self::MaxFrameSize(value),
      6 => Self::MaxHeaderListSize(value),
      8 => Self::EnableConnectProtocol(value != 0),
      _ => return Err(protocol_err(Http2Error::UnknownSettingFrameTy)),
    })
  }
}