Skip to main content

braid_http/protocol/
braid_state.rs

1//! Braid protocol state extracted from HTTP request headers.
2
3use crate::protocol as protocol;
4use crate::protocol::constants::headers;
5use crate::types::Version;
6use std::collections::BTreeMap;
7
8/// Braid protocol state extracted from HTTP request headers.
9#[derive(Clone, Debug)]
10pub struct BraidState {
11    /// Whether client explicitly requested subscription via `Subscribe: true`
12    pub subscribe: bool,
13
14    /// Parsed `Version` header
15    pub version: Option<Vec<Version>>,
16
17    /// Parsed `Parents` header
18    pub parents: Option<Vec<Version>>,
19
20    /// `Peer` identifier
21    pub peer: Option<String>,
22
23    /// Heartbeat interval in seconds
24    pub heartbeat: Option<u64>,
25
26    /// Merge type strategy
27    pub merge_type: Option<String>,
28
29    /// `Content-Range` specification
30    pub content_range: Option<String>,
31
32    /// `Multiplex-Through` path
33    pub multiplex_through: Option<String>,
34
35    /// Acknowledged versions
36    pub ack: Option<Vec<Version>>,
37
38    /// Complete HTTP headers map
39    pub headers: BTreeMap<String, String>,
40}
41
42impl BraidState {
43    /// Parse and create BraidState from HTTP request headers.
44    #[must_use]
45    pub fn from_headers(headers: &http::HeaderMap) -> Self {
46        let mut braid_state = BraidState {
47            subscribe: false,
48            version: None,
49            parents: None,
50            peer: None,
51            heartbeat: None,
52            merge_type: None,
53            content_range: None,
54            multiplex_through: None,
55            ack: None,
56            headers: BTreeMap::new(),
57        };
58
59        for (name, value) in headers.iter() {
60            if let Ok(value_str) = value.to_str() {
61                let name_lower = name.to_string().to_lowercase();
62                braid_state
63                    .headers
64                    .insert(name_lower.clone(), value_str.to_string());
65
66                if name_lower == headers::SUBSCRIBE.as_str() {
67                    braid_state.subscribe = value_str.to_lowercase() == "true";
68                } else if name_lower == headers::VERSION.as_str() {
69                    braid_state.version = protocol::parse_version_header(value_str).ok();
70                } else if name_lower == headers::PARENTS.as_str() {
71                    braid_state.parents = protocol::parse_version_header(value_str).ok();
72                } else if name_lower == headers::PEER.as_str() {
73                    braid_state.peer = Some(value_str.to_string());
74                } else if name_lower == headers::HEARTBEATS.as_str() {
75                    braid_state.heartbeat = protocol::parse_heartbeat(value_str).ok();
76                } else if name_lower == headers::MERGE_TYPE.as_str() {
77                    braid_state.merge_type = Some(value_str.to_string());
78                } else if name_lower == headers::CONTENT_RANGE.as_str() {
79                    braid_state.content_range = Some(value_str.to_string());
80                } else if name_lower == headers::MULTIPLEX_THROUGH.as_str() {
81                    braid_state.multiplex_through = Some(value_str.to_string());
82                } else if name_lower == "ack" {
83                    braid_state.ack = protocol::parse_version_header(value_str).ok();
84                }
85            }
86        }
87
88        braid_state
89    }
90}