starknet_rust_providers/jsonrpc/transports/
http.rs1use async_trait::async_trait;
2use log::trace;
3use reqwest::{Client, Url};
4use serde::{de::DeserializeOwned, Serialize};
5
6use crate::{
7 jsonrpc::{transports::JsonRpcTransport, JsonRpcMethod, JsonRpcResponse},
8 ProviderRequestData,
9};
10
11#[derive(Debug, Clone)]
13pub struct HttpTransport {
14 client: Client,
15 url: Url,
16 headers: Vec<(String, String)>,
17}
18
19#[derive(Debug, thiserror::Error)]
21#[error(transparent)]
22pub enum HttpTransportError {
23 Reqwest(reqwest::Error),
25 Json(serde_json::Error),
27 #[error("unexpected response ID: {0}")]
29 UnexpectedResponseId(u64),
30}
31
32#[derive(Debug, Serialize)]
33struct JsonRpcRequest<T> {
34 id: u64,
35 jsonrpc: &'static str,
36 method: JsonRpcMethod,
37 params: T,
38}
39
40impl HttpTransport {
41 pub fn new(url: impl Into<Url>) -> Self {
46 Self::new_with_client(url, Client::new())
47 }
48
49 pub fn new_with_client(url: impl Into<Url>, client: Client) -> Self {
51 Self {
52 client,
53 url: url.into(),
54 headers: vec![],
55 }
56 }
57
58 pub fn with_header(self, name: String, value: String) -> Self {
61 let mut headers = self.headers;
62 headers.push((name, value));
63
64 Self {
65 client: self.client,
66 url: self.url,
67 headers,
68 }
69 }
70
71 pub fn add_header(&mut self, name: String, value: String) {
73 self.headers.push((name, value))
74 }
75}
76
77#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
78#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
79impl JsonRpcTransport for HttpTransport {
80 type Error = HttpTransportError;
81
82 async fn send_request<P, R>(
83 &self,
84 method: JsonRpcMethod,
85 params: P,
86 ) -> Result<JsonRpcResponse<R>, Self::Error>
87 where
88 P: Serialize + Send,
89 R: DeserializeOwned + Send,
90 {
91 let request_body = JsonRpcRequest {
92 id: 1,
93 jsonrpc: "2.0",
94 method,
95 params,
96 };
97
98 let request_body = serde_json::to_string(&request_body).map_err(Self::Error::Json)?;
99 trace!("Sending request via JSON-RPC: {}", request_body);
100
101 let mut request = self
102 .client
103 .post(self.url.clone())
104 .body(request_body)
105 .header("Content-Type", "application/json");
106 for (name, value) in &self.headers {
107 request = request.header(name, value);
108 }
109
110 let response = request.send().await.map_err(Self::Error::Reqwest)?;
111
112 let response_body = response.text().await.map_err(Self::Error::Reqwest)?;
113 trace!("Response from JSON-RPC: {}", response_body);
114
115 let parsed_response = serde_json::from_str(&response_body).map_err(Self::Error::Json)?;
116
117 Ok(parsed_response)
118 }
119
120 async fn send_requests<R>(
121 &self,
122 requests: R,
123 ) -> Result<Vec<JsonRpcResponse<serde_json::Value>>, Self::Error>
124 where
125 R: AsRef<[ProviderRequestData]> + Send + Sync,
126 {
127 let request_bodies = requests
128 .as_ref()
129 .iter()
130 .enumerate()
131 .map(|(ind, request)| JsonRpcRequest {
132 id: ind as u64,
133 jsonrpc: "2.0",
134 method: request.jsonrpc_method(),
135 params: request,
136 })
137 .collect::<Vec<_>>();
138
139 let request_count = request_bodies.len();
140
141 let request_body = serde_json::to_string(&request_bodies).map_err(Self::Error::Json)?;
142 trace!("Sending request via JSON-RPC: {}", request_body);
143
144 let mut request = self
145 .client
146 .post(self.url.clone())
147 .body(request_body)
148 .header("Content-Type", "application/json");
149 for (name, value) in &self.headers {
150 request = request.header(name, value);
151 }
152
153 let response = request.send().await.map_err(Self::Error::Reqwest)?;
154
155 let response_body = response.text().await.map_err(Self::Error::Reqwest)?;
156 trace!("Response from JSON-RPC: {}", response_body);
157
158 let parsed_response: Vec<JsonRpcResponse<serde_json::Value>> =
159 serde_json::from_str(&response_body).map_err(Self::Error::Json)?;
160
161 let mut responses: Vec<Option<JsonRpcResponse<serde_json::Value>>> = vec![];
162 responses.resize(request_bodies.len(), None);
163
164 for response_item in parsed_response {
166 let id = match &response_item {
167 JsonRpcResponse::Success { id, .. } | JsonRpcResponse::Error { id, .. } => {
168 *id as usize
169 }
170 };
171
172 if id >= request_count {
173 return Err(HttpTransportError::UnexpectedResponseId(id as u64));
174 }
175
176 responses[id] = Some(response_item);
177 }
178
179 let responses = responses.into_iter().flatten().collect::<Vec<_>>();
180 Ok(responses)
181 }
182}