aboutsummaryrefslogtreecommitdiff
path: root/src/encoding/headers.rs
blob: 6bffc7e341834016faba9ece69477dc3fdff3ed1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
use super::{headermap::HeaderMap, method::Method};
use anyhow::{anyhow, bail};
use std::{fmt::Display, str::FromStr};

macro_rules! header {
    ($hname:literal, struct $name:ident($type:ty)) => {
        #[derive(Debug)]
        pub struct $name(pub $type);
        impl Header for $name {
            const NAME: &'static str = $hname;
        }
        impl Display for $name {
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                write!(f, "{}", self.0)
            }
        }
        impl FromStr for $name {
            type Err = anyhow::Error;
            fn from_str(s: &str) -> Result<Self, Self::Err> {
                Ok($name(<$type>::from_str(s)?))
            }
        }
    };
}

pub trait Header: FromStr<Err = anyhow::Error> + Display {
    const NAME: &'static str;
}

header!("Content-Length", struct ContentLength(usize));
header!("Call-ID", struct CallID(String));
header!("Via", struct Via(String));
header!("Contact", struct Contact(String));
header!("Max-Forwards", struct MaxForwards(usize));
header!("From", struct From(String));
header!("To", struct To(String));
header!("User-Agent", struct UserAgent(String));
header!("Allow", struct Allow(String));

#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct CSeq(pub u32, pub Method);

impl Header for CSeq {
    const NAME: &'static str = "CSeq";
}
impl Display for CSeq {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{} {}", self.0, self.1)
    }
}
impl FromStr for CSeq {
    type Err = anyhow::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let (seq, method) = s.split_once(" ").ok_or(anyhow!("method missing"))?;
        Ok(CSeq(seq.parse()?, method.parse()?))
    }
}

#[derive(Debug)]
pub struct WWWAuthenticate {
    pub realm: String,
    pub nonce: String,
}
impl Header for WWWAuthenticate {
    const NAME: &'static str = "WWW-Authenticate";
}
impl Display for WWWAuthenticate {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Digest realm={:?}, nonce={:?}", self.realm, self.nonce)
    }
}
impl FromStr for WWWAuthenticate {
    type Err = anyhow::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        // TODO this is totally wrong
        let kvs = s
            .strip_prefix("Digest ")
            .ok_or(anyhow!("type not digest"))?
            .split(", ")
            .map(|e| {
                let Some((k, v)) = e.split_once("=") else {
                    bail!("not a KV-pair")
                };
                Ok((
                    k.to_string(),
                    v.strip_prefix("\"")
                        .ok_or(anyhow!("start quote missing"))?
                        .strip_suffix("\"")
                        .ok_or(anyhow!("end quote missing"))?
                        .to_string(),
                ))
            })
            .try_collect::<HeaderMap>()?;
        Ok(WWWAuthenticate {
            realm: kvs
                .get_raw("realm")
                .ok_or(anyhow!("realm missing"))?
                .to_string(),
            nonce: kvs
                .get_raw("nonce")
                .ok_or(anyhow!("nonce missing"))?
                .to_string(),
        })
    }
}