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
67pub struct Template {
69 kawa: DefaultAnswerStream,
70 body_replacements: Vec<Replacement>,
71 header_replacements: Vec<Replacement>,
72 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 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
296pub struct ListenerAnswers {
298 pub answer_301: Template,
300 pub answer_400: Template,
302 pub answer_401: Template,
304 pub answer_404: Template,
306 pub answer_408: Template,
308 pub answer_413: Template,
310 pub answer_502: Template,
312 pub answer_503: Template,
314 pub answer_504: Template,
316 pub answer_507: Template,
318}
319
320#[allow(non_snake_case)]
322pub struct ClusterAnswers {
323 pub answer_503: Template,
325}
326
327pub struct HttpAnswers {
328 pub listener_answers: ListenerAnswers, pub cluster_custom_answers: HashMap<ClusterId, ClusterAnswers>,
330}
331
332fn 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 template.fill(&variables, &mut variables_once)
989 }
990}