irox_networking/http/
headers.rs1use irox_tools::options::MaybeMap;
6use irox_tools::read::Buffer;
7use irox_tools::scanner::{LineEnding, ReadToken, Scanner};
8use log::debug;
9use std::collections::BTreeMap;
10use std::io::{BufReader, Read, Write};
11
12#[derive(Default, Debug, Clone)]
13pub struct HttpHeaders {
14 pub(crate) headers: BTreeMap<String, Vec<String>>,
15}
16
17pub struct HeaderResponse<T> {
18 pub headers: HttpHeaders,
19 pub remaining: Buffer<BufReader<T>>,
20}
21
22impl HttpHeaders {
23 pub fn new_empty() -> Self {
24 HttpHeaders::default()
25 }
26 pub fn new_request() -> Self {
27 let mut resp = HttpHeaders::new_empty();
28 resp.add_header("Accept", "*/*");
29 resp.add_header("Connection", "close");
30 resp
31 }
32 pub fn add_header<K: AsRef<str>, V: AsRef<str>>(&mut self, key: K, value: V) {
33 let key = key.as_ref().to_string();
34 let val = value.as_ref().to_string();
35 self.headers.entry(key).or_default().push(val);
36 }
37
38 pub fn get_header<K: AsRef<str>>(&self, key: K) -> Option<&String> {
39 let key = key.as_ref().to_string();
40 self.headers.get(&key).maybe_map(|v| v.first())
41 }
42
43 pub fn get_headers<K: AsRef<str>>(&self, key: K) -> Option<&Vec<String>> {
44 let key = key.as_ref().to_string();
45 self.headers.get(&key)
46 }
47 pub fn headers(&self) -> &BTreeMap<String, Vec<String>> {
48 &self.headers
49 }
50 pub fn contains_header<K: AsRef<str>>(&self, key: K) -> bool {
51 self.headers.contains_key(key.as_ref())
52 }
53 pub fn maybe_add<K: AsRef<str>, V: AsRef<str>>(&mut self, key: K, val: V) {
54 let key = key.as_ref();
55 if !self.contains_header(key) {
56 self.add_header(key, val);
57 }
58 }
59
60 pub fn write_to<T: Write>(&self, out: &mut T) -> Result<(), std::io::Error> {
61 for (name, value) in &self.headers {
62 let vals = value.join(" ");
63 let hdr = format!("{name}: {vals}");
64 debug!("{hdr}");
65 write!(out, "{hdr}\r\n")?;
66 }
67 debug!("");
68 write!(out, "\r\n")?;
69 Ok(())
70 }
71
72 pub fn add_line(&mut self, val: &[u8]) {
73 let val = String::from_utf8_lossy(val);
74 if let Some(idx) = val.find(':') {
75 let (name, val) = val.split_at(idx);
76 let val = val.get(2..).unwrap_or_default();
77 self.add_header(name, val);
78 }
79 }
80
81 pub fn create_from<T: Read + Sized>(input: T) -> Result<HeaderResponse<T>, std::io::Error> {
82 let mut headers = HttpHeaders::new_empty();
83 let mut scanner = Scanner::new_crlf(input);
84 loop {
85 let next = scanner.read_next()?;
86 match next {
87 ReadToken::Found { data, token } => {
88 if *token.get_response() != LineEnding::CarriageReturnLineFeed {
89 return Err(std::io::ErrorKind::InvalidData.into());
90 }
91 if data.is_empty() {
92 break;
93 }
94 headers.add_line(&data);
95 }
96 ReadToken::EndOfData { data } => {
97 headers.add_line(&data);
98 }
99 ReadToken::NotFound => {}
100 }
101 }
102 Ok(HeaderResponse {
103 headers,
104 remaining: scanner.take_back(),
105 })
106 }
107}