ethers_providers/rpc/transports/
http.rs1use super::common::{Authorization, JsonRpcError, Request, Response};
4use crate::{errors::ProviderError, JsonRpcClient};
5use async_trait::async_trait;
6use reqwest::{header::HeaderValue, Client, Error as ReqwestError};
7use serde::{de::DeserializeOwned, Serialize};
8use std::{
9 str::FromStr,
10 sync::atomic::{AtomicU64, Ordering},
11};
12use thiserror::Error;
13use url::Url;
14
15#[derive(Debug)]
31pub struct Provider {
32 id: AtomicU64,
33 client: Client,
34 url: Url,
35}
36
37#[derive(Error, Debug)]
38pub enum ClientError {
40 #[error(transparent)]
42 ReqwestError(#[from] ReqwestError),
43 #[error(transparent)]
44 JsonRpcError(#[from] JsonRpcError),
46
47 #[error("Deserialization Error: {err}. Response: {text}")]
48 SerdeJson {
50 err: serde_json::Error,
52 text: String,
54 },
55}
56
57impl From<ClientError> for ProviderError {
58 fn from(src: ClientError) -> Self {
59 match src {
60 ClientError::ReqwestError(err) => ProviderError::HTTPError(err),
61 _ => ProviderError::JsonRpcClientError(Box::new(src)),
62 }
63 }
64}
65
66impl crate::RpcError for ClientError {
67 fn as_error_response(&self) -> Option<&super::JsonRpcError> {
68 if let ClientError::JsonRpcError(err) = self {
69 Some(err)
70 } else {
71 None
72 }
73 }
74
75 fn as_serde_error(&self) -> Option<&serde_json::Error> {
76 match self {
77 ClientError::SerdeJson { err, .. } => Some(err),
78 _ => None,
79 }
80 }
81}
82
83#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
84#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
85impl JsonRpcClient for Provider {
86 type Error = ClientError;
87
88 async fn request<T: Serialize + Send + Sync, R: DeserializeOwned>(
89 &self,
90 method: &str,
91 params: T,
92 ) -> Result<R, ClientError> {
93 let next_id = self.id.fetch_add(1, Ordering::SeqCst);
94 let payload = Request::new(next_id, method, params);
95
96 let res = self.client.post(self.url.as_ref()).json(&payload).send().await?;
97 let body = res.bytes().await?;
98
99 let raw = match serde_json::from_slice(&body) {
100 Ok(Response::Success { result, .. }) => result.to_owned(),
101 Ok(Response::Error { error, .. }) => return Err(error.into()),
102 Ok(_) => {
103 let err = ClientError::SerdeJson {
104 err: serde::de::Error::custom("unexpected notification over HTTP transport"),
105 text: String::from_utf8_lossy(&body).to_string(),
106 };
107 return Err(err)
108 }
109 Err(err) => {
110 return Err(ClientError::SerdeJson {
111 err,
112 text: String::from_utf8_lossy(&body).to_string(),
113 })
114 }
115 };
116
117 let res = serde_json::from_str(raw.get())
118 .map_err(|err| ClientError::SerdeJson { err, text: raw.to_string() })?;
119
120 Ok(res)
121 }
122}
123
124impl Provider {
125 pub fn new(url: impl Into<Url>) -> Self {
137 Self::new_with_client(url, Client::new())
138 }
139
140 pub fn url(&self) -> &Url {
142 &self.url
143 }
144
145 pub fn url_mut(&mut self) -> &mut Url {
147 &mut self.url
148 }
149
150 pub fn new_with_auth(
162 url: impl Into<Url>,
163 auth: Authorization,
164 ) -> Result<Self, HttpClientError> {
165 let mut auth_value = HeaderValue::from_str(&auth.to_string())?;
166 auth_value.set_sensitive(true);
167
168 let mut headers = reqwest::header::HeaderMap::new();
169 headers.insert(reqwest::header::AUTHORIZATION, auth_value);
170
171 let client = Client::builder().default_headers(headers).build()?;
172
173 Ok(Self::new_with_client(url, client))
174 }
175
176 pub fn new_with_client(url: impl Into<Url>, client: reqwest::Client) -> Self {
189 Self { id: AtomicU64::new(1), client, url: url.into() }
190 }
191}
192
193impl FromStr for Provider {
194 type Err = url::ParseError;
195
196 fn from_str(src: &str) -> Result<Self, Self::Err> {
197 let url = Url::parse(src)?;
198 Ok(Provider::new(url))
199 }
200}
201
202impl Clone for Provider {
203 fn clone(&self) -> Self {
204 Self { id: AtomicU64::new(1), client: self.client.clone(), url: self.url.clone() }
205 }
206}
207
208#[derive(Error, Debug)]
209pub enum HttpClientError {
211 #[error(transparent)]
213 InvalidHeader(#[from] http::header::InvalidHeaderValue),
214
215 #[error(transparent)]
217 ClientBuild(#[from] reqwest::Error),
218}