A pit full of rusty nails
at main 84 lines 2.4 kB view raw
1use std::sync::Arc; 2 3use hashbrown::HashMap; 4use hyper::{HeaderMap, body::Bytes, header::ACCEPT_ENCODING}; 5use nailbox::try_arc_within; 6use nailconfig::{DropBehavior, NailConfig, RateLimitingConfig}; 7use rapidhash::fast::RandomState; 8 9pub type SpicyPayloads = HashMap<SpicyPayloadKind, Bytes, RandomState>; 10 11static GZIP: &str = "gzip"; 12static BROTLI: &str = "br"; 13 14#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] 15#[repr(u32)] 16pub enum SpicyPayloadKind { 17 Gz, 18 Brotli, 19} 20 21impl SpicyPayloadKind { 22 pub fn as_str(&self) -> &'static str { 23 match self { 24 SpicyPayloadKind::Gz => GZIP, 25 SpicyPayloadKind::Brotli => BROTLI, 26 } 27 } 28} 29 30impl SpicyPayloadKind { 31 fn file_kind(file: impl AsRef<str>) -> Option<SpicyPayloadKind> { 32 let file = file.as_ref(); 33 34 if file.ends_with(".gz") { 35 Some(Self::Gz) 36 } else if file.ends_with(".br") { 37 Some(Self::Brotli) 38 } else { 39 None 40 } 41 } 42 43 pub fn accepts_encoding(header: &HeaderMap) -> Option<SpicyPayloadKind> { 44 header 45 .get(ACCEPT_ENCODING) 46 .and_then(|header| header.to_str().ok()) 47 .and_then(|header| { 48 header 49 .contains(BROTLI) 50 .then_some(Self::Brotli) 51 .or_else(|| header.contains(GZIP).then_some(Self::Gz)) 52 }) 53 } 54} 55 56pub fn get_spicy_payload(config: &NailConfig) -> Option<Arc<SpicyPayloads>> { 57 match &config.rate_limiting { 58 RateLimitingConfig::HardLimit { 59 drop_behavior: DropBehavior::Spicy { payload }, 60 .. 61 } 62 | RateLimitingConfig::SoftWithHardLimit { 63 drop_behavior: DropBehavior::Spicy { payload }, 64 .. 65 } => try_arc_within(|| { 66 payload 67 .iter() 68 .filter_map(|file| SpicyPayloadKind::file_kind(file).map(|kind| (kind, file))) 69 .map(|(kind, file)| { 70 Ok(( 71 kind, 72 std::fs::read(file) 73 .inspect_err(|err| { 74 tracing::error!("Failed to load spicy payload: {err}") 75 }) 76 .map(Bytes::from)?, 77 )) 78 }) 79 .collect::<std::io::Result<_>>() 80 }) 81 .ok(), 82 _ => None, 83 } 84}