forked from casper-ecosystem/casper-client-rs
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcall.rs
More file actions
147 lines (129 loc) · 4.68 KB
/
call.rs
File metadata and controls
147 lines (129 loc) · 4.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#[cfg(not(target_arch = "wasm32"))]
use std::time::Duration;
use crate::{Error, JsonRpcId, SuccessResponse, Verbosity};
use jsonrpc_lite::{JsonRpc, Params};
use once_cell::sync::OnceCell;
use reqwest::{Client, Url};
use serde::{de::DeserializeOwned, Serialize};
use serde_json::json;
const RPC_API_PATH: &str = "rpc";
#[cfg(not(target_arch = "wasm32"))]
const REQUEST_TIMEOUT: Duration = Duration::from_secs(15);
/// Statically declared client used when making HTTP requests
/// so opened connections are pooled.
static CLIENT: OnceCell<Client> = OnceCell::new();
/// Struct representing a single JSON-RPC call to the casper node.
#[derive(Debug)]
pub(crate) struct Call {
rpc_id: JsonRpcId,
node_address: Url,
#[allow(dead_code)]
verbosity: Verbosity,
}
/// `Call` encapsulates JSON-RPC calls made to the casper node service.
impl Call {
pub(crate) fn new(
rpc_id: JsonRpcId,
node_address: &str,
verbosity: Verbosity,
) -> Result<Self, Error> {
let url = if node_address.ends_with(RPC_API_PATH) {
node_address.to_string()
} else {
format!("{}/{}", node_address, RPC_API_PATH)
};
let node_address = Url::parse(&url).map_err(|_| Error::FailedToParseNodeAddress)?;
Ok(Self {
rpc_id,
// node_address: node_address.trim_end_matches('/').to_string(),
node_address,
verbosity,
})
}
pub(crate) async fn send_request<P: Serialize, R: DeserializeOwned>(
self,
method: &'static str,
maybe_params: Option<P>,
) -> Result<SuccessResponse<R>, Error> {
let rpc_request = match maybe_params {
Some(params) => {
let params = Params::Map(
json!(params)
.as_object()
.unwrap_or_else(|| panic!("should be a JSON Map"))
.clone(),
);
JsonRpc::request_with_params(&self.rpc_id, method, params)
}
None => JsonRpc::request(&self.rpc_id, method),
};
#[cfg(feature = "std-fs-io")]
crate::json_pretty_print(&rpc_request, self.verbosity)?;
let client = CLIENT.get_or_init(|| {
let builder = Client::builder();
#[cfg(not(target_arch = "wasm32"))]
let builder = builder.timeout(REQUEST_TIMEOUT);
builder.build().expect("failed to initialize HTTP client")
});
let http_response = client
.post(self.node_address)
.json(&rpc_request)
.send()
.await
.map_err(|error| Error::FailedToGetResponse {
rpc_id: self.rpc_id.clone(),
rpc_method: method,
error,
})?;
if let Err(error) = http_response.error_for_status_ref() {
return Err(Error::ResponseIsHttpError {
rpc_id: self.rpc_id.clone(),
rpc_method: method,
error,
});
}
let rpc_response: JsonRpc =
http_response
.json()
.await
.map_err(|error| Error::FailedToParseResponse {
rpc_id: self.rpc_id.clone(),
rpc_method: method,
error,
})?;
#[cfg(feature = "std-fs-io")]
crate::json_pretty_print(&rpc_response, self.verbosity)?;
let response_kind = match &rpc_response {
JsonRpc::Request(_) => "Request",
JsonRpc::Notification(_) => "Notification",
JsonRpc::Success(_) => "Success",
JsonRpc::Error(_) => "Error",
};
if let Some(json_value) = rpc_response.get_result().cloned() {
let value =
serde_json::from_value(json_value).map_err(|err| Error::InvalidRpcResponse {
rpc_id: self.rpc_id.clone(),
rpc_method: method,
response_kind,
response: json!(rpc_response),
source: Some(err),
})?;
let success_response = SuccessResponse::new(self.rpc_id.clone(), value);
return Ok(success_response);
}
if let Some(error) = rpc_response.get_error().cloned() {
return Err(Error::ResponseIsRpcError {
rpc_id: self.rpc_id.clone(),
rpc_method: method,
error,
});
}
Err(Error::InvalidRpcResponse {
rpc_id: self.rpc_id.clone(),
rpc_method: method,
response_kind,
response: json!(rpc_response),
source: None,
})
}
}