| 1 | use std::fmt; |
| 2 | |
| 3 | use crate::frame::{util, Error, Frame, FrameSize, Head, Kind, StreamId}; |
| 4 | use bytes::{BufMut, BytesMut}; |
| 5 | |
| 6 | #[derive (Clone, Default, Eq, PartialEq)] |
| 7 | pub struct Settings { |
| 8 | flags: SettingsFlags, |
| 9 | // Fields |
| 10 | header_table_size: Option<u32>, |
| 11 | enable_push: Option<u32>, |
| 12 | max_concurrent_streams: Option<u32>, |
| 13 | initial_window_size: Option<u32>, |
| 14 | max_frame_size: Option<u32>, |
| 15 | max_header_list_size: Option<u32>, |
| 16 | enable_connect_protocol: Option<u32>, |
| 17 | } |
| 18 | |
| 19 | /// An enum that lists all valid settings that can be sent in a SETTINGS |
| 20 | /// frame. |
| 21 | /// |
| 22 | /// Each setting has a value that is a 32 bit unsigned integer (6.5.1.). |
| 23 | #[derive (Debug)] |
| 24 | pub enum Setting { |
| 25 | HeaderTableSize(u32), |
| 26 | EnablePush(u32), |
| 27 | MaxConcurrentStreams(u32), |
| 28 | InitialWindowSize(u32), |
| 29 | MaxFrameSize(u32), |
| 30 | MaxHeaderListSize(u32), |
| 31 | EnableConnectProtocol(u32), |
| 32 | } |
| 33 | |
| 34 | #[derive (Copy, Clone, Eq, PartialEq, Default)] |
| 35 | pub struct SettingsFlags(u8); |
| 36 | |
| 37 | const ACK: u8 = 0x1; |
| 38 | const ALL: u8 = ACK; |
| 39 | |
| 40 | /// The default value of SETTINGS_HEADER_TABLE_SIZE |
| 41 | pub const DEFAULT_SETTINGS_HEADER_TABLE_SIZE: usize = 4_096; |
| 42 | |
| 43 | /// The default value of SETTINGS_INITIAL_WINDOW_SIZE |
| 44 | pub const DEFAULT_INITIAL_WINDOW_SIZE: u32 = 65_535; |
| 45 | |
| 46 | /// The default value of MAX_FRAME_SIZE |
| 47 | pub const DEFAULT_MAX_FRAME_SIZE: FrameSize = 16_384; |
| 48 | |
| 49 | /// INITIAL_WINDOW_SIZE upper bound |
| 50 | pub const MAX_INITIAL_WINDOW_SIZE: usize = (1 << 31) - 1; |
| 51 | |
| 52 | /// MAX_FRAME_SIZE upper bound |
| 53 | pub const MAX_MAX_FRAME_SIZE: FrameSize = (1 << 24) - 1; |
| 54 | |
| 55 | // ===== impl Settings ===== |
| 56 | |
| 57 | impl Settings { |
| 58 | pub fn ack() -> Settings { |
| 59 | Settings { |
| 60 | flags: SettingsFlags::ack(), |
| 61 | ..Settings::default() |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | pub fn is_ack(&self) -> bool { |
| 66 | self.flags.is_ack() |
| 67 | } |
| 68 | |
| 69 | pub fn initial_window_size(&self) -> Option<u32> { |
| 70 | self.initial_window_size |
| 71 | } |
| 72 | |
| 73 | pub fn set_initial_window_size(&mut self, size: Option<u32>) { |
| 74 | self.initial_window_size = size; |
| 75 | } |
| 76 | |
| 77 | pub fn max_concurrent_streams(&self) -> Option<u32> { |
| 78 | self.max_concurrent_streams |
| 79 | } |
| 80 | |
| 81 | pub fn set_max_concurrent_streams(&mut self, max: Option<u32>) { |
| 82 | self.max_concurrent_streams = max; |
| 83 | } |
| 84 | |
| 85 | pub fn max_frame_size(&self) -> Option<u32> { |
| 86 | self.max_frame_size |
| 87 | } |
| 88 | |
| 89 | pub fn set_max_frame_size(&mut self, size: Option<u32>) { |
| 90 | if let Some(val) = size { |
| 91 | assert!(DEFAULT_MAX_FRAME_SIZE <= val && val <= MAX_MAX_FRAME_SIZE); |
| 92 | } |
| 93 | self.max_frame_size = size; |
| 94 | } |
| 95 | |
| 96 | pub fn max_header_list_size(&self) -> Option<u32> { |
| 97 | self.max_header_list_size |
| 98 | } |
| 99 | |
| 100 | pub fn set_max_header_list_size(&mut self, size: Option<u32>) { |
| 101 | self.max_header_list_size = size; |
| 102 | } |
| 103 | |
| 104 | pub fn is_push_enabled(&self) -> Option<bool> { |
| 105 | self.enable_push.map(|val| val != 0) |
| 106 | } |
| 107 | |
| 108 | pub fn set_enable_push(&mut self, enable: bool) { |
| 109 | self.enable_push = Some(enable as u32); |
| 110 | } |
| 111 | |
| 112 | pub fn is_extended_connect_protocol_enabled(&self) -> Option<bool> { |
| 113 | self.enable_connect_protocol.map(|val| val != 0) |
| 114 | } |
| 115 | |
| 116 | pub fn set_enable_connect_protocol(&mut self, val: Option<u32>) { |
| 117 | self.enable_connect_protocol = val; |
| 118 | } |
| 119 | |
| 120 | pub fn header_table_size(&self) -> Option<u32> { |
| 121 | self.header_table_size |
| 122 | } |
| 123 | |
| 124 | pub fn set_header_table_size(&mut self, size: Option<u32>) { |
| 125 | self.header_table_size = size; |
| 126 | } |
| 127 | |
| 128 | pub fn load(head: Head, payload: &[u8]) -> Result<Settings, Error> { |
| 129 | use self::Setting::*; |
| 130 | |
| 131 | debug_assert_eq!(head.kind(), crate::frame::Kind::Settings); |
| 132 | |
| 133 | if !head.stream_id().is_zero() { |
| 134 | return Err(Error::InvalidStreamId); |
| 135 | } |
| 136 | |
| 137 | // Load the flag |
| 138 | let flag = SettingsFlags::load(head.flag()); |
| 139 | |
| 140 | if flag.is_ack() { |
| 141 | // Ensure that the payload is empty |
| 142 | if !payload.is_empty() { |
| 143 | return Err(Error::InvalidPayloadLength); |
| 144 | } |
| 145 | |
| 146 | // Return the ACK frame |
| 147 | return Ok(Settings::ack()); |
| 148 | } |
| 149 | |
| 150 | // Ensure the payload length is correct, each setting is 6 bytes long. |
| 151 | if payload.len() % 6 != 0 { |
| 152 | tracing::debug!("invalid settings payload length; len= {:?}" , payload.len()); |
| 153 | return Err(Error::InvalidPayloadAckSettings); |
| 154 | } |
| 155 | |
| 156 | let mut settings = Settings::default(); |
| 157 | debug_assert!(!settings.flags.is_ack()); |
| 158 | |
| 159 | for raw in payload.chunks(6) { |
| 160 | match Setting::load(raw) { |
| 161 | Some(HeaderTableSize(val)) => { |
| 162 | settings.header_table_size = Some(val); |
| 163 | } |
| 164 | Some(EnablePush(val)) => match val { |
| 165 | 0 | 1 => { |
| 166 | settings.enable_push = Some(val); |
| 167 | } |
| 168 | _ => { |
| 169 | return Err(Error::InvalidSettingValue); |
| 170 | } |
| 171 | }, |
| 172 | Some(MaxConcurrentStreams(val)) => { |
| 173 | settings.max_concurrent_streams = Some(val); |
| 174 | } |
| 175 | Some(InitialWindowSize(val)) => { |
| 176 | if val as usize > MAX_INITIAL_WINDOW_SIZE { |
| 177 | return Err(Error::InvalidSettingValue); |
| 178 | } else { |
| 179 | settings.initial_window_size = Some(val); |
| 180 | } |
| 181 | } |
| 182 | Some(MaxFrameSize(val)) => { |
| 183 | if DEFAULT_MAX_FRAME_SIZE <= val && val <= MAX_MAX_FRAME_SIZE { |
| 184 | settings.max_frame_size = Some(val); |
| 185 | } else { |
| 186 | return Err(Error::InvalidSettingValue); |
| 187 | } |
| 188 | } |
| 189 | Some(MaxHeaderListSize(val)) => { |
| 190 | settings.max_header_list_size = Some(val); |
| 191 | } |
| 192 | Some(EnableConnectProtocol(val)) => match val { |
| 193 | 0 | 1 => { |
| 194 | settings.enable_connect_protocol = Some(val); |
| 195 | } |
| 196 | _ => { |
| 197 | return Err(Error::InvalidSettingValue); |
| 198 | } |
| 199 | }, |
| 200 | None => {} |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | Ok(settings) |
| 205 | } |
| 206 | |
| 207 | fn payload_len(&self) -> usize { |
| 208 | let mut len = 0; |
| 209 | self.for_each(|_| len += 6); |
| 210 | len |
| 211 | } |
| 212 | |
| 213 | pub fn encode(&self, dst: &mut BytesMut) { |
| 214 | // Create & encode an appropriate frame head |
| 215 | let head = Head::new(Kind::Settings, self.flags.into(), StreamId::zero()); |
| 216 | let payload_len = self.payload_len(); |
| 217 | |
| 218 | tracing::trace!("encoding SETTINGS; len= {}" , payload_len); |
| 219 | |
| 220 | head.encode(payload_len, dst); |
| 221 | |
| 222 | // Encode the settings |
| 223 | self.for_each(|setting| { |
| 224 | tracing::trace!("encoding setting; val= {:?}" , setting); |
| 225 | setting.encode(dst) |
| 226 | }); |
| 227 | } |
| 228 | |
| 229 | fn for_each<F: FnMut(Setting)>(&self, mut f: F) { |
| 230 | use self::Setting::*; |
| 231 | |
| 232 | if let Some(v) = self.header_table_size { |
| 233 | f(HeaderTableSize(v)); |
| 234 | } |
| 235 | |
| 236 | if let Some(v) = self.enable_push { |
| 237 | f(EnablePush(v)); |
| 238 | } |
| 239 | |
| 240 | if let Some(v) = self.max_concurrent_streams { |
| 241 | f(MaxConcurrentStreams(v)); |
| 242 | } |
| 243 | |
| 244 | if let Some(v) = self.initial_window_size { |
| 245 | f(InitialWindowSize(v)); |
| 246 | } |
| 247 | |
| 248 | if let Some(v) = self.max_frame_size { |
| 249 | f(MaxFrameSize(v)); |
| 250 | } |
| 251 | |
| 252 | if let Some(v) = self.max_header_list_size { |
| 253 | f(MaxHeaderListSize(v)); |
| 254 | } |
| 255 | |
| 256 | if let Some(v) = self.enable_connect_protocol { |
| 257 | f(EnableConnectProtocol(v)); |
| 258 | } |
| 259 | } |
| 260 | } |
| 261 | |
| 262 | impl<T> From<Settings> for Frame<T> { |
| 263 | fn from(src: Settings) -> Frame<T> { |
| 264 | Frame::Settings(src) |
| 265 | } |
| 266 | } |
| 267 | |
| 268 | impl fmt::Debug for Settings { |
| 269 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 270 | let mut builder = f.debug_struct("Settings" ); |
| 271 | builder.field("flags" , &self.flags); |
| 272 | |
| 273 | self.for_each(|setting| match setting { |
| 274 | Setting::EnablePush(v) => { |
| 275 | builder.field("enable_push" , &v); |
| 276 | } |
| 277 | Setting::HeaderTableSize(v) => { |
| 278 | builder.field("header_table_size" , &v); |
| 279 | } |
| 280 | Setting::InitialWindowSize(v) => { |
| 281 | builder.field("initial_window_size" , &v); |
| 282 | } |
| 283 | Setting::MaxConcurrentStreams(v) => { |
| 284 | builder.field("max_concurrent_streams" , &v); |
| 285 | } |
| 286 | Setting::MaxFrameSize(v) => { |
| 287 | builder.field("max_frame_size" , &v); |
| 288 | } |
| 289 | Setting::MaxHeaderListSize(v) => { |
| 290 | builder.field("max_header_list_size" , &v); |
| 291 | } |
| 292 | Setting::EnableConnectProtocol(v) => { |
| 293 | builder.field("enable_connect_protocol" , &v); |
| 294 | } |
| 295 | }); |
| 296 | |
| 297 | builder.finish() |
| 298 | } |
| 299 | } |
| 300 | |
| 301 | // ===== impl Setting ===== |
| 302 | |
| 303 | impl Setting { |
| 304 | /// Creates a new `Setting` with the correct variant corresponding to the |
| 305 | /// given setting id, based on the settings IDs defined in section |
| 306 | /// 6.5.2. |
| 307 | pub fn from_id(id: u16, val: u32) -> Option<Setting> { |
| 308 | use self::Setting::*; |
| 309 | |
| 310 | match id { |
| 311 | 1 => Some(HeaderTableSize(val)), |
| 312 | 2 => Some(EnablePush(val)), |
| 313 | 3 => Some(MaxConcurrentStreams(val)), |
| 314 | 4 => Some(InitialWindowSize(val)), |
| 315 | 5 => Some(MaxFrameSize(val)), |
| 316 | 6 => Some(MaxHeaderListSize(val)), |
| 317 | 8 => Some(EnableConnectProtocol(val)), |
| 318 | _ => None, |
| 319 | } |
| 320 | } |
| 321 | |
| 322 | /// Creates a new `Setting` by parsing the given buffer of 6 bytes, which |
| 323 | /// contains the raw byte representation of the setting, according to the |
| 324 | /// "SETTINGS format" defined in section 6.5.1. |
| 325 | /// |
| 326 | /// The `raw` parameter should have length at least 6 bytes, since the |
| 327 | /// length of the raw setting is exactly 6 bytes. |
| 328 | /// |
| 329 | /// # Panics |
| 330 | /// |
| 331 | /// If given a buffer shorter than 6 bytes, the function will panic. |
| 332 | fn load(raw: &[u8]) -> Option<Setting> { |
| 333 | let id: u16 = (u16::from(raw[0]) << 8) | u16::from(raw[1]); |
| 334 | let val: u32 = unpack_octets_4!(raw, 2, u32); |
| 335 | |
| 336 | Setting::from_id(id, val) |
| 337 | } |
| 338 | |
| 339 | fn encode(&self, dst: &mut BytesMut) { |
| 340 | use self::Setting::*; |
| 341 | |
| 342 | let (kind, val) = match *self { |
| 343 | HeaderTableSize(v) => (1, v), |
| 344 | EnablePush(v) => (2, v), |
| 345 | MaxConcurrentStreams(v) => (3, v), |
| 346 | InitialWindowSize(v) => (4, v), |
| 347 | MaxFrameSize(v) => (5, v), |
| 348 | MaxHeaderListSize(v) => (6, v), |
| 349 | EnableConnectProtocol(v) => (8, v), |
| 350 | }; |
| 351 | |
| 352 | dst.put_u16(kind); |
| 353 | dst.put_u32(val); |
| 354 | } |
| 355 | } |
| 356 | |
| 357 | // ===== impl SettingsFlags ===== |
| 358 | |
| 359 | impl SettingsFlags { |
| 360 | pub fn empty() -> SettingsFlags { |
| 361 | SettingsFlags(0) |
| 362 | } |
| 363 | |
| 364 | pub fn load(bits: u8) -> SettingsFlags { |
| 365 | SettingsFlags(bits & ALL) |
| 366 | } |
| 367 | |
| 368 | pub fn ack() -> SettingsFlags { |
| 369 | SettingsFlags(ACK) |
| 370 | } |
| 371 | |
| 372 | pub fn is_ack(&self) -> bool { |
| 373 | self.0 & ACK == ACK |
| 374 | } |
| 375 | } |
| 376 | |
| 377 | impl From<SettingsFlags> for u8 { |
| 378 | fn from(src: SettingsFlags) -> u8 { |
| 379 | src.0 |
| 380 | } |
| 381 | } |
| 382 | |
| 383 | impl fmt::Debug for SettingsFlags { |
| 384 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 385 | util&mut DebugFlags<'_, '_>::debug_flags(f, self.0) |
| 386 | .flag_if(self.is_ack(), name:"ACK" ) |
| 387 | .finish() |
| 388 | } |
| 389 | } |
| 390 | |