1use std::str::FromStr;
2
3use super::Error;
4
5#[derive(Clone)]
7pub struct Header {
8 pub status: StatusCode,
10 pub meta: String,
13}
14
15#[derive(Clone, Copy)]
17pub enum StatusCode {
18 Input(InputCode),
19 Success,
20 Redirect(RedirectCode),
21 FailTemporary(FailTemporaryCode),
22 FailPermanent(FailPermanentCode),
23 CertFail(CertFailCode),
24}
25
26#[derive(Clone, Copy)]
28pub enum InputCode {
29 Input,
30 Sensitive,
31}
32
33#[derive(Clone, Copy)]
35pub enum RedirectCode {
36 Temporary,
37 Permanent,
38}
39
40#[derive(Clone, Copy)]
42pub enum FailTemporaryCode {
43 Temporary,
44 ServerUnavailable,
45 CGIError,
46 ProxyError,
47 SlowDown,
48}
49#[derive(Clone, Copy)]
51pub enum FailPermanentCode {
52 Permanent,
53 NotFound,
54 Gone,
55 ProxyRefused,
56 BadRequest,
57}
58
59#[derive(Clone, Copy)]
61pub enum CertFailCode {
62 CertRequired,
63 CertNotAuthorized,
64 CertNotValid,
65}
66
67impl TryFrom<String> for Header {
68 type Error = Error;
69
70 fn try_from(header: String) -> Result<Self, Error> {
71 let space_index = 2;
79 let (status, meta) = if header.chars().nth(space_index).unwrap() == ' ' {
81 (&header[0..2], &header[3..])
83 } else {
84 return Err(Error::HeaderFormat(format!(
85 "Missing space after status, provided header: {}",
86 header
87 )));
88 };
89
90 if status.len() != 2 {
92 return Err(Error::HeaderFormat(format!(
93 "The status must be exactly two integers, provided header: {}",
94 header
95 )));
96 }
97 if !meta.ends_with("\r\n") {
99 return Err(Error::HeaderFormat(String::from(
100 "Meta information for the header doesn't end in <CR><LF>",
101 )));
102 }
103
104 let meta = &meta[0..meta.len() - 2];
106 if meta.len() > 1024 {
108 return Err(Error::HeaderFormat(format!(
109 "The header's meta info was too long, maximum length is 1024 bytes, actual
110 length was {} bytes",
111 meta.len()
112 )));
113 }
114
115 let status = StatusCode::from_str(status)?;
116
117 Ok(Header {
118 status,
119 meta: meta.to_string(),
120 })
121 }
122}
123
124impl StatusCode {
125 pub fn to_str(&self) -> &'static str {
126 match self {
127 StatusCode::Input(InputCode::Input) => "10",
128 StatusCode::Input(InputCode::Sensitive) => "11",
129 StatusCode::Success => "20",
130 StatusCode::Redirect(RedirectCode::Temporary) => "30",
131 StatusCode::Redirect(RedirectCode::Permanent) => "31",
132 StatusCode::FailTemporary(FailTemporaryCode::Temporary) => "40",
133 StatusCode::FailTemporary(FailTemporaryCode::ServerUnavailable) => "41",
134 StatusCode::FailTemporary(FailTemporaryCode::CGIError) => "42",
135 StatusCode::FailTemporary(FailTemporaryCode::ProxyError) => "43",
136 StatusCode::FailTemporary(FailTemporaryCode::SlowDown) => "44",
137 StatusCode::FailPermanent(FailPermanentCode::Permanent) => "50",
138 StatusCode::FailPermanent(FailPermanentCode::NotFound) => "51",
139 StatusCode::FailPermanent(FailPermanentCode::Gone) => "52",
140 StatusCode::FailPermanent(FailPermanentCode::ProxyRefused) => "53",
141 StatusCode::FailPermanent(FailPermanentCode::BadRequest) => "59",
142 StatusCode::CertFail(CertFailCode::CertRequired) => "60",
143 StatusCode::CertFail(CertFailCode::CertNotAuthorized) => "61",
144 StatusCode::CertFail(CertFailCode::CertNotValid) => "62",
145 }
146 }
147}
148
149impl FromStr for StatusCode {
150 type Err = Error;
151
152 fn from_str(s: &str) -> Result<Self, Self::Err> {
158 Ok(match s {
159 "10" => StatusCode::Input(InputCode::Input),
160 "11" => StatusCode::Input(InputCode::Sensitive),
161 "20" => StatusCode::Success,
162 "30" => StatusCode::Redirect(RedirectCode::Temporary),
163 "31" => StatusCode::Redirect(RedirectCode::Permanent),
164 "40" => StatusCode::FailTemporary(FailTemporaryCode::Temporary),
165 "41" => StatusCode::FailTemporary(FailTemporaryCode::ServerUnavailable),
166 "42" => StatusCode::FailTemporary(FailTemporaryCode::CGIError),
167 "43" => StatusCode::FailTemporary(FailTemporaryCode::ProxyError),
168 "44" => StatusCode::FailTemporary(FailTemporaryCode::SlowDown),
169 "50" => StatusCode::FailPermanent(FailPermanentCode::Permanent),
170 "51" => StatusCode::FailPermanent(FailPermanentCode::NotFound),
171 "52" => StatusCode::FailPermanent(FailPermanentCode::Gone),
172 "53" => StatusCode::FailPermanent(FailPermanentCode::ProxyRefused),
173 "59" => StatusCode::FailPermanent(FailPermanentCode::BadRequest),
174 "60" => StatusCode::CertFail(CertFailCode::CertRequired),
175 "61" => StatusCode::CertFail(CertFailCode::CertNotAuthorized),
176 "62" => StatusCode::CertFail(CertFailCode::CertNotValid),
177 _ => {
178 return Err(Error::HeaderFormat(format!(
179 "Header status code ({}) was not recognized",
180 s
181 )))
182 }
183 })
184 }
185}
186
187impl std::fmt::Display for StatusCode {
188 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
189 write!(f, "{}", self.to_str())
190 }
191}
192
193impl std::fmt::Display for InputCode {
194 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
195 write!(f, "{}", StatusCode::Input(*self))
196 }
197}
198
199impl std::fmt::Display for RedirectCode {
200 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
201 write!(f, "{}", StatusCode::Redirect(*self))
202 }
203}
204
205impl std::fmt::Display for FailTemporaryCode {
206 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
207 write!(f, "{}", StatusCode::FailTemporary(*self))
208 }
209}
210
211impl std::fmt::Display for FailPermanentCode {
212 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
213 write!(f, "{}", StatusCode::FailPermanent(*self))
214 }
215}
216
217impl std::fmt::Display for CertFailCode {
218 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
219 write!(f, "{}", StatusCode::CertFail(*self))
220 }
221}
222
223impl std::fmt::Display for Header {
224 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
225 write!(f, "{}: {}", self.status, self.meta)
226 }
227}