const SNIFF_LIMIT: usize = 512; fn is_ws(byte: u8) -> bool { matches!(byte, b'\t' | b'\n' | 0x0c | b'\r' | b' ') } fn is_tt(byte: u8) -> bool { matches!(byte, b' ' | b'>') } enum Sig { Exact(&'static [u8], &'static str), Masked { mask: &'static [u8], pat: &'static [u8], skip_ws: bool, ct: &'static str, }, Html(&'static [u8]), Mp4, Text, } impl Sig { fn detect(&self, data: &[u8], first_non_ws: usize) -> Option<&'static str> { match self { Sig::Exact(sig, ct) => data.starts_with(sig).then_some(*ct), Sig::Masked { mask, pat, skip_ws, ct, } => { let data = if *skip_ws { &data[first_non_ws..] } else { data }; (mask.len() == pat.len() && data.len() >= pat.len() && pat .iter() .zip(mask.iter()) .enumerate() .all(|(index, (byte, mask))| data[index] & mask == *byte)) .then_some(*ct) } Sig::Html(tag) => { let data = &data[first_non_ws..]; (data.len() > tag.len() && tag.iter().enumerate().all(|(index, byte)| { let candidate = data[index]; let candidate = match byte.is_ascii_uppercase() { true => candidate & 0xDF, false => candidate, }; *byte == candidate }) && is_tt(data[tag.len()])) .then_some("text/html; charset=utf-8") } Sig::Mp4 => mp4(data), Sig::Text => text(data, first_non_ws), } } } fn mp4(data: &[u8]) -> Option<&'static str> { if data.len() < 12 { return None; } let box_size = u32::from_be_bytes([data[0], data[1], data[2], data[3]]) as usize; if data.len() < box_size || !box_size.is_multiple_of(4) || &data[4..8] != b"ftyp" { return None; } (8..box_size) .step_by(4) .filter(|start| *start != 12) .any(|start| &data[start..start + 3] == b"mp4") .then_some("video/mp4") } fn text(data: &[u8], first_non_ws: usize) -> Option<&'static str> { data[first_non_ws..] .iter() .all(|byte| !matches!(byte, 0x00..=0x08 | 0x0b | 0x0e..=0x1a | 0x1c..=0x1f)) .then_some("text/plain; charset=utf-8") } const SIGNATURES: &[Sig] = &[ Sig::Html(b" &'static str { let data = &content[..content.len().min(SNIFF_LIMIT)]; let first_non_ws = data .iter() .position(|byte| !is_ws(*byte)) .unwrap_or(data.len()); SIGNATURES .iter() .find_map(|sig| sig.detect(data, first_non_ws)) .unwrap_or("application/octet-stream") } pub(crate) fn override_by_extension(path: &str, detected: &'static str) -> &'static str { let extension = path.rsplit_once('.').map(|(_, ext)| ext).unwrap_or(""); match extension.to_ascii_lowercase().as_str() { "svg" => "image/svg+xml", "avif" => "image/avif", "jxl" => "image/jxl", "heic" | "heif" => "image/heif", _ => detected, } } pub(crate) fn is_textual_mime(mime: &str) -> bool { mime.starts_with("text/") || matches!( mime, "application/json" | "application/xml" | "application/yaml" | "application/x-yaml" | "application/toml" | "application/javascript" | "application/ecmascript" ) } #[cfg(test)] mod tests { use super::detect_content_type; #[test] fn detects_common_content_signatures() { let cases: &[(&[u8], &str)] = &[ (b" \n", "text/html; charset=utf-8"), (b"
", "text/html; charset=utf-8"), (b"", "text/html; charset=utf-8"), (b"\n\t", "text/xml; charset=utf-8"), (b"\xfe\xff\x00h", "text/plain; charset=utf-16be"), (b"\xef\xbb\xbfhello", "text/plain; charset=utf-8"), (b"\x89PNG\x0d\x0a\x1a\x0a", "image/png"), (b"GIF89a", "image/gif"), (b"fn main() {}\n", "text/plain; charset=utf-8"), (b"\x00\x01\x02\x03", "application/octet-stream"), (b"", "text/plain; charset=utf-8"), ]; cases.iter().for_each(|(input, expected)| { assert_eq!(detect_content_type(input), *expected); }); } }