feat(provider): check for serde error with missing req id (#1910)

This commit is contained in:
Matthias Seitz 2022-11-29 16:48:58 +01:00 committed by GitHub
parent 79e64a8378
commit d48187ac0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 45 additions and 17 deletions

View File

@ -4,7 +4,7 @@
use super::{common::JsonRpcError, http::ClientError}; use super::{common::JsonRpcError, http::ClientError};
use crate::{provider::ProviderError, JsonRpcClient}; use crate::{provider::ProviderError, JsonRpcClient};
use async_trait::async_trait; use async_trait::async_trait;
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::{ use std::{
fmt::Debug, fmt::Debug,
sync::atomic::{AtomicU32, Ordering}, sync::atomic::{AtomicU32, Ordering},
@ -347,11 +347,8 @@ pub struct HttpRateLimitRetryPolicy;
impl RetryPolicy<ClientError> for HttpRateLimitRetryPolicy { impl RetryPolicy<ClientError> for HttpRateLimitRetryPolicy {
fn should_retry(&self, error: &ClientError) -> bool { fn should_retry(&self, error: &ClientError) -> bool {
match error { fn should_retry_json_rpc_error(err: &JsonRpcError) -> bool {
ClientError::ReqwestError(err) => { let JsonRpcError { code, message, .. } = err;
err.status() == Some(http::StatusCode::TOO_MANY_REQUESTS)
}
ClientError::JsonRpcError(JsonRpcError { code, message, .. }) => {
// alchemy throws it this way // alchemy throws it this way
if *code == 429 { if *code == 429 {
return true return true
@ -370,7 +367,25 @@ impl RetryPolicy<ClientError> for HttpRateLimitRetryPolicy {
_ => false, _ => false,
} }
} }
_ => false,
match error {
ClientError::ReqwestError(err) => {
err.status() == Some(http::StatusCode::TOO_MANY_REQUESTS)
}
ClientError::JsonRpcError(err) => should_retry_json_rpc_error(err),
ClientError::SerdeJson { text, .. } => {
// some providers send invalid JSON RPC in the error case (no `id:u64`), but the
// text should be a `JsonRpcError`
#[derive(Deserialize)]
struct Resp {
error: JsonRpcError,
}
if let Ok(resp) = serde_json::from_str::<Resp>(text) {
return should_retry_json_rpc_error(&resp.error)
}
false
}
} }
} }
@ -526,4 +541,17 @@ mod tests {
let should_retry = HttpRateLimitRetryPolicy::default().should_retry(&err); let should_retry = HttpRateLimitRetryPolicy::default().should_retry(&err);
assert!(should_retry); assert!(should_retry);
} }
#[test]
fn test_rate_limit_omitted_id() {
let s = r#"{"jsonrpc":"2.0","error":{"code":-32016,"message":"Your IP has exceeded its requests per second capacity. To increase your rate limits, please sign up for a free Alchemy account at https://www.alchemy.com/optimism."},"id":null}"#;
let err = ClientError::SerdeJson {
err: serde::de::Error::custom("unexpected notification over HTTP transport"),
text: s.to_string(),
};
let should_retry = HttpRateLimitRetryPolicy::default().should_retry(&err);
assert!(should_retry);
}
} }