feat(router): send connector timeouts and connection closures as 2xx response instead of giving 5xx response (#2047)

This commit is contained in:
Sai Harsha Vardhan
2023-08-31 17:25:47 +05:30
committed by GitHub
parent c9fe389b2c
commit 31088b6062
6 changed files with 54 additions and 5 deletions

View File

@ -13,6 +13,9 @@ pub(crate) const ALPHABETS: [char; 62] = [
pub const REQUEST_TIME_OUT: u64 = 30;
pub const REQUEST_TIMEOUT_ERROR_CODE: &str = "TIMEOUT";
pub const REQUEST_TIMEOUT_ERROR_MESSAGE: &str = "Connector did not respond in specified time";
pub const CONNECTION_CLOSED_ERROR_CODE: &str = "CONNECTION_CLOSED";
pub const CONNECTION_CLOSED_ERROR_MESSAGE: &str =
"Connection closed before a message could complete";
///Payment intent fulfillment default timeout (in seconds)
pub const DEFAULT_FULFILLMENT_TIME: i64 = 15 * 60;

View File

@ -273,6 +273,9 @@ pub enum ApiClientError {
#[error("Server responded with Request Timeout")]
RequestTimeoutReceived,
#[error("connection closed before a message could complete")]
ConnectionClosed,
#[error("Server responded with Internal Server Error")]
InternalServerErrorReceived,
#[error("Server responded with Bad Gateway")]
@ -566,6 +569,9 @@ impl ApiClientError {
pub fn is_upstream_timeout(&self) -> bool {
self == &Self::RequestTimeoutReceived
}
pub fn is_connection_closed(&self) -> bool {
self == &Self::ConnectionClosed
}
}
impl ConnectorError {

View File

@ -172,7 +172,7 @@ pub async fn refresh_connector_auth(
code: consts::REQUEST_TIMEOUT_ERROR_CODE.to_string(),
message: consts::REQUEST_TIMEOUT_ERROR_MESSAGE.to_string(),
reason: Some(consts::REQUEST_TIMEOUT_ERROR_MESSAGE.to_string()),
status_code: 200,
status_code: 504,
};
Ok(Err(error_response))

View File

@ -3,6 +3,7 @@ pub mod request;
use std::{
collections::HashMap,
error::Error,
fmt::Debug,
future::Future,
str,
@ -384,8 +385,27 @@ where
}
Err(error) => {
if error.current_context().is_upstream_timeout() {
Err(error
.change_context(errors::ConnectorError::RequestTimeoutReceived))
let error_response = ErrorResponse {
code: consts::REQUEST_TIMEOUT_ERROR_CODE.to_string(),
message: consts::REQUEST_TIMEOUT_ERROR_MESSAGE.to_string(),
reason: Some(consts::REQUEST_TIMEOUT_ERROR_MESSAGE.to_string()),
status_code: 504,
};
router_data.response = Err(error_response);
router_data.connector_http_status_code = Some(504);
Ok(router_data)
} else if error.current_context().is_connection_closed() {
let error_response = ErrorResponse {
code: consts::CONNECTION_CLOSED_ERROR_CODE.to_string(),
message: consts::CONNECTION_CLOSED_ERROR_MESSAGE.to_string(),
reason: Some(
consts::CONNECTION_CLOSED_ERROR_MESSAGE.to_string(),
),
status_code: 504,
};
router_data.response = Err(error_response);
router_data.connector_http_status_code = Some(504);
Ok(router_data)
} else {
Err(error.change_context(
errors::ConnectorError::ProcessingStepFailed(None),
@ -505,6 +525,10 @@ pub async fn send_request(
metrics::REQUEST_BUILD_FAILURE.add(&metrics::CONTEXT, 1, &[]);
errors::ApiClientError::RequestTimeoutReceived
}
error if is_connection_closed(&error) => {
metrics::REQUEST_BUILD_FAILURE.add(&metrics::CONTEXT, 1, &[]);
errors::ApiClientError::ConnectionClosed
}
_ => errors::ApiClientError::RequestNotSent(error.to_string()),
})
.into_report()
@ -519,6 +543,19 @@ pub async fn send_request(
.await
}
fn is_connection_closed(error: &reqwest::Error) -> bool {
let mut source = error.source();
while let Some(err) = source {
if let Some(hyper_err) = err.downcast_ref::<hyper::Error>() {
if hyper_err.is_incomplete_message() {
return true;
}
}
source = err.source();
}
false
}
#[instrument(skip_all)]
async fn handle_response(
response: CustomResult<reqwest::Response, errors::ApiClientError>,