mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-03 21:37:41 +08:00
feat(router): send connector timeouts and connection closures as 2xx response instead of giving 5xx response (#2047)
This commit is contained in:
committed by
GitHub
parent
c9fe389b2c
commit
31088b6062
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -2362,9 +2362,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.26"
|
||||
version = "0.14.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4"
|
||||
checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
@ -3789,6 +3789,8 @@ dependencies = [
|
||||
"futures",
|
||||
"hex",
|
||||
"http",
|
||||
"hyper",
|
||||
"image",
|
||||
"infer 0.13.0",
|
||||
"josekit",
|
||||
"jsonwebtoken",
|
||||
|
||||
@ -51,6 +51,7 @@ error-stack = "0.3.1"
|
||||
futures = "0.3.28"
|
||||
hex = "0.4.3"
|
||||
http = "0.2.9"
|
||||
hyper = "0.14.27"
|
||||
image = "0.23.14"
|
||||
infer = "0.13.0"
|
||||
josekit = "0.8.3"
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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>,
|
||||
|
||||
Reference in New Issue
Block a user