sozu_lib/protocol/kawa_h1/
answers.rs

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