cratestack_axum/rpc/
error_encode.rs1use axum::http::HeaderMap;
5use cratestack_core::CratestackError;
6use cratestack_core::rpc::RpcErrorBody;
7use serde::Serialize;
8
9use crate::HttpTransport;
10
11use super::RPC_BINDING_CAPABILITIES;
12use super::codec_helpers::decode_rpc_body;
13use super::util::synthesize_error_for_status;
14
15pub fn encode_rpc_error<C>(
22 codec: &C,
23 headers: &HeaderMap,
24 error: &CratestackError,
25) -> axum::response::Response
26where
27 C: HttpTransport,
28{
29 let disowned = crate::idempotency::disowned(error);
32 let error = disowned.as_ref().unwrap_or(error);
33 let body = RpcErrorBody::from_cratestack(error);
34 let status = error.status_code();
35 let response = encode_rpc_value_response(codec, headers, status, body);
36 let response = crate::auth_challenge::attach(response, error.auth_challenges());
37 crate::idempotency::with_tag(crate::idempotency::unrecorded_tag(error), response)
38}
39
40pub async fn convert_handler_error_response<C>(
49 response: axum::response::Response,
50 codec: &C,
51 headers: &HeaderMap,
52) -> axum::response::Response
53where
54 C: HttpTransport,
55{
56 if response.status().is_success() {
57 return response;
58 }
59 let tag = crate::idempotency::tag_of(&response);
62 let converted = convert_error_body(response, codec, headers).await;
63 crate::idempotency::with_tag(tag, converted)
64}
65
66async fn convert_error_body<C>(
67 response: axum::response::Response,
68 codec: &C,
69 headers: &HeaderMap,
70) -> axum::response::Response
71where
72 C: HttpTransport,
73{
74 let (parts, body) = response.into_parts();
78 let status = parts.status;
79 let challenge_headers = parts.headers;
80 let body_bytes =
81 match axum::body::to_bytes(body, cratestack_core::MAX_RESPONSE_REBUFFER_BYTES).await {
82 Ok(bytes) => bytes.to_vec(),
83 Err(error) => {
84 let cool = CratestackError::Internal(format!("buffer handler error body: {error}"));
86 return encode_rpc_error(codec, headers, &cool);
87 }
88 };
89
90 let rpc_body = match decode_rpc_body::<_, cratestack_core::CratestackErrorResponse>(
91 codec,
92 headers,
93 &body_bytes,
94 ) {
95 Ok(parsed) => RpcErrorBody::from_cratestack_response(parsed),
96 Err(_) => {
97 let cool = synthesize_error_for_status(status);
102 RpcErrorBody::from_cratestack(&cool)
103 }
104 };
105
106 let response = encode_rpc_value_response(codec, headers, status, rpc_body);
107 crate::auth_challenge::copy(challenge_headers, response)
108}
109
110fn encode_rpc_value_response<C, T>(
111 codec: &C,
112 headers: &HeaderMap,
113 status: axum::http::StatusCode,
114 value: T,
115) -> axum::response::Response
116where
117 C: HttpTransport,
118 T: Serialize,
119{
120 crate::encode_transport_result_with_status_for::<_, T>(
123 codec,
124 headers,
125 &RPC_BINDING_CAPABILITIES,
126 status,
127 Ok(value),
128 )
129}