diff --git a/lib/src/http.rs b/lib/src/http.rs index 79c3735ec..76c63535f 100644 --- a/lib/src/http.rs +++ b/lib/src/http.rs @@ -32,8 +32,9 @@ use crate::{ pool::Pool, protocol::{ http::{ - answers::HttpAnswers, + answers::{HttpAnswers, RawAnswers}, parser::{hostname_and_port, Method}, + ResponseStream, }, proxy_protocol::expect::ExpectProxyProtocol, Http, Pipe, SessionState, @@ -230,8 +231,14 @@ impl HttpSession { container_frontend_timeout.reset(); container_backend_timeout.reset(); + let backend_buffer = if let ResponseStream::BackendAnswer(kawa) = http.response_stream { + kawa.storage.buffer + } else { + return None; + }; + let mut pipe = Pipe::new( - http.response_stream.storage.buffer, + backend_buffer, http.backend_id, http.backend_socket, http.backend, @@ -580,14 +587,17 @@ impl HttpProxy { }) } - pub fn add_cluster(&mut self, cluster: Cluster) -> Result<(), ProxyError> { - if let Some(answer_503) = &cluster.answer_503 { + pub fn add_cluster(&mut self, mut cluster: Cluster) -> Result<(), ProxyError> { + if let Some(answer_503) = cluster.answer_503.take() { for listener in self.listeners.values() { listener .borrow() .answers .borrow_mut() - .add_custom_answer(&cluster.cluster_id, answer_503); + .add_custom_answer(&cluster.cluster_id, answer_503.clone()) + .map_err(|(status, error)| { + ProxyError::AddCluster(ListenerError::TemplateParse(status, error)) + })?; } } self.clusters.insert(cluster.cluster_id.clone(), cluster); @@ -717,10 +727,14 @@ impl HttpListener { Ok(HttpListener { active: false, address: config.address.clone().into(), - answers: Rc::new(RefCell::new(HttpAnswers::new( - &config.answer_404, - &config.answer_503, - ))), + answers: Rc::new(RefCell::new( + HttpAnswers::new( + // &config.answer_404, + // &config.answer_503, + RawAnswers::default(), + ) + .map_err(|(status, error)| ListenerError::TemplateParse(status, error))?, + )), config, fronts: Router::new(), listener: None, @@ -1467,10 +1481,14 @@ mod tests { listener: None, address: address.into(), fronts, - answers: Rc::new(RefCell::new(HttpAnswers::new( - "HTTP/1.1 404 Not Found\r\n\r\n", - "HTTP/1.1 503 Service Unavailable\r\n\r\n", - ))), + answers: Rc::new(RefCell::new( + HttpAnswers::new( + // "HTTP/1.1 404 Not Found\r\n\r\n", + // "HTTP/1.1 503 Service Unavailable\r\n\r\n", + RawAnswers::default(), + ) + .unwrap(), + )), config: default_config, token: Token(0), active: true, diff --git a/lib/src/https.rs b/lib/src/https.rs index 8d2fd1da8..bf3edbfe4 100644 --- a/lib/src/https.rs +++ b/lib/src/https.rs @@ -55,8 +55,9 @@ use crate::{ protocol::{ h2::Http2, http::{ - answers::HttpAnswers, + answers::{HttpAnswers, RawAnswers}, parser::{hostname_and_port, Method}, + ResponseStream, }, proxy_protocol::expect::ExpectProxyProtocol, rustls::TlsHandshake, @@ -357,8 +358,14 @@ impl HttpsSession { container_frontend_timeout.reset(); container_backend_timeout.reset(); + let backend_buffer = if let ResponseStream::BackendAnswer(kawa) = http.response_stream { + kawa.storage.buffer + } else { + return None; + }; + let mut pipe = Pipe::new( - http.response_stream.storage.buffer, + backend_buffer, http.backend_id, http.backend_socket, http.backend, @@ -625,10 +632,14 @@ impl HttpsListener { rustls_details: server_config, active: false, fronts: Router::new(), - answers: Rc::new(RefCell::new(HttpAnswers::new( - &config.answer_404, - &config.answer_503, - ))), + answers: Rc::new(RefCell::new( + HttpAnswers::new( + // &config.answer_404, + // &config.answer_503, + RawAnswers::default(), + ) + .map_err(|(status, error)| ListenerError::TemplateParse(status, error))?, + )), config, token, tags: BTreeMap::new(), @@ -1009,7 +1020,10 @@ impl HttpsProxy { .borrow() .answers .borrow_mut() - .add_custom_answer(&cluster.cluster_id, &answer_503); + .add_custom_answer(&cluster.cluster_id, answer_503.clone()) + .map_err(|(status, error)| { + ProxyError::AddCluster(ListenerError::TemplateParse(status, error)) + })?; } } self.clusters.insert(cluster.cluster_id.clone(), cluster); @@ -1592,10 +1606,14 @@ mod tests { fronts, rustls_details, resolver, - answers: Rc::new(RefCell::new(HttpAnswers::new( - "HTTP/1.1 404 Not Found\r\n\r\n", - "HTTP/1.1 503 Service Unavailable\r\n\r\n", - ))), + answers: Rc::new(RefCell::new( + HttpAnswers::new( + // "HTTP/1.1 404 Not Found\r\n\r\n", + // "HTTP/1.1 503 Service Unavailable\r\n\r\n", + RawAnswers::default(), + ) + .unwrap(), + )), config: default_config, token: Token(0), active: true, diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 3439ca256..e794233cd 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -353,7 +353,7 @@ use std::{ use backends::BackendError; use hex::FromHexError; use mio::{net::TcpStream, Interest, Token}; -use protocol::http::parser::Method; +use protocol::http::{answers::TemplateError, parser::Method}; use router::RouterError; use time::{Duration, Instant}; use tls::CertificateResolverError; @@ -642,6 +642,8 @@ pub enum ListenerError { Resolver(CertificateResolverError), #[error("failed to parse pem, {0}")] PemParse(String), + #[error("failed to parse template {0}: {1}")] + TemplateParse(u16, TemplateError), #[error("failed to build rustls context, {0}")] BuildRustls(String), #[error("could not activate listener with address {address:?}: {error}")] @@ -671,8 +673,10 @@ pub enum ProxyError { NoListenerFound(SocketAddr), #[error("a listener is already present for this token")] ListenerAlreadyPresent, - #[error("could not create add listener: {0}")] + #[error("could not add listener: {0}")] AddListener(ListenerError), + #[error("could not add cluster: {0}")] + AddCluster(ListenerError), #[error("failed to activate listener with address {address:?}: {listener_error}")] ListenerActivation { address: SocketAddr, diff --git a/lib/src/protocol/kawa_h1/answers.rs b/lib/src/protocol/kawa_h1/answers.rs index 556f6fd1c..3e7e354e5 100644 --- a/lib/src/protocol/kawa_h1/answers.rs +++ b/lib/src/protocol/kawa_h1/answers.rs @@ -1,32 +1,326 @@ -use std::{collections::HashMap, rc::Rc}; +use crate::{protocol::http::DefaultAnswer, sozu_command::state::ClusterId}; +use kawa::{ + h1::NoCallbacks, AsBuffer, Block, BodySize, Buffer, Chunk, Kawa, Kind, Pair, ParsingPhase, + StatusLine, Store, +}; +use std::{ + collections::{HashMap, VecDeque}, + fmt, + rc::Rc, +}; -use crate::{protocol::http::DefaultAnswerStatus, sozu_command::state::ClusterId}; +#[derive(Clone)] +pub struct SharedBuffer(Rc<[u8]>); + +impl AsBuffer for SharedBuffer { + fn as_buffer(&self) -> &[u8] { + &self.0 + } + + fn as_mut_buffer(&mut self) -> &mut [u8] { + panic!() + } +} + +pub type DefaultAnswerStream = Kawa; + +#[derive(thiserror::Error, Debug)] +pub enum TemplateError { + #[error("invalid template type: request was found, expected response")] + InvalidType, + #[error("template seems invalid or incomplete: {0:?}")] + InvalidTemplate(ParsingPhase), + #[error("unexpected status code: {0}")] + InvalidStatusCode(u16), + #[error("streaming is not supported in templates")] + UnsupportedStreaming, + #[error("template variable {0} is not allowed in headers")] + NotAllowedInHeader(&'static str), + #[error("template variable {0} is not allowed in body")] + NotAllowedInBody(&'static str), + #[error("template variable {0} can only be used once")] + AlreadyConsumed(&'static str), +} + +#[derive(Clone, Copy, Debug)] +struct TemplateVariable { + name: &'static str, + valid_in_body: bool, + valid_in_header: bool, + typ: ReplacementType, +} + +#[derive(Clone, Copy, Debug)] +enum ReplacementType { + Variable(usize), + VariableOnce(usize), + ContentLength, +} + +#[derive(Clone, Copy, Debug)] +struct Replacement { + block_index: usize, + typ: ReplacementType, +} + +pub struct Template { + kawa: DefaultAnswerStream, + body_replacements: Vec, + header_replacements: Vec, + // Size of body without any variables + body_size: usize, +} + +impl fmt::Debug for Template { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Template") + .field("body_replacements", &self.body_replacements) + .field("header_replacements", &self.header_replacements) + .field("body_size", &self.body_size) + .finish() + } +} + +impl Template { + fn new( + status: u16, + answer: Vec, + variables: &[TemplateVariable], + ) -> Result { + Self::_new(status, answer, variables).map_err(|e| (status, e)) + } + + fn _new( + status: u16, + answer: Vec, + variables: &[TemplateVariable], + ) -> Result { + let len = answer.len(); + let mut kawa = Kawa::new(Kind::Response, Buffer::new(SharedBuffer(Rc::from(answer)))); + kawa.storage.end = len; + kawa::h1::parse(&mut kawa, &mut NoCallbacks); + if !kawa.is_main_phase() { + return Err(TemplateError::InvalidTemplate(kawa.parsing_phase)); + } + if let StatusLine::Response { code, .. } = kawa.detached.status_line { + if code != status { + return Err(TemplateError::InvalidStatusCode(code)); + } + } else { + return Err(TemplateError::InvalidType); + } + let buf = kawa.storage.buffer(); + let mut blocks = VecDeque::new(); + let mut header_replacements = Vec::new(); + let mut body_replacements = Vec::new(); + let mut body_size = 0; + let mut used_once = Vec::new(); + for mut block in kawa.blocks.into_iter() { + match &mut block { + Block::ChunkHeader(_) => return Err(TemplateError::UnsupportedStreaming), + Block::StatusLine | Block::Cookies | Block::Flags(_) => { + blocks.push_back(block); + } + Block::Header(Pair { key, val }) => { + let val_data = val.data(buf); + let key_data = key.data(buf); + if let Some(b'%') = val_data.first() { + for variable in variables { + if &val_data[1..] == variable.name.as_bytes() { + if !variable.valid_in_header { + return Err(TemplateError::NotAllowedInHeader(variable.name)); + } + *val = Store::Static(b"PLACEHOLDER"); + match variable.typ { + ReplacementType::Variable(_) => {} + ReplacementType::VariableOnce(var_index) => { + if used_once.contains(&var_index) { + return Err(TemplateError::AlreadyConsumed( + variable.name, + )); + } + used_once.push(var_index); + } + ReplacementType::ContentLength => { + if let Some(b'%') = key_data.first() { + *key = Store::new_slice(buf, &key_data[1..]); + } + } + } + header_replacements.push(Replacement { + block_index: blocks.len(), + typ: variable.typ, + }); + break; + } + } + } + blocks.push_back(block); + } + Block::Chunk(Chunk { data }) => { + let data = data.data(buf); + body_size += data.len(); + let mut start = 0; + let mut i = 0; + while i < data.len() { + if data[i] == b'%' { + for variable in variables { + if data[i + 1..].starts_with(variable.name.as_bytes()) { + if !variable.valid_in_body { + return Err(TemplateError::NotAllowedInBody(variable.name)); + } + if start < i { + blocks.push_back(Block::Chunk(Chunk { + data: Store::new_slice(buf, &data[start..i]), + })); + } + body_size -= variable.name.len() + 1; + start = i + variable.name.len() + 1; + i += variable.name.len(); + match variable.typ { + ReplacementType::Variable(_) => {} + ReplacementType::ContentLength => {} + ReplacementType::VariableOnce(var_index) => { + if used_once.contains(&var_index) { + return Err(TemplateError::AlreadyConsumed( + variable.name, + )); + } + used_once.push(var_index); + } + } + body_replacements.push(Replacement { + block_index: blocks.len(), + typ: variable.typ, + }); + blocks.push_back(Block::Chunk(Chunk { + data: Store::Static(b"PLACEHOLDER"), + })); + break; + } + } + } + i += 1; + } + if start < data.len() { + blocks.push_back(Block::Chunk(Chunk { + data: Store::new_slice(buf, &data[start..]), + })); + } + } + } + } + kawa.blocks = blocks; + Ok(Self { + kawa, + body_replacements, + header_replacements, + body_size, + }) + } + + fn fill(&self, variables: &[&[u8]], variables_once: &mut [Vec]) -> DefaultAnswerStream { + let mut blocks = self.kawa.blocks.clone(); + let mut body_size = self.body_size; + for replacement in &self.body_replacements { + match replacement.typ { + ReplacementType::Variable(var_index) => { + let variable = &variables[var_index]; + body_size += variable.len(); + blocks[replacement.block_index] = Block::Chunk(Chunk { + data: Store::new_vec(&variable), + }) + } + ReplacementType::VariableOnce(var_index) => { + let variable = std::mem::take(&mut variables_once[var_index]); + body_size += variable.len(); + blocks[replacement.block_index] = Block::Chunk(Chunk { + data: Store::Alloc(variable.into_boxed_slice(), 0), + }) + } + ReplacementType::ContentLength => unreachable!(), + } + } + for replacement in &self.header_replacements { + if let Block::Header(pair) = &mut blocks[replacement.block_index] { + match replacement.typ { + ReplacementType::Variable(var_index) => { + pair.val = Store::new_vec(variables[var_index]); + } + ReplacementType::VariableOnce(var_index) => { + let variable = std::mem::take(&mut variables_once[var_index]); + pair.val = Store::Alloc(variable.into_boxed_slice(), 0); + } + ReplacementType::ContentLength => { + pair.val = + Store::Alloc(body_size.to_string().into_bytes().into_boxed_slice(), 0) + } + } + } + } + Kawa { + storage: Buffer::new(self.kawa.storage.buffer.clone()), + blocks, + out: Default::default(), + detached: self.kawa.detached.clone(), + kind: Kind::Response, + expects: 0, + parsing_phase: ParsingPhase::Terminated, + body_size: BodySize::Length(body_size), + consumed: false, + } + } +} +pub struct RawAnswers { + /// MovedPermanently + pub answer_301: Vec, + /// BadRequest + pub answer_400: Vec, + /// Unauthorized + pub answer_401: Vec, + /// NotFound + pub answer_404: Vec, + /// RequestTimeout + pub answer_408: Vec, + /// PayloadTooLarge + pub answer_413: Vec, + /// BadGateway + pub answer_502: Vec, + /// ServiceUnavailable + pub answer_503: Vec, + /// GatewayTimeout + pub answer_504: Vec, + /// InsufficientStorage + pub answer_507: Vec, +} -#[allow(non_snake_case)] pub struct DefaultAnswers { - /// 400 - pub BadRequest: Rc>, - /// 401 - pub Unauthorized: Rc>, - /// 404 - pub NotFound: Rc>, - /// 408 - pub RequestTimeout: Rc>, - /// 413 - pub PayloadTooLarge: Rc>, - /// 502 - pub BadGateway: Rc>, - /// 503 - pub ServiceUnavailable: Rc>, - /// 504 - pub GatewayTimeout: Rc>, - /// 507 - pub InsufficientStorage: Rc>, + /// MovedPermanently + pub answer_301: Template, + /// BadRequest + pub answer_400: Template, + /// Unauthorized + pub answer_401: Template, + /// NotFound + pub answer_404: Template, + /// RequestTimeout + pub answer_408: Template, + /// PayloadTooLarge + pub answer_413: Template, + /// BadGateway + pub answer_502: Template, + /// ServiceUnavailable + pub answer_503: Template, + /// GatewayTimeout + pub answer_504: Template, + /// InsufficientStorage + pub answer_507: Template, } #[allow(non_snake_case)] pub struct CustomAnswers { - pub ServiceUnavailable: Option>>, + /// ServiceUnavailable + pub answer_503: Template, } pub struct HttpAnswers { @@ -34,67 +328,241 @@ pub struct HttpAnswers { pub custom: HashMap, } +impl Default for RawAnswers { + fn default() -> Self { + Self { + answer_301: Vec::from( + &b"\ +HTTP/1.1 301 Moved Permanently\r +Location: %REDIRECT_LOCATION\r +Connection: close\r +Content-Length: 0\r +Sozu-Id: %SOZU_ID\r +\r\n"[..], + ), + answer_400: Vec::from( + &b"\ +HTTP/1.1 400 Bad Request\r +Cache-Control: no-cache\r +Connection: close\r +%Content-Length: %CONTENT_LENGTH\r +Sozu-Id: %SOZU_ID\r +\r +

Sozu automatic 400 answer

+

Request %SOZU_ID could not be parsed.

+
+Kawa error: %DETAILS
+
+"[..],
+            ),
+            answer_401: Vec::from(
+                &b"\
+HTTP/1.1 401 Unauthorized\r
+Cache-Control: no-cache\r
+Connection: close\r
+Sozu-Id: %SOZU_ID\r
+\r\n"[..],
+            ),
+            answer_404: Vec::from(
+                &b"\
+HTTP/1.1 404 Not Found\r
+Cache-Control: no-cache\r
+Connection: close\r
+Sozu-Id: %SOZU_ID\r
+\r\n"[..],
+            ),
+            answer_408: Vec::from(
+                &b"\
+HTTP/1.1 408 Request Timeout\r
+Cache-Control: no-cache\r
+Connection: close\r
+Sozu-Id: %SOZU_ID\r
+\r\n"[..],
+            ),
+            answer_413: Vec::from(
+                &b"\
+HTTP/1.1 413 Payload Too Large\r
+Cache-Control: no-cache\r
+Connection: close\r
+%Content-Length: %CONTENT_LENGTH\r
+Sozu-Id: %SOZU_ID\r
+\r
+

Sozu automatic 413 answer

+

Request %SOZU_ID failed because all its headers could not be contained at once

+
+Kawa cursors: %DETAILS
+
+"[..],
+            ),
+            answer_502: Vec::from(
+                &b"\
+HTTP/1.1 502 Bad Gateway\r
+Cache-Control: no-cache\r
+Connection: close\r
+%Content-Length: %CONTENT_LENGTH\r
+Sozu-Id: %SOZU_ID\r
+\r
+

Sozu automatic 502 answer

+

Response to %SOZU_ID could not be parsed.

+
+Kawa error: %DETAILS
+
+"[..],
+            ),
+            answer_503: Vec::from(
+                &b"\
+HTTP/1.1 503 Service Unavailable\r
+Cache-Control: no-cache\r
+Connection: close\r
+%Content-Length: %CONTENT_LENGTH\r
+Sozu-Id: %SOZU_ID\r
+\r
+

Sozu automatic 503 answer

+

Could not find an available backend for request %SOZU_ID.

+
+%DETAILS
+
+"[..],
+            ),
+            answer_504: Vec::from(
+                &b"\
+HTTP/1.1 504 Gateway Timeout\r
+Cache-Control: no-cache\r
+Connection: close\r
+Sozu-Id: %SOZU_ID\r
+\r\n"[..],
+            ),
+            answer_507: Vec::from(
+                &b"\
+HTTP/1.1 507 Insufficient Storage\r
+Cache-Control: no-cache\r
+Connection: close\r
+%Content-Length: %CONTENT_LENGTH\r
+Sozu-Id: %SOZU_ID\r
+\r
+

Sozu automatic 507 answer

+

Response to %SOZU_ID failed because all its headers could not be contained at once.

+
+Kawa cursors: %DETAILS
+
+"[..],
+            ),
+        }
+    }
+}
+
 impl HttpAnswers {
-    pub fn new(answer_404: &str, answer_503: &str) -> Self {
-        HttpAnswers {
+    pub fn new(answers: RawAnswers) -> Result {
+        let sozu_id = TemplateVariable {
+            name: "SOZU_ID",
+            valid_in_body: true,
+            valid_in_header: true,
+            typ: ReplacementType::Variable(0),
+        };
+        let length = TemplateVariable {
+            name: "CONTENT_LENGTH",
+            valid_in_body: false,
+            valid_in_header: true,
+            typ: ReplacementType::ContentLength,
+        };
+        let details = TemplateVariable {
+            name: "DETAILS",
+            valid_in_body: true,
+            valid_in_header: false,
+            typ: ReplacementType::VariableOnce(0),
+        };
+        let location = TemplateVariable {
+            name: "REDIRECT_LOCATION",
+            valid_in_body: false,
+            valid_in_header: true,
+            typ: ReplacementType::VariableOnce(0),
+        };
+        Ok(HttpAnswers {
             default: DefaultAnswers {
-                BadRequest: Rc::new(Vec::from(
-                    &b"HTTP/1.1 400 Bad Request\r\nCache-Control: no-cache\r\nConnection: close\r\n\r\n"[..]
-                )),
-                Unauthorized: Rc::new(Vec::from(
-                    &b"HTTP/1.1 401 Unauthorized\r\nCache-Control: no-cache\r\nConnection: close\r\n\r\n"[..]
-                )),
-                NotFound: Rc::new(Vec::from(answer_404.as_bytes())),
-                RequestTimeout: Rc::new(Vec::from(
-                    &b"HTTP/1.1 408 Request Timeout\r\nCache-Control: no-cache\r\nConnection: close\r\n\r\n"[..]
-                )),
-                PayloadTooLarge: Rc::new(Vec::from(
-                    &b"HTTP/1.1 413 Payload Too Large\r\nCache-Control: no-cache\r\nConnection: close\r\n\r\n"[..]
-                )),
-                BadGateway: Rc::new(Vec::from(
-                    &b"HTTP/1.1 502 Bad Gateway\r\nCache-Control: no-cache\r\nConnection: close\r\n\r\n"[..]
-                )),
-                ServiceUnavailable: Rc::new(Vec::from(answer_503.as_bytes())),
-                GatewayTimeout: Rc::new(Vec::from(
-                    &b"HTTP/1.1 504 Gateway Timeout\r\nCache-Control: no-cache\r\nConnection: close\r\n\r\n"[..]
-                )),
-                InsufficientStorage: Rc::new(Vec::from(
-                    &b"HTTP/1.1 507 Insufficient Storage\r\nCache-Control: no-cache\r\nConnection: close\r\n\r\n"[..]
-                )),
+                answer_301: Template::new(301, answers.answer_301, &[sozu_id, length, location])?,
+                answer_400: Template::new(400, answers.answer_400, &[sozu_id, length, details])?,
+                answer_401: Template::new(401, answers.answer_401, &[sozu_id, length])?,
+                answer_404: Template::new(404, answers.answer_404, &[sozu_id, length])?,
+                answer_408: Template::new(408, answers.answer_408, &[sozu_id, length])?,
+                answer_413: Template::new(413, answers.answer_413, &[sozu_id, length, details])?,
+                answer_502: Template::new(502, answers.answer_502, &[sozu_id, length, details])?,
+                answer_503: Template::new(503, answers.answer_503, &[sozu_id, length, details])?,
+                answer_504: Template::new(504, answers.answer_504, &[sozu_id, length])?,
+                answer_507: Template::new(507, answers.answer_507, &[sozu_id, length, details])?,
             },
             custom: HashMap::new(),
-        }
+        })
     }
 
-    pub fn add_custom_answer(&mut self, cluster_id: &str, answer_503: &str) {
-        let owned_answer_503 = answer_503.to_owned();
+    pub fn add_custom_answer(
+        &mut self,
+        cluster_id: &str,
+        answer_503: String,
+    ) -> Result<(), (u16, TemplateError)> {
+        let answer_503 = Template::new(503, answer_503.into(), &[])?;
         self.custom
-            .entry(cluster_id.to_string())
-            .and_modify(|c| c.ServiceUnavailable = Some(Rc::new(owned_answer_503.into_bytes())))
-            .or_insert(CustomAnswers {
-                ServiceUnavailable: Some(Rc::new(answer_503.to_owned().into_bytes())),
-            });
+            .insert(cluster_id.to_string(), CustomAnswers { answer_503 });
+        Ok(())
     }
 
     pub fn remove_custom_answer(&mut self, cluster_id: &str) {
         self.custom.remove(cluster_id);
     }
 
-    pub fn get(&self, answer: DefaultAnswerStatus, cluster_id: Option<&str>) -> Rc> {
-        match answer {
-            DefaultAnswerStatus::Answer301 => panic!("the 301 answer is generated dynamically"),
-            DefaultAnswerStatus::Answer400 => self.default.BadRequest.clone(),
-            DefaultAnswerStatus::Answer401 => self.default.Unauthorized.clone(),
-            DefaultAnswerStatus::Answer404 => self.default.NotFound.clone(),
-            DefaultAnswerStatus::Answer408 => self.default.RequestTimeout.clone(),
-            DefaultAnswerStatus::Answer413 => self.default.PayloadTooLarge.clone(),
-            DefaultAnswerStatus::Answer502 => self.default.BadGateway.clone(),
-            DefaultAnswerStatus::Answer503 => cluster_id
-                .and_then(|id: &str| self.custom.get(id))
-                .and_then(|c| c.ServiceUnavailable.clone())
-                .unwrap_or_else(|| self.default.ServiceUnavailable.clone()),
-            DefaultAnswerStatus::Answer504 => self.default.GatewayTimeout.clone(),
-            DefaultAnswerStatus::Answer507 => self.default.InsufficientStorage.clone(),
-        }
+    pub fn get(
+        &self,
+        answer: DefaultAnswer,
+        cluster_id: Option<&str>,
+        request_id: String,
+    ) -> DefaultAnswerStream {
+        let mut variables_once: Vec>;
+        let template = match answer {
+            DefaultAnswer::Answer301 { location } => {
+                variables_once = vec![location.into()];
+                &self.default.answer_301
+            }
+            DefaultAnswer::Answer400 { details } => {
+                variables_once = vec![details.into()];
+                &self.default.answer_400
+            }
+            DefaultAnswer::Answer401 {} => {
+                variables_once = vec![];
+                &self.default.answer_401
+            }
+            DefaultAnswer::Answer404 {} => {
+                variables_once = vec![];
+                &self.default.answer_404
+            }
+            DefaultAnswer::Answer408 {} => {
+                variables_once = vec![];
+                &self.default.answer_408
+            }
+            DefaultAnswer::Answer413 { details } => {
+                variables_once = vec![details.into()];
+                &self.default.answer_413
+            }
+            DefaultAnswer::Answer502 { details } => {
+                variables_once = vec![details.into()];
+                &self.default.answer_502
+            }
+            DefaultAnswer::Answer503 { details } => {
+                variables_once = vec![details.into()];
+                cluster_id
+                    .and_then(|id: &str| self.custom.get(id))
+                    .map(|c| &c.answer_503)
+                    .unwrap_or_else(|| &self.default.answer_503)
+            }
+            DefaultAnswer::Answer504 {} => {
+                variables_once = vec![];
+                &self.default.answer_504
+            }
+            DefaultAnswer::Answer507 { details } => {
+                variables_once = vec![details.into()];
+                &self.default.answer_507
+            }
+        };
+        // kawa::debug_kawa(&template.kawa);
+        // println!("{template:#?}");
+        template.fill(&[request_id.as_bytes()], &mut variables_once)
     }
 }
diff --git a/lib/src/protocol/kawa_h1/mod.rs b/lib/src/protocol/kawa_h1/mod.rs
index 1d1b5a34f..12785fc95 100644
--- a/lib/src/protocol/kawa_h1/mod.rs
+++ b/lib/src/protocol/kawa_h1/mod.rs
@@ -22,7 +22,7 @@ use crate::{
     logs::{Endpoint, LogContext, RequestRecord},
     pool::{Checkout, Pool},
     protocol::{
-        http::{editor::HttpContext, parser::Method},
+        http::{answers::DefaultAnswerStream, editor::HttpContext, parser::Method},
         SessionState,
     },
     retry::RetryPolicy,
@@ -49,40 +49,32 @@ impl kawa::AsBuffer for Checkout {
 }
 
 #[derive(Debug, Clone, PartialEq, Eq)]
-pub enum SessionStatus {
-    Normal,
-    /// status, HTTP answer, index in HTTP answer
-    DefaultAnswer(DefaultAnswerStatus, Rc>, usize),
+pub enum DefaultAnswer {
+    Answer301 { location: String },
+    Answer400 { details: String },
+    Answer401 {},
+    Answer404 {},
+    Answer408 {},
+    Answer413 { details: String },
+    Answer502 { details: String },
+    Answer503 { details: String },
+    Answer504 {},
+    Answer507 { details: String },
 }
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum DefaultAnswerStatus {
-    Answer301,
-    Answer400,
-    Answer401,
-    Answer404,
-    Answer408,
-    Answer413,
-    Answer502,
-    Answer503,
-    Answer504,
-    Answer507,
-}
-
-#[allow(clippy::from_over_into)]
-impl Into for DefaultAnswerStatus {
-    fn into(self) -> u16 {
-        match self {
-            Self::Answer301 => 301,
-            Self::Answer400 => 400,
-            Self::Answer401 => 401,
-            Self::Answer404 => 404,
-            Self::Answer408 => 408,
-            Self::Answer413 => 413,
-            Self::Answer502 => 502,
-            Self::Answer503 => 503,
-            Self::Answer504 => 504,
-            Self::Answer507 => 507,
+impl From<&DefaultAnswer> for u16 {
+    fn from(answer: &DefaultAnswer) -> u16 {
+        match answer {
+            DefaultAnswer::Answer301 { .. } => 301,
+            DefaultAnswer::Answer400 { .. } => 400,
+            DefaultAnswer::Answer401 { .. } => 401,
+            DefaultAnswer::Answer404 { .. } => 404,
+            DefaultAnswer::Answer408 { .. } => 408,
+            DefaultAnswer::Answer413 { .. } => 413,
+            DefaultAnswer::Answer502 { .. } => 502,
+            DefaultAnswer::Answer503 { .. } => 503,
+            DefaultAnswer::Answer504 { .. } => 504,
+            DefaultAnswer::Answer507 { .. } => 507,
         }
     }
 }
@@ -95,6 +87,11 @@ pub enum TimeoutStatus {
     WaitingForResponse,
 }
 
+pub enum ResponseStream {
+    BackendAnswer(GenericHttpStream),
+    DefaultAnswer(u16, DefaultAnswerStream),
+}
+
 /// Http will be contained in State which itself is contained by Session
 pub struct Http {
     answers: Rc>,
@@ -119,8 +116,7 @@ pub struct Http {
     keepalive_count: usize,
     listener: Rc>,
     pub request_stream: GenericHttpStream,
-    pub response_stream: GenericHttpStream,
-    status: SessionStatus,
+    pub response_stream: ResponseStream,
     /// The HTTP context was separated from the State for borrowing reasons.
     /// Calling a kawa parser mutably borrows the State through request_stream or response_stream,
     /// so Http can't be borrowed again to be used in callbacks. HttContext is an independant
@@ -190,11 +186,10 @@ impl Http Http response_stream,
+            _ => return,
+        };
+
         self.context.keep_alive_frontend = true;
         self.context.keep_alive_backend = true;
         self.context.sticky_session_found = None;
         self.context.id = Ulid::generate();
 
         self.request_stream.clear();
-        self.response_stream.clear();
+        response_stream.clear();
         self.keepalive_count += 1;
         gauge_add!("http.active_requests", -1);
 
@@ -248,22 +248,20 @@ impl Http Http response_stream,
+            ResponseStream::DefaultAnswer(..) => {
+                error!(
+                    "{}\tsending default answer, should not read from front socket",
+                    self.log_context()
+                );
+                self.frontend_readiness.interest.remove(Ready::READABLE);
+                self.frontend_readiness.interest.insert(Ready::WRITABLE);
+                return StateResult::Continue;
+            }
+        };
 
         if self.request_stream.storage.is_full() {
             self.frontend_readiness.interest.remove(Ready::READABLE);
@@ -297,7 +298,10 @@ impl Http Http{:?}]: read {} bytes",
-            self.log_context(),
-            self.frontend_token.0,
-            self.backend_token,
-            size
+            "self.log_context()", self.frontend_token.0, self.backend_token, size
         );
 
         if size > 0 {
@@ -353,10 +354,6 @@ impl Http {}
         };
 
-        self.readable_parse(metrics)
-    }
-
-    pub fn readable_parse(&mut self, metrics: &mut SessionMetrics) -> StateResult {
         trace!("==============readable_parse");
         let was_initial = self.request_stream.is_initial();
         let was_not_proxying = !self.request_stream.is_main_phase();
@@ -378,7 +375,7 @@ impl Http {
@@ -397,11 +394,13 @@ impl Http message.to_owned(),
                 }
             );
-            if self.response_stream.consumed {
+            if response_stream.consumed {
                 self.log_request_error(metrics, "Parsing error on the request");
                 return StateResult::CloseSession;
             } else {
-                self.set_answer(DefaultAnswerStatus::Answer400, None);
+                self.set_answer(DefaultAnswer::Answer400 {
+                    details: format!("{marker:?}: {kind:?}"),
+                });
                 return StateResult::Continue;
             }
         }
@@ -424,14 +423,14 @@ impl Http StateResult {
         trace!("==============writable");
-        //handle default answers
-        if let SessionStatus::DefaultAnswer(_, _, _) = self.status {
-            return self.writable_default_answer(metrics);
-        }
+        let response_stream = match &mut self.response_stream {
+            ResponseStream::BackendAnswer(response_stream) => response_stream,
+            _ => return self.writable_default_answer(metrics),
+        };
 
-        self.response_stream.prepare(&mut kawa::h1::BlockConverter);
+        response_stream.prepare(&mut kawa::h1::BlockConverter);
 
-        let bufs = self.response_stream.as_io_slice();
+        let bufs = response_stream.as_io_slice();
         if bufs.is_empty() {
             self.frontend_readiness.interest.remove(Ready::WRITABLE);
             return StateResult::Continue;
@@ -440,14 +439,11 @@ impl Http 0 {
-            self.response_stream.consume(size);
+            response_stream.consume(size);
             count!("bytes_out", size as i64);
             metrics.bout += size;
             self.backend_readiness.interest.insert(Ready::READABLE);
@@ -474,8 +470,7 @@ impl Http {}
         }
 
-        if self.response_stream.is_terminated() && self.response_stream.is_completed() {
-            self.log_request_success(metrics);
+        if response_stream.is_terminated() && response_stream.is_completed() {
             metrics.reset();
 
             if self.context.closing {
@@ -483,26 +478,29 @@ impl Http {
                     trace!("============== HANDLE UPGRADE!");
+                    self.log_request_success(metrics);
                     return StateResult::Upgrade;
                 }
                 kawa::StatusLine::Response { code: 100, .. } => {
                     trace!("============== HANDLE CONTINUE!");
-                    self.response_stream.clear();
+                    response_stream.clear();
+                    self.log_request_success(metrics);
                     return StateResult::Continue;
                 }
                 kawa::StatusLine::Response { code: 103, .. } => {
                     self.backend_readiness.event.insert(Ready::READABLE);
                     trace!("============== HANDLE EARLY HINT!");
-                    self.response_stream.clear();
+                    response_stream.clear();
+                    self.log_request_success(metrics);
                     return StateResult::Continue;
                 }
                 _ => (),
             }
 
-            let response_length_known = self.response_stream.body_size != kawa::BodySize::Empty;
+            let response_length_known = response_stream.body_size != kawa::BodySize::Empty;
             let request_length_known = self.request_stream.body_size != kawa::BodySize::Empty;
             if !(self.request_stream.is_terminated() && self.request_stream.is_completed())
                 && request_length_known
@@ -523,10 +521,11 @@ impl Http {
                     debug!("{} keep alive front/back", self.log_context());
@@ -547,9 +546,46 @@ impl Http StateResult {
+        trace!("==============writable_default_answer");
+        let response_stream = match &mut self.response_stream {
+            ResponseStream::DefaultAnswer(_, response_stream) => response_stream,
+            _ => return StateResult::CloseSession,
+        };
+        let bufs = response_stream.as_io_slice();
+        let (size, socket_state) = self.frontend_socket.socket_write_vectored(&bufs);
+
+        count!("bytes_out", size as i64);
+        metrics.bout += size;
+        response_stream.consume(size);
+
+        if size == 0 || socket_state != SocketResult::Continue {
+            self.frontend_readiness.event.remove(Ready::WRITABLE);
+        }
+
+        if response_stream.is_completed() {
+            save_http_status_metric(self.context.status, self.log_context());
+            self.log_default_answer_success(metrics);
+            self.frontend_readiness.reset();
+            self.backend_readiness.reset();
+            return StateResult::CloseSession;
+        }
+
+        if socket_state == SocketResult::Error {
+            self.frontend_socket.write_error();
+            self.log_request_error(
+                metrics,
+                "error writing default answer to front socket, closing",
+            );
+            StateResult::CloseSession
+        } else {
+            StateResult::Continue
+        }
+    }
+
     pub fn backend_writable(&mut self, metrics: &mut SessionMetrics) -> SessionResult {
         trace!("==============backend_writable");
-        if let SessionStatus::DefaultAnswer(_, _, _) = self.status {
+        if let ResponseStream::DefaultAnswer(..) = self.response_stream {
             error!(
                 "{}\tsending default answer, should not write to back",
                 self.log_context()
@@ -635,15 +671,18 @@ impl Http response_stream,
+            _ => {
+                error!(
+                    "{}\tsending default answer, should not read from back socket",
+                    self.log_context()
+                );
+                self.backend_readiness.interest.remove(Ready::READABLE);
+                self.frontend_readiness.interest.insert(Ready::WRITABLE);
+                return SessionResult::Continue;
+            }
+        };
 
         let backend_socket = if let Some(backend_socket) = &mut self.backend_socket {
             backend_socket
@@ -652,18 +691,22 @@ impl Http Http 0 {
-            self.response_stream.storage.fill(size);
+            response_stream.storage.fill(size);
             count!("back_bytes_in", size as i64);
             metrics.backend_bin += size;
             // if self.kawa_response.storage.is_full() {
@@ -703,49 +746,46 @@ impl Http {}
         }
 
-        self.backend_readable_parse(metrics)
-    }
-
-    pub fn backend_readable_parse(&mut self, metrics: &mut SessionMetrics) -> SessionResult {
         trace!("==============backend_readable_parse");
-        kawa::h1::parse(&mut self.response_stream, &mut self.context);
+        kawa::h1::parse(response_stream, &mut self.context);
         // kawa::debug_kawa(&self.response_stream);
 
-        if let kawa::ParsingPhase::Error { marker, kind } = self.response_stream.parsing_phase {
+        if let kawa::ParsingPhase::Error { marker, kind } = response_stream.parsing_phase {
             incr!("http.backend_parse_errors");
             warn!(
                 "{} Parsing response error in {:?}: {}",
-                self.log_context(),
+                "self.log_context()",
                 marker,
                 match kind {
                     kawa::ParsingErrorKind::Consuming { index } => {
-                        let kawa = &self.response_stream;
                         parser::view(
-                            kawa.storage.used(),
+                            response_stream.storage.used(),
                             16,
                             &[
-                                kawa.storage.start,
-                                kawa.storage.head,
+                                response_stream.storage.start,
+                                response_stream.storage.head,
                                 index as usize,
-                                kawa.storage.end,
+                                response_stream.storage.end,
                             ],
                         )
                     }
                     kawa::ParsingErrorKind::Processing { message } => message.to_owned(),
                 }
             );
-            if self.response_stream.consumed {
+            if response_stream.consumed {
                 return SessionResult::Close;
             } else {
-                self.set_answer(DefaultAnswerStatus::Answer502, None);
+                self.set_answer(DefaultAnswer::Answer502 {
+                    details: format!("{marker:?}: {kind:?}"),
+                });
                 return SessionResult::Continue;
             }
         }
 
-        if self.response_stream.is_main_phase() {
+        if response_stream.is_main_phase() {
             self.frontend_readiness.interest.insert(Ready::WRITABLE);
         }
-        if self.response_stream.is_terminated() {
+        if response_stream.is_terminated() {
             metrics.backend_stop();
             self.backend_stop = Some(Instant::now());
             self.backend_readiness.interest.remove(Ready::READABLE);
@@ -811,7 +851,7 @@ impl Http) {
+    pub fn log_request(&self, metrics: &SessionMetrics, message: Option<&str>) {
         let listener = self.listener.borrow();
         let tags = self.context.authority.as_ref().and_then(|host| {
             let hostname = match host.split_once(':') {
@@ -820,12 +860,8 @@ impl Http self.context.status,
-            SessionStatus::DefaultAnswer(answers, ..) => Some(answers.into()),
-        };
 
-        let user_agent = self.context.user_agent.take();
+        let user_agent = self.context.user_agent.clone();
         RequestRecord {
             error: message,
             context: self.log_context(),
@@ -836,7 +872,7 @@ impl Http Http Http>>) {
-        if let SessionStatus::DefaultAnswer(status, _, _) = self.status {
+    pub fn set_answer(&mut self, answer: DefaultAnswer) {
+        let status = u16::from(&answer);
+        if let ResponseStream::DefaultAnswer(old_status, ..) = self.response_stream {
             error!(
-                "already set the default answer to {:?}, trying to set to {:?}",
-                status, answer
+                "already set the default answer to {}, trying to set to {}",
+                old_status, status
             );
         } else {
             match answer {
-                DefaultAnswerStatus::Answer301 => incr!(
+                DefaultAnswer::Answer301 { .. } => incr!(
                     "http.301.redirection",
                     self.cluster_id.as_deref(),
                     self.backend_id.as_deref()
                 ),
-                DefaultAnswerStatus::Answer400 => incr!("http.400.errors"),
-                DefaultAnswerStatus::Answer401 => incr!(
+                DefaultAnswer::Answer400 { .. } => incr!("http.400.errors"),
+                DefaultAnswer::Answer401 { .. } => incr!(
                     "http.401.errors",
                     self.cluster_id.as_deref(),
                     self.backend_id.as_deref()
                 ),
-                DefaultAnswerStatus::Answer404 => incr!("http.404.errors"),
-                DefaultAnswerStatus::Answer408 => incr!(
+                DefaultAnswer::Answer404 { .. } => incr!("http.404.errors"),
+                DefaultAnswer::Answer408 { .. } => incr!(
                     "http.408.errors",
                     self.cluster_id.as_deref(),
                     self.backend_id.as_deref()
                 ),
-                DefaultAnswerStatus::Answer413 => incr!(
+                DefaultAnswer::Answer413 { .. } => incr!(
                     "http.413.errors",
                     self.cluster_id.as_deref(),
                     self.backend_id.as_deref()
                 ),
-                DefaultAnswerStatus::Answer502 => incr!(
+                DefaultAnswer::Answer502 { .. } => incr!(
                     "http.502.errors",
                     self.cluster_id.as_deref(),
                     self.backend_id.as_deref()
                 ),
-                DefaultAnswerStatus::Answer503 => incr!(
+                DefaultAnswer::Answer503 { .. } => incr!(
                     "http.503.errors",
                     self.cluster_id.as_deref(),
                     self.backend_id.as_deref()
                 ),
-                DefaultAnswerStatus::Answer504 => incr!(
+                DefaultAnswer::Answer504 { .. } => incr!(
                     "http.504.errors",
                     self.cluster_id.as_deref(),
                     self.backend_id.as_deref()
                 ),
-                DefaultAnswerStatus::Answer507 => incr!(
+                DefaultAnswer::Answer507 { .. } => incr!(
                     "http.507.errors",
                     self.cluster_id.as_deref(),
                     self.backend_id.as_deref()
@@ -919,64 +956,20 @@ impl Http StateResult {
-        trace!("==============writable_default_answer");
-        let socket_result = match self.status {
-            SessionStatus::DefaultAnswer(status, ref buf, mut index) => {
-                let len = buf.len();
-
-                let mut sz = 0usize;
-                let mut socket_result = SocketResult::Continue;
-                while socket_result == SocketResult::Continue && index < len {
-                    let (current_sz, current_res) =
-                        self.frontend_socket.socket_write(&buf[index..]);
-                    socket_result = current_res;
-                    sz += current_sz;
-                    index += current_sz;
-                }
-
-                count!("bytes_out", sz as i64);
-                metrics.bout += sz;
-
-                if socket_result != SocketResult::Continue {
-                    self.frontend_readiness.event.remove(Ready::WRITABLE);
-                }
-
-                if index == len {
-                    save_http_status_metric(Some(status.into()), self.log_context());
-                    self.log_default_answer_success(metrics);
-                    self.frontend_readiness.reset();
-                    self.backend_readiness.reset();
-                    return StateResult::CloseSession;
-                }
-
-                socket_result
-            }
-            _ => return StateResult::CloseSession,
-        };
-
-        if socket_result == SocketResult::Error {
-            self.frontend_socket.write_error();
-            self.log_request_error(
-                metrics,
-                "error writing default answer to front socket, closing",
-            );
-            StateResult::CloseSession
-        } else {
-            StateResult::Continue
-        }
-    }
-
     pub fn test_backend_socket(&self) -> bool {
         match self.backend_socket {
             Some(ref s) => {
@@ -1097,7 +1090,12 @@ impl Http Result<(), BackendConnectionError> {
         if self.connection_attempts >= CONN_RETRIES {
             error!("{} max connection attempt reached", self.log_context());
-            self.set_answer(DefaultAnswerStatus::Answer503, None);
+            self.set_answer(DefaultAnswer::Answer503 {
+                details: format!(
+                    "Max connection attempt reached: {}",
+                    self.connection_attempts
+                ),
+            });
             return Err(BackendConnectionError::MaxConnectionRetries(None));
         }
         Ok(())
@@ -1148,7 +1146,9 @@ impl Http tuple,
             Err(cluster_error) => {
-                self.set_answer(DefaultAnswerStatus::Answer400, None);
+                self.set_answer(DefaultAnswer::Answer400 {
+                    details: "...".into(),
+                });
                 return Err(cluster_error);
             }
         };
@@ -1161,7 +1161,7 @@ impl Http route,
             Err(frontend_error) => {
-                self.set_answer(DefaultAnswerStatus::Answer404, None);
+                self.set_answer(DefaultAnswer::Answer404 {});
                 return Err(RetrieveClusterError::RetrieveFrontend(frontend_error));
             }
         };
@@ -1169,7 +1169,7 @@ impl Http cluster_id,
             Route::Deny => {
-                self.set_answer(DefaultAnswerStatus::Answer401, None);
+                self.set_answer(DefaultAnswer::Answer401 {});
                 return Err(RetrieveClusterError::UnauthorizedRoute);
             }
         };
@@ -1183,11 +1183,9 @@ impl Http Http Http StateResult {
+        let response_stream = match &mut self.response_stream {
+            ResponseStream::BackendAnswer(response_stream) => response_stream,
+            _ => return StateResult::CloseBackend,
+        };
+
         // there might still data we can read on the socket
         if self.backend_readiness.event.is_readable()
             && self.backend_readiness.interest.is_readable()
@@ -1470,12 +1475,12 @@ impl Http Http Http Http TimeoutStatus {
         if self.request_stream.is_main_phase() {
-            if self.response_stream.is_initial() {
-                TimeoutStatus::WaitingForResponse
-            } else {
-                TimeoutStatus::Response
+            match &self.response_stream {
+                ResponseStream::BackendAnswer(kawa) if kawa.is_initial() => {
+                    TimeoutStatus::WaitingForResponse
+                }
+                _ => TimeoutStatus::Response,
             }
         } else if self.keepalive_count > 0 {
             TimeoutStatus::WaitingForNewRequest
@@ -1708,16 +1716,20 @@ impl SessionState
     ) -> SessionResult {
         let session_result = self.ready_inner(session, proxy, metrics);
         if session_result == SessionResult::Upgrade {
+            let response_storage = match &mut self.response_stream {
+                ResponseStream::BackendAnswer(response_stream) => &mut response_stream.storage,
+                _ => return SessionResult::Close,
+            };
+
             // sync the underlying Checkout buffers, if they contain remaining data
             // it will be processed once upgraded to websocket
             self.request_stream.storage.buffer.sync(
                 self.request_stream.storage.end,
                 self.request_stream.storage.head,
             );
-            self.response_stream.storage.buffer.sync(
-                self.response_stream.storage.end,
-                self.response_stream.storage.head,
-            );
+            response_storage
+                .buffer
+                .sync(response_storage.end, response_storage.head);
         }
         session_result
     }
@@ -1750,11 +1762,11 @@ impl SessionState
             self.container_frontend_timeout.triggered();
             return match self.timeout_status() {
                 TimeoutStatus::Request => {
-                    self.set_answer(DefaultAnswerStatus::Answer408, None);
+                    self.set_answer(DefaultAnswer::Answer408 {});
                     self.writable(metrics)
                 }
                 TimeoutStatus::WaitingForResponse => {
-                    self.set_answer(DefaultAnswerStatus::Answer504, None);
+                    self.set_answer(DefaultAnswer::Answer504 {});
                     self.writable(metrics)
                 }
                 TimeoutStatus::Response => StateResult::CloseSession,
@@ -1770,11 +1782,11 @@ impl SessionState
                     error!(
                         "got backend timeout while waiting for a request, this should not happen"
                     );
-                    self.set_answer(DefaultAnswerStatus::Answer504, None);
+                    self.set_answer(DefaultAnswer::Answer504 {});
                     self.writable(metrics)
                 }
                 TimeoutStatus::WaitingForResponse => {
-                    self.set_answer(DefaultAnswerStatus::Answer504, None);
+                    self.set_answer(DefaultAnswer::Answer504 {});
                     self.writable(metrics)
                 }
                 TimeoutStatus::Response => {
@@ -1804,21 +1816,19 @@ impl SessionState
 \tFrontend:
 \t\ttoken: {:?}\treadiness: {:?}\tstate: {:?}
 \tBackend:
-\t\ttoken: {:?}\treadiness: {:?}\tstate: {:?}",
+\t\ttoken: {:?}\treadiness: {:?}\tstate: {{:?}}",
             context,
             self.frontend_token,
             self.frontend_readiness,
             self.request_stream.parsing_phase,
             self.backend_token,
-            self.backend_readiness,
-            self.response_stream.parsing_phase
+            self.backend_readiness // self.response_stream.parsing_phase
         );
     }
 
     fn shutting_down(&mut self) -> SessionIsToBeClosed {
-        if self.request_stream.is_initial()
-            && self.request_stream.storage.is_empty()
-            && self.response_stream.storage.is_empty()
+        if self.request_stream.is_initial() && self.request_stream.storage.is_empty()
+        // && self.response_stream.storage.is_empty()
         {
             true
         } else {