sozu_lib/protocol/kawa_h1/
answers.rs

1use std::{
2    collections::{HashMap, VecDeque},
3    fmt,
4    rc::Rc,
5};
6
7use kawa::{
8    AsBuffer, Block, BodySize, Buffer, Chunk, Kawa, Kind, Pair, ParsingPhase, ParsingPhaseMarker,
9    StatusLine, Store, h1::NoCallbacks,
10};
11use sozu_command::proto::command::CustomHttpAnswers;
12
13use crate::{protocol::http::DefaultAnswer, sozu_command::state::ClusterId};
14
15#[derive(Clone)]
16pub struct SharedBuffer(Rc<[u8]>);
17
18impl AsBuffer for SharedBuffer {
19    fn as_buffer(&self) -> &[u8] {
20        &self.0
21    }
22
23    fn as_mut_buffer(&mut self) -> &mut [u8] {
24        panic!()
25    }
26}
27
28pub type DefaultAnswerStream = Kawa<SharedBuffer>;
29
30#[derive(thiserror::Error, Debug)]
31pub enum TemplateError {
32    #[error("invalid template type: request was found, expected response")]
33    InvalidType,
34    #[error("template seems invalid or incomplete: {0:?}")]
35    InvalidTemplate(ParsingPhase),
36    #[error("unexpected status code: {0}")]
37    InvalidStatusCode(u16),
38    #[error("streaming is not supported in templates")]
39    UnsupportedStreaming,
40    #[error("template variable {0} is not allowed in headers")]
41    NotAllowedInHeader(&'static str),
42    #[error("template variable {0} is not allowed in body")]
43    NotAllowedInBody(&'static str),
44    #[error("template variable {0} can only be used once")]
45    AlreadyConsumed(&'static str),
46}
47
48#[derive(Clone, Copy, Debug)]
49pub struct TemplateVariable {
50    name: &'static str,
51    valid_in_body: bool,
52    valid_in_header: bool,
53    typ: ReplacementType,
54}
55
56#[derive(Clone, Copy, Debug)]
57pub enum ReplacementType {
58    Variable(usize),
59    VariableOnce(usize),
60    ContentLength,
61}
62
63#[derive(Clone, Copy, Debug)]
64pub struct Replacement {
65    block_index: usize,
66    typ: ReplacementType,
67}
68
69// TODO: rename for clarity, for instance HttpAnswerTemplate
70pub struct Template {
71    kawa: DefaultAnswerStream,
72    body_replacements: Vec<Replacement>,
73    header_replacements: Vec<Replacement>,
74    /// Size of body without any variables
75    body_size: usize,
76}
77
78impl fmt::Debug for Template {
79    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80        f.debug_struct("Template")
81            .field("body_replacements", &self.body_replacements)
82            .field("header_replacements", &self.header_replacements)
83            .field("body_size", &self.body_size)
84            .finish()
85    }
86}
87
88impl Template {
89    /// sanitize the template: transform newlines \r (CR) to \r\n (CRLF)
90    fn new(
91        status: u16,
92        answer: String,
93        variables: &[TemplateVariable],
94    ) -> Result<Self, TemplateError> {
95        let mut i = 0;
96        let mut j = 0;
97        let variables = variables
98            .iter()
99            .map(|v| match v.typ {
100                ReplacementType::Variable(_) => {
101                    i += 1;
102                    TemplateVariable {
103                        typ: ReplacementType::Variable(i - 1),
104                        ..*v
105                    }
106                }
107                ReplacementType::VariableOnce(_) => {
108                    j += 1;
109                    TemplateVariable {
110                        typ: ReplacementType::VariableOnce(j - 1),
111                        ..*v
112                    }
113                }
114                ReplacementType::ContentLength => *v,
115            })
116            .collect::<Vec<_>>();
117        let answer = answer
118            .replace("\r\n", "\n")
119            .replace('\n', "\r\n")
120            .into_bytes();
121
122        let len = answer.len();
123        let mut kawa = Kawa::new(Kind::Response, Buffer::new(SharedBuffer(Rc::from(answer))));
124        kawa.storage.end = len;
125        kawa::h1::parse(&mut kawa, &mut NoCallbacks);
126        if !kawa.is_main_phase() {
127            return Err(TemplateError::InvalidTemplate(kawa.parsing_phase));
128        }
129        if let StatusLine::Response { code, .. } = kawa.detached.status_line {
130            if code != status {
131                return Err(TemplateError::InvalidStatusCode(code));
132            }
133        } else {
134            return Err(TemplateError::InvalidType);
135        }
136        let buf = kawa.storage.buffer();
137        let mut blocks = VecDeque::new();
138        let mut header_replacements = Vec::new();
139        let mut body_replacements = Vec::new();
140        let mut body_size = 0;
141        let mut used_once = Vec::new();
142        for mut block in kawa.blocks.into_iter() {
143            match &mut block {
144                Block::ChunkHeader(_) => return Err(TemplateError::UnsupportedStreaming),
145                Block::StatusLine | Block::Cookies | Block::Flags(_) => {
146                    blocks.push_back(block);
147                }
148                Block::Header(Pair { key, val }) => {
149                    let val_data = val.data(buf);
150                    let key_data = key.data(buf);
151                    if let Some(b'%') = val_data.first() {
152                        for variable in &variables {
153                            if &val_data[1..] == variable.name.as_bytes() {
154                                if !variable.valid_in_header {
155                                    return Err(TemplateError::NotAllowedInHeader(variable.name));
156                                }
157                                *val = Store::Static(b"PLACEHOLDER");
158                                match variable.typ {
159                                    ReplacementType::Variable(_) => {}
160                                    ReplacementType::VariableOnce(var_index) => {
161                                        if used_once.contains(&var_index) {
162                                            return Err(TemplateError::AlreadyConsumed(
163                                                variable.name,
164                                            ));
165                                        }
166                                        used_once.push(var_index);
167                                    }
168                                    ReplacementType::ContentLength => {
169                                        if let Some(b'%') = key_data.first() {
170                                            *key = Store::new_slice(buf, &key_data[1..]);
171                                        }
172                                    }
173                                }
174                                header_replacements.push(Replacement {
175                                    block_index: blocks.len(),
176                                    typ: variable.typ,
177                                });
178                                break;
179                            }
180                        }
181                    }
182                    blocks.push_back(block);
183                }
184                Block::Chunk(Chunk { data }) => {
185                    let data = data.data(buf);
186                    body_size += data.len();
187                    let mut start = 0;
188                    let mut i = 0;
189                    while i < data.len() {
190                        if data[i] == b'%' {
191                            for variable in &variables {
192                                if data[i + 1..].starts_with(variable.name.as_bytes()) {
193                                    if !variable.valid_in_body {
194                                        return Err(TemplateError::NotAllowedInBody(variable.name));
195                                    }
196                                    if start < i {
197                                        blocks.push_back(Block::Chunk(Chunk {
198                                            data: Store::new_slice(buf, &data[start..i]),
199                                        }));
200                                    }
201                                    body_size -= variable.name.len() + 1;
202                                    start = i + variable.name.len() + 1;
203                                    i += variable.name.len();
204                                    match variable.typ {
205                                        ReplacementType::Variable(_) => {}
206                                        ReplacementType::ContentLength => {}
207                                        ReplacementType::VariableOnce(var_index) => {
208                                            if used_once.contains(&var_index) {
209                                                return Err(TemplateError::AlreadyConsumed(
210                                                    variable.name,
211                                                ));
212                                            }
213                                            used_once.push(var_index);
214                                        }
215                                    }
216                                    body_replacements.push(Replacement {
217                                        block_index: blocks.len(),
218                                        typ: variable.typ,
219                                    });
220                                    blocks.push_back(Block::Chunk(Chunk {
221                                        data: Store::Static(b"PLACEHOLDER"),
222                                    }));
223                                    break;
224                                }
225                            }
226                        }
227                        i += 1;
228                    }
229                    if start < data.len() {
230                        blocks.push_back(Block::Chunk(Chunk {
231                            data: Store::new_slice(buf, &data[start..]),
232                        }));
233                    }
234                }
235            }
236        }
237        kawa.blocks = blocks;
238        Ok(Self {
239            kawa,
240            body_replacements,
241            header_replacements,
242            body_size,
243        })
244    }
245
246    fn fill(&self, variables: &[Vec<u8>], variables_once: &mut [Vec<u8>]) -> DefaultAnswerStream {
247        let mut blocks = self.kawa.blocks.clone();
248        let mut body_size = self.body_size;
249        for replacement in &self.body_replacements {
250            match replacement.typ {
251                ReplacementType::Variable(var_index) => {
252                    let variable = &variables[var_index];
253                    body_size += variable.len();
254                    blocks[replacement.block_index] = Block::Chunk(Chunk {
255                        data: Store::from_slice(variable),
256                    })
257                }
258                ReplacementType::VariableOnce(var_index) => {
259                    let variable = std::mem::take(&mut variables_once[var_index]);
260                    body_size += variable.len();
261                    blocks[replacement.block_index] = Block::Chunk(Chunk {
262                        data: Store::from_vec(variable),
263                    })
264                }
265                ReplacementType::ContentLength => unreachable!(),
266            }
267        }
268        for replacement in &self.header_replacements {
269            if let Block::Header(pair) = &mut blocks[replacement.block_index] {
270                match replacement.typ {
271                    ReplacementType::Variable(var_index) => {
272                        pair.val = Store::from_slice(&variables[var_index]);
273                    }
274                    ReplacementType::VariableOnce(var_index) => {
275                        let variable = std::mem::take(&mut variables_once[var_index]);
276                        pair.val = Store::from_vec(variable);
277                    }
278                    ReplacementType::ContentLength => {
279                        pair.val = Store::from_string(body_size.to_string())
280                    }
281                }
282            }
283        }
284        Kawa {
285            storage: Buffer::new(self.kawa.storage.buffer.clone()),
286            blocks,
287            out: Default::default(),
288            detached: self.kawa.detached.clone(),
289            kind: Kind::Response,
290            expects: 0,
291            parsing_phase: ParsingPhase::Terminated,
292            body_size: BodySize::Length(body_size),
293            consumed: false,
294        }
295    }
296}
297
298/// a set of templates for HTTP answers, meant for one listener to use
299pub struct ListenerAnswers {
300    /// MovedPermanently
301    pub answer_301: Template,
302    /// BadRequest
303    pub answer_400: Template,
304    /// Unauthorized
305    pub answer_401: Template,
306    /// NotFound
307    pub answer_404: Template,
308    /// RequestTimeout
309    pub answer_408: Template,
310    /// PayloadTooLarge
311    pub answer_413: Template,
312    /// BadGateway
313    pub answer_502: Template,
314    /// ServiceUnavailable
315    pub answer_503: Template,
316    /// GatewayTimeout
317    pub answer_504: Template,
318    /// InsufficientStorage
319    pub answer_507: Template,
320}
321
322/// templates for HTTP answers, set for one cluster
323#[allow(non_snake_case)]
324pub struct ClusterAnswers {
325    /// ServiceUnavailable
326    pub answer_503: Template,
327}
328
329pub struct HttpAnswers {
330    pub listener_answers: ListenerAnswers, // configurated answers
331    pub cluster_custom_answers: HashMap<ClusterId, ClusterAnswers>,
332}
333
334// const HEADERS: &str = "Connection: close\r
335// Content-Length: 0\r
336// Sozu-Id: %REQUEST_ID\r
337// \r";
338// const STYLE: &str = "<style>
339// pre {
340//   background: #EEE;
341//   padding: 10px;
342//   border: 1px solid #AAA;
343//   border-radius: 5px;
344// }
345// </style>";
346// const FOOTER: &str = "<footer>This is an automatic answer by Sōzu.</footer>";
347fn default_301() -> String {
348    String::from(
349        "\
350HTTP/1.1 301 Moved Permanently\r
351Location: %REDIRECT_LOCATION\r
352Connection: close\r
353Content-Length: 0\r
354Sozu-Id: %REQUEST_ID\r
355\r\n",
356    )
357}
358
359fn default_400() -> String {
360    String::from(
361        "\
362HTTP/1.1 400 Bad Request\r
363Cache-Control: no-cache\r
364Connection: close\r
365%Content-Length: %CONTENT_LENGTH\r
366Sozu-Id: %REQUEST_ID\r
367\r
368<html><head><meta charset='utf-8'><head><body>
369<style>pre{background:#EEE;padding:10px;border:1px solid #AAA;border-radius: 5px;}</style>
370<h1>400 Bad Request</h1>
371<pre>
372{
373    \"status_code\": 400,
374    \"route\": \"%ROUTE\",
375    \"request_id\": \"%REQUEST_ID\",
376    \"parsing_phase\": \"%PHASE\",
377    \"successfully_parsed\": %SUCCESSFULLY_PARSED,
378    \"partially_parsed\": %PARTIALLY_PARSED,
379    \"invalid\": %INVALID
380}
381</pre>
382<p>Request could not be parsed. %MESSAGE</p>
383<div id=details hidden=true>
384<p>While parsing %PHASE, this was reported as invalid:</p>
385<pre>
386<span id=p1 style='background:rgba(0,255,0,0.2)'></span><span id=p2 style='background:rgba(255,255,0,0.2)'></span><span id=p3 style='background:rgba(255,0,0,0.2)'></span>
387</pre>
388</div>
389<script>
390function display(id, chunks) {
391    let [start, end] = chunks.split(' … ');
392    let dec = new TextDecoder('utf8');
393    let decode = chunk => dec.decode(new Uint8Array(chunk.split(' ').filter(e => e).map(e => parseInt(e, 16))));
394    document.getElementById(id).innerText = JSON.stringify(end ? `${decode(start)} […] ${decode(end)}` : `${decode(start)}`).slice(1,-1);
395}
396let p1 = %SUCCESSFULLY_PARSED;
397let p2 = %PARTIALLY_PARSED;
398let p3 = %INVALID;
399if (p1 !== null) {
400    document.getElementById('details').hidden=false;
401    display('p1', p1);display('p2', p2);display('p3', p3);
402}
403</script>
404<footer>This is an automatic answer by Sōzu.</footer></body></html>",
405    )
406}
407
408fn default_401() -> String {
409    String::from(
410        "\
411HTTP/1.1 401 Unauthorized\r
412Cache-Control: no-cache\r
413Connection: close\r
414Sozu-Id: %REQUEST_ID\r
415\r
416<html><head><meta charset='utf-8'><head><body>
417<style>pre{background:#EEE;padding:10px;border:1px solid #AAA;border-radius: 5px;}</style>
418<h1>401 Unauthorized</h1>
419<pre>
420{
421    \"status_code\": 401,
422    \"route\": \"%ROUTE\",
423    \"request_id\": \"%REQUEST_ID\"
424}
425</pre>
426<footer>This is an automatic answer by Sōzu.</footer></body></html>",
427    )
428}
429
430fn default_404() -> String {
431    String::from(
432        "\
433HTTP/1.1 404 Not Found\r
434Cache-Control: no-cache\r
435Connection: close\r
436Sozu-Id: %REQUEST_ID\r
437\r
438<html><head><meta charset='utf-8'><head><body>
439<style>pre{background:#EEE;padding:10px;border:1px solid #AAA;border-radius: 5px;}</style>
440<h1>404 Not Found</h1>
441<pre>
442{
443    \"status_code\": 404,
444    \"route\": \"%ROUTE\",
445    \"request_id\": \"%REQUEST_ID\"
446}
447</pre>
448<footer>This is an automatic answer by Sōzu.</footer></body></html>",
449    )
450}
451
452fn default_408() -> String {
453    String::from(
454        "\
455HTTP/1.1 408 Request Timeout\r
456Cache-Control: no-cache\r
457Connection: close\r
458Sozu-Id: %REQUEST_ID\r
459\r
460<html><head><meta charset='utf-8'><head><body>
461<style>pre{background:#EEE;padding:10px;border:1px solid #AAA;border-radius: 5px;}</style>
462<h1>408 Request Timeout</h1>
463<pre>
464{
465    \"status_code\": 408,
466    \"route\": \"%ROUTE\",
467    \"request_id\": \"%REQUEST_ID\"
468}
469</pre>
470<p>Request timed out after %DURATION.</p>
471<footer>This is an automatic answer by Sōzu.</footer></body></html>",
472    )
473}
474
475fn default_413() -> String {
476    String::from(
477        "\
478HTTP/1.1 413 Payload Too Large\r
479Cache-Control: no-cache\r
480Connection: close\r
481%Content-Length: %CONTENT_LENGTH\r
482Sozu-Id: %REQUEST_ID\r
483\r
484<html><head><meta charset='utf-8'><head><body>
485<style>pre{background:#EEE;padding:10px;border:1px solid #AAA;border-radius: 5px;}</style>
486<h1>413 Payload Too Large</h1>
487<pre>
488{
489    \"status_code\": 413,
490    \"route\": \"%ROUTE\",
491    \"request_id\": \"%REQUEST_ID\"
492}
493</pre>
494<p>Request needed more than %CAPACITY bytes to fit. Parser stopped at phase: %PHASE. %MESSAGE</p>
495<footer>This is an automatic answer by Sōzu.</footer></body></html>",
496    )
497}
498
499fn default_502() -> String {
500    String::from(
501        "\
502HTTP/1.1 502 Bad Gateway\r
503Cache-Control: no-cache\r
504Connection: close\r
505%Content-Length: %CONTENT_LENGTH\r
506Sozu-Id: %REQUEST_ID\r
507\r
508<html><head><meta charset='utf-8'><head><body>
509<style>pre{background:#EEE;padding:10px;border:1px solid #AAA;border-radius: 5px;}</style>
510<h1>502 Bad Gateway</h1>
511<pre>
512{
513    \"status_code\": 502,
514    \"route\": \"%ROUTE\",
515    \"request_id\": \"%REQUEST_ID\",
516    \"cluster_id\": \"%CLUSTER_ID\",
517    \"backend_id\": \"%BACKEND_ID\",
518    \"parsing_phase\": \"%PHASE\",
519    \"successfully_parsed\": \"%SUCCESSFULLY_PARSED\",
520    \"partially_parsed\": \"%PARTIALLY_PARSED\",
521    \"invalid\": \"%INVALID\"
522}
523</pre>
524<p>Response could not be parsed. %MESSAGE</p>
525<div id=details hidden=true>
526<p>While parsing %PHASE, this was reported as invalid:</p>
527<pre>
528<span id=p1 style='background:rgba(0,255,0,0.2)'></span><span id=p2 style='background:rgba(255,255,0,0.2)'></span><span id=p3 style='background:rgba(255,0,0,0.2)'></span>
529</pre>
530</div>
531<script>
532function display(id, chunks) {
533    let [start, end] = chunks.split(' … ');
534    let dec = new TextDecoder('utf8');
535    let decode = chunk => dec.decode(new Uint8Array(chunk.split(' ').filter(e => e).map(e => parseInt(e, 16))));
536    document.getElementById(id).innerText = JSON.stringify(end ? `${decode(start)} […] ${decode(end)}` : `${decode(start)}`).slice(1,-1);
537}
538let p1 = %SUCCESSFULLY_PARSED;
539let p2 = %PARTIALLY_PARSED;
540let p3 = %INVALID;
541if (p1 !== null) {
542    document.getElementById('details').hidden=false;
543    display('p1', p1);display('p2', p2);display('p3', p3);
544}
545</script>
546<footer>This is an automatic answer by Sōzu.</footer></body></html>",
547    )
548}
549
550fn default_503() -> String {
551    String::from(
552        "\
553HTTP/1.1 503 Service Unavailable\r
554Cache-Control: no-cache\r
555Connection: close\r
556%Content-Length: %CONTENT_LENGTH\r
557Sozu-Id: %REQUEST_ID\r
558\r
559<html><head><meta charset='utf-8'><head><body>
560<style>pre{background:#EEE;padding:10px;border:1px solid #AAA;border-radius: 5px;}</style>
561<h1>503 Service Unavailable</h1>
562<pre>
563{
564    \"status_code\": 503,
565    \"route\": \"%ROUTE\",
566    \"request_id\": \"%REQUEST_ID\",
567    \"cluster_id\": \"%CLUSTER_ID\",
568    \"backend_id\": \"%BACKEND_ID\"
569}
570</pre>
571<p>%MESSAGE</p>
572<footer>This is an automatic answer by Sōzu.</footer></body></html>",
573    )
574}
575
576fn default_504() -> String {
577    String::from(
578        "\
579HTTP/1.1 504 Gateway Timeout\r
580Cache-Control: no-cache\r
581Connection: close\r
582Sozu-Id: %REQUEST_ID\r
583\r
584<html><head><meta charset='utf-8'><head><body>
585<style>pre{background:#EEE;padding:10px;border:1px solid #AAA;border-radius: 5px;}</style>
586<h1>504 Gateway Timeout</h1>
587<pre>
588{
589    \"status_code\": 504,
590    \"route\": \"%ROUTE\",
591    \"request_id\": \"%REQUEST_ID\",
592    \"cluster_id\": \"%CLUSTER_ID\",
593    \"backend_id\": \"%BACKEND_ID\"
594}
595</pre>
596<p>Response timed out after %DURATION.</p>
597<footer>This is an automatic answer by Sōzu.</footer></body></html>",
598    )
599}
600
601fn default_507() -> String {
602    String::from(
603        "\
604HTTP/1.1 507 Insufficient Storage\r
605Cache-Control: no-cache\r
606Connection: close\r
607%Content-Length: %CONTENT_LENGTH\r
608Sozu-Id: %REQUEST_ID\r
609\r
610<html><head><meta charset='utf-8'><head><body>
611<style>pre{background:#EEE;padding:10px;border:1px solid #AAA;border-radius: 5px;}</style>
612<h1>507 Insufficient Storage</h1>
613<pre>
614{
615    \"status_code\": 507,
616    \"route\": \"%ROUTE\",
617    \"request_id\": \"%REQUEST_ID\",
618    \"cluster_id\": \"%CLUSTER_ID\",
619    \"backend_id\": \"%BACKEND_ID\"
620}
621</pre>
622<p>Response needed more than %CAPACITY bytes to fit. Parser stopped at phase: %PHASE. %MESSAGE/p>
623<footer>This is an automatic answer by Sōzu.</footer></body></html>",
624    )
625}
626
627fn phase_to_vec(phase: ParsingPhaseMarker) -> Vec<u8> {
628    match phase {
629        ParsingPhaseMarker::StatusLine => "StatusLine",
630        ParsingPhaseMarker::Headers => "Headers",
631        ParsingPhaseMarker::Cookies => "Cookies",
632        ParsingPhaseMarker::Body => "Body",
633        ParsingPhaseMarker::Chunks => "Chunks",
634        ParsingPhaseMarker::Trailers => "Trailers",
635        ParsingPhaseMarker::Terminated => "Terminated",
636        ParsingPhaseMarker::Error => "Error",
637    }
638    .into()
639}
640
641impl HttpAnswers {
642    #[rustfmt::skip]
643    pub fn template(status: u16, answer: String) -> Result<Template, (u16, TemplateError)> {
644        let length = TemplateVariable {
645            name: "CONTENT_LENGTH",
646            valid_in_body: false,
647            valid_in_header: true,
648            typ: ReplacementType::ContentLength,
649        };
650
651        let route = TemplateVariable {
652            name: "ROUTE",
653            valid_in_body: true,
654            valid_in_header: true,
655            typ: ReplacementType::Variable(0),
656        };
657        let request_id = TemplateVariable {
658            name: "REQUEST_ID",
659            valid_in_body: true,
660            valid_in_header: true,
661            typ: ReplacementType::Variable(0),
662        };
663        let cluster_id = TemplateVariable {
664            name: "CLUSTER_ID",
665            valid_in_body: true,
666            valid_in_header: true,
667            typ: ReplacementType::Variable(0),
668        };
669        let backend_id = TemplateVariable {
670            name: "BACKEND_ID",
671            valid_in_body: true,
672            valid_in_header: true,
673            typ: ReplacementType::Variable(0),
674        };
675        let duration = TemplateVariable {
676            name: "DURATION",
677            valid_in_body: true,
678            valid_in_header: true,
679            typ: ReplacementType::Variable(0),
680        };
681        let capacity = TemplateVariable {
682            name: "CAPACITY",
683            valid_in_body: true,
684            valid_in_header: true,
685            typ: ReplacementType::Variable(0),
686        };
687        let phase = TemplateVariable {
688            name: "PHASE",
689            valid_in_body: true,
690            valid_in_header: true,
691            typ: ReplacementType::Variable(0),
692        };
693
694        let location = TemplateVariable {
695            name: "REDIRECT_LOCATION",
696            valid_in_body: false,
697            valid_in_header: true,
698            typ: ReplacementType::VariableOnce(0),
699        };
700        let message = TemplateVariable {
701            name: "MESSAGE",
702            valid_in_body: true,
703            valid_in_header: false,
704            typ: ReplacementType::VariableOnce(0),
705        };
706        let successfully_parsed = TemplateVariable {
707            name: "SUCCESSFULLY_PARSED",
708            valid_in_body: true,
709            valid_in_header: false,
710            typ: ReplacementType::Variable(0),
711        };
712        let partially_parsed = TemplateVariable {
713            name: "PARTIALLY_PARSED",
714            valid_in_body: true,
715            valid_in_header: false,
716            typ: ReplacementType::Variable(0),
717        };
718        let invalid = TemplateVariable {
719            name: "INVALID",
720            valid_in_body: true,
721            valid_in_header: false,
722            typ: ReplacementType::Variable(0),
723        };
724
725        match status {
726            301 => Template::new(
727                301,
728                answer,
729                &[length, route, request_id, location]
730            ),
731            400 => Template::new(
732                400,
733                answer,
734                &[length, route, request_id, message, phase, successfully_parsed, partially_parsed, invalid],
735            ),
736            401 => Template::new(
737                401,
738                answer,
739                &[length, route, request_id]
740            ),
741            404 => Template::new(
742                404,
743                answer,
744                &[length, route, request_id]
745            ),
746            408 => Template::new(
747                408,
748                answer,
749                &[length, route, request_id, duration]
750            ),
751            413 => Template::new(
752                413,
753                answer,
754                &[length, route, request_id, capacity, message, phase],
755            ),
756            502 => Template::new(
757                502,
758                answer,
759                &[length, route, request_id, cluster_id, backend_id, message, phase, successfully_parsed, partially_parsed, invalid],
760            ),
761            503 => Template::new(
762                503,
763                answer,
764                &[length, route, request_id, cluster_id, backend_id, message],
765            ),
766            504 => Template::new(
767                504,
768                answer,
769                &[length, route, request_id, cluster_id, backend_id, duration],
770            ),
771            507 => Template::new(
772                507,
773                answer,
774                &[length, route, request_id, cluster_id, backend_id, capacity, message, phase],
775            ),
776            _ => Err(TemplateError::InvalidStatusCode(status)),
777        }
778        .map_err(|e| (status, e))
779    }
780
781    pub fn new(conf: &Option<CustomHttpAnswers>) -> Result<Self, (u16, TemplateError)> {
782        Ok(HttpAnswers {
783            listener_answers: ListenerAnswers {
784                answer_301: Self::template(
785                    301,
786                    conf.as_ref()
787                        .and_then(|c| c.answer_301.clone())
788                        .unwrap_or(default_301()),
789                )?,
790                answer_400: Self::template(
791                    400,
792                    conf.as_ref()
793                        .and_then(|c| c.answer_400.clone())
794                        .unwrap_or(default_400()),
795                )?,
796                answer_401: Self::template(
797                    401,
798                    conf.as_ref()
799                        .and_then(|c| c.answer_401.clone())
800                        .unwrap_or(default_401()),
801                )?,
802                answer_404: Self::template(
803                    404,
804                    conf.as_ref()
805                        .and_then(|c| c.answer_404.clone())
806                        .unwrap_or(default_404()),
807                )?,
808                answer_408: Self::template(
809                    408,
810                    conf.as_ref()
811                        .and_then(|c| c.answer_408.clone())
812                        .unwrap_or(default_408()),
813                )?,
814                answer_413: Self::template(
815                    413,
816                    conf.as_ref()
817                        .and_then(|c| c.answer_413.clone())
818                        .unwrap_or(default_413()),
819                )?,
820                answer_502: Self::template(
821                    502,
822                    conf.as_ref()
823                        .and_then(|c| c.answer_502.clone())
824                        .unwrap_or(default_502()),
825                )?,
826                answer_503: Self::template(
827                    503,
828                    conf.as_ref()
829                        .and_then(|c| c.answer_503.clone())
830                        .unwrap_or(default_503()),
831                )?,
832                answer_504: Self::template(
833                    504,
834                    conf.as_ref()
835                        .and_then(|c| c.answer_504.clone())
836                        .unwrap_or(default_504()),
837                )?,
838                answer_507: Self::template(
839                    507,
840                    conf.as_ref()
841                        .and_then(|c| c.answer_507.clone())
842                        .unwrap_or(default_507()),
843                )?,
844            },
845            cluster_custom_answers: HashMap::new(),
846        })
847    }
848
849    pub fn add_custom_answer(
850        &mut self,
851        cluster_id: &str,
852        answer_503: String,
853    ) -> Result<(), (u16, TemplateError)> {
854        let answer_503 = Self::template(503, answer_503)?;
855        self.cluster_custom_answers
856            .insert(cluster_id.to_string(), ClusterAnswers { answer_503 });
857        Ok(())
858    }
859
860    pub fn remove_custom_answer(&mut self, cluster_id: &str) {
861        self.cluster_custom_answers.remove(cluster_id);
862    }
863
864    pub fn get(
865        &self,
866        answer: DefaultAnswer,
867        request_id: String,
868        cluster_id: Option<&str>,
869        backend_id: Option<&str>,
870        route: String,
871    ) -> DefaultAnswerStream {
872        let variables: Vec<Vec<u8>>;
873        let mut variables_once: Vec<Vec<u8>>;
874        let template = match answer {
875            DefaultAnswer::Answer301 { location } => {
876                variables = vec![route.into(), request_id.into()];
877                variables_once = vec![location.into()];
878                &self.listener_answers.answer_301
879            }
880            DefaultAnswer::Answer400 {
881                message,
882                phase,
883                successfully_parsed,
884                partially_parsed,
885                invalid,
886            } => {
887                variables = vec![
888                    route.into(),
889                    request_id.into(),
890                    phase_to_vec(phase),
891                    successfully_parsed.into(),
892                    partially_parsed.into(),
893                    invalid.into(),
894                ];
895                variables_once = vec![message.into()];
896                &self.listener_answers.answer_400
897            }
898            DefaultAnswer::Answer401 {} => {
899                variables = vec![route.into(), request_id.into()];
900                variables_once = vec![];
901                &self.listener_answers.answer_401
902            }
903            DefaultAnswer::Answer404 {} => {
904                variables = vec![route.into(), request_id.into()];
905                variables_once = vec![];
906                &self.listener_answers.answer_404
907            }
908            DefaultAnswer::Answer408 { duration } => {
909                variables = vec![route.into(), request_id.into(), duration.to_string().into()];
910                variables_once = vec![];
911                &self.listener_answers.answer_408
912            }
913            DefaultAnswer::Answer413 {
914                message,
915                phase,
916                capacity,
917            } => {
918                variables = vec![
919                    route.into(),
920                    request_id.into(),
921                    capacity.to_string().into(),
922                    phase_to_vec(phase),
923                ];
924                variables_once = vec![message.into()];
925                &self.listener_answers.answer_413
926            }
927            DefaultAnswer::Answer502 {
928                message,
929                phase,
930                successfully_parsed,
931                partially_parsed,
932                invalid,
933            } => {
934                variables = vec![
935                    route.into(),
936                    request_id.into(),
937                    cluster_id.unwrap_or_default().into(),
938                    backend_id.unwrap_or_default().into(),
939                    phase_to_vec(phase),
940                    successfully_parsed.into(),
941                    partially_parsed.into(),
942                    invalid.into(),
943                ];
944                variables_once = vec![message.into()];
945                &self.listener_answers.answer_502
946            }
947            DefaultAnswer::Answer503 { message } => {
948                variables = vec![
949                    route.into(),
950                    request_id.into(),
951                    cluster_id.unwrap_or_default().into(),
952                    backend_id.unwrap_or_default().into(),
953                ];
954                variables_once = vec![message.into()];
955                cluster_id
956                    .and_then(|id: &str| self.cluster_custom_answers.get(id))
957                    .map(|c| &c.answer_503)
958                    .unwrap_or_else(|| &self.listener_answers.answer_503)
959            }
960            DefaultAnswer::Answer504 { duration } => {
961                variables = vec![
962                    route.into(),
963                    request_id.into(),
964                    cluster_id.unwrap_or_default().into(),
965                    backend_id.unwrap_or_default().into(),
966                    duration.to_string().into(),
967                ];
968                variables_once = vec![];
969                &self.listener_answers.answer_504
970            }
971            DefaultAnswer::Answer507 {
972                phase,
973                message,
974                capacity,
975            } => {
976                variables = vec![
977                    route.into(),
978                    request_id.into(),
979                    cluster_id.unwrap_or_default().into(),
980                    backend_id.unwrap_or_default().into(),
981                    capacity.to_string().into(),
982                    phase_to_vec(phase),
983                ];
984                variables_once = vec![message.into()];
985                &self.listener_answers.answer_507
986            }
987        };
988        // kawa::debug_kawa(&template.kawa);
989        // println!("{template:#?}");
990        template.fill(&variables, &mut variables_once)
991    }
992}