Open cetra3 opened 1 week ago
Where are you trying to write the error? Ideally this is would happen in fail_to_proxy
.
/// Users may write an error response to the downstream if the downstream is still writable.
Implementing the fail_to_proxy
method doesn't work either:
async fn fail_to_proxy(&self, session: &mut Session, e: &Error, _ctx: &mut Self::CTX) -> u16
where
Self::CTX: Send + Sync,
{
let server_session = session.as_mut();
let code = match e.etype() {
HTTPStatus(code) => *code,
_ => {
match e.esource() {
ErrorSource::Upstream => 502,
ErrorSource::Downstream => {
match e.etype() {
WriteError | ReadError | ConnectionClosed => {
/* conn already dead */
0
}
_ => 400,
}
}
ErrorSource::Internal | ErrorSource::Unset => 500,
}
}
};
if code > 0 {
server_session.respond_error(code).await;
if let Some(ref ctx) = e.context {
// Nothing is written to the downstream client
server_session.write_response_body(ctx.to_string().into(), true).await.ok();
}
}
code
}
The respond_error()
function uses gen_error_response()
which has a default Content-Length: 0
. so the body is being ignored when you try to write it.
You can either calculate the length of the error message and set the correct content length or use chunked encoding on the error response. Either way you'll need to construct your own error response headers.
IE:
fn gen_chunked_error_response(code: u16) -> ResponseHeader {
let mut resp = ResponseHeader::build(code, Some(4)).unwrap();
resp.insert_header(header::SERVER, &SERVER_NAME[..])
.unwrap();
resp.insert_header(header::DATE, "Sun, 06 Nov 1994 08:49:37 GMT")
.unwrap(); // placeholder
resp.insert_header(header::TRANSFER_ENCODING, "chunked")
.unwrap();
resp.insert_header(header::CACHE_CONTROL, "private, no-store")
.unwrap();
resp
}
Then use write_response_header()
and write_response_body()
.
We actually have some code that handles error responses with bodies internally, will move this out so this is easier for others.
What is the problem your feature solves, or the need it fulfills?
At the moment the context of the error is logged, but that information isn't sent to the downstream client. We're using pingora for some internal proxying, and while we can get an error status back when things go wrong, the context is lost. It'd be great if there was a way to send an error back to the client.
Describe the solution you'd like
A way to specify that the error context is returned in the response body to clients.
Describe alternatives you've considered
As the error is happening within the
upstream_peer
of theProxyHttp
trait, I tried setting the session's downstream response body directly, but this doesn't appear to work. Instead no body is sent back to the client downstream.I.e, doing something like this:
And also tried
write_response_body
directly: