//! HTTP Request and response header handling. use http::header::{HeaderMap, HeaderName, HeaderValue}; use httparse::Status; use super::machine::TryParse; use crate::error::Result; /// Limit for the number of header lines. pub const MAX_HEADERS: usize = 124; /// Trait to convert raw objects into HTTP parseables. pub(crate) trait FromHttparse: Sized { /// Convert raw object into parsed HTTP headers. fn from_httparse(raw: T) -> Result; } impl<'b: 'h, 'h> FromHttparse<&'b [httparse::Header<'h>]> for HeaderMap { fn from_httparse(raw: &'b [httparse::Header<'h>]) -> Result { let mut headers = HeaderMap::new(); for h in raw { headers.append( HeaderName::from_bytes(h.name.as_bytes())?, HeaderValue::from_bytes(h.value)?, ); } Ok(headers) } } impl TryParse for HeaderMap { fn try_parse(buf: &[u8]) -> Result> { let mut hbuffer = [httparse::EMPTY_HEADER; MAX_HEADERS]; Ok(match httparse::parse_headers(buf, &mut hbuffer)? { Status::Partial => None, Status::Complete((size, hdr)) => Some((size, HeaderMap::from_httparse(hdr)?)), }) } } #[cfg(test)] mod tests { use super::{super::machine::TryParse, HeaderMap}; #[test] fn headers() { const DATA: &[u8] = b"Host: foo.com\r\n\ Connection: Upgrade\r\n\ Upgrade: websocket\r\n\ \r\n"; let (_, hdr) = HeaderMap::try_parse(DATA).unwrap().unwrap(); assert_eq!(hdr.get("Host").unwrap(), &b"foo.com"[..]); assert_eq!(hdr.get("Upgrade").unwrap(), &b"websocket"[..]); assert_eq!(hdr.get("Connection").unwrap(), &b"Upgrade"[..]); } #[test] fn headers_iter() { const DATA: &[u8] = b"Host: foo.com\r\n\ Sec-WebSocket-Extensions: permessage-deflate\r\n\ Connection: Upgrade\r\n\ Sec-WebSocket-ExtenSIONS: permessage-unknown\r\n\ Upgrade: websocket\r\n\ \r\n"; let (_, hdr) = HeaderMap::try_parse(DATA).unwrap().unwrap(); let mut iter = hdr.get_all("Sec-WebSocket-Extensions").iter(); assert_eq!(iter.next().unwrap(), &b"permessage-deflate"[..]); assert_eq!(iter.next().unwrap(), &b"permessage-unknown"[..]); assert_eq!(iter.next(), None); } #[test] fn headers_incomplete() { const DATA: &[u8] = b"Host: foo.com\r\n\ Connection: Upgrade\r\n\ Upgrade: websocket\r\n"; let hdr = HeaderMap::try_parse(DATA).unwrap(); assert!(hdr.is_none()); } }