Skip to main content

rings_rpc/
jsonrpc.rs

1//! rings-rpc client
2
3use serde::de::DeserializeOwned;
4use serde::Serialize;
5
6use crate::method::Method;
7use crate::prelude::reqwest::Client as HttpClient;
8use crate::protos::rings_node::*;
9
10/// Wrap json_client send request between nodes or browsers.
11pub struct Client {
12    client: HttpClient,
13    endpoint_url: String,
14}
15
16/// The errors returned by the client.
17#[derive(Debug, thiserror::Error)]
18pub enum RpcError {
19    /// An error returned by the server.
20    #[error("Server returned rpc error {0}")]
21    JsonClientError(jsonrpc_core::Error),
22    /// Failure to parse server response.
23    #[error("Failed to parse server response as {0}: {1}")]
24    ParseError(String, Box<dyn std::error::Error + Send>),
25    /// Request timed out.
26    #[error("Request timed out")]
27    Timeout,
28    /// A general client error.
29    #[error("Client error: {0}")]
30    Client(String),
31    /// Not rpc specific errors.
32    #[error("{0}")]
33    Other(Box<dyn std::error::Error + Send>),
34}
35
36/// A wrap `Result` contains ClientError.
37type Result<T> = std::result::Result<T, RpcError>;
38
39impl Client {
40    /// Creates a new Client instance with the specified endpoint URL
41    pub fn new(endpoint_url: &str) -> Self {
42        Self {
43            client: HttpClient::default(),
44            endpoint_url: endpoint_url.to_string(),
45        }
46    }
47
48    /// Sends a typed JSON-RPC request and decodes the typed response body.
49    pub async fn call_method<T>(&self, method: Method, req: &impl Serialize) -> Result<T>
50    where T: DeserializeOwned {
51        use jsonrpc_core::*;
52
53        let params = serde_json::to_value(req)
54            .map_err(|e| RpcError::Client(e.to_string()))?
55            .as_object()
56            .ok_or(RpcError::Client("params should be an object".to_string()))?
57            .clone();
58
59        let jsonrpc_request = Request::Single(Call::MethodCall(MethodCall {
60            jsonrpc: Some(Version::V2),
61            method: method.to_string(),
62            params: Params::Map(params),
63            id: Id::Num(1),
64        }));
65
66        let result = self.do_jsonrpc_request(&jsonrpc_request).await?;
67        serde_json::from_value(result).map_err(|e| RpcError::ParseError(e.to_string(), Box::new(e)))
68    }
69
70    async fn do_jsonrpc_request(&self, req: &jsonrpc_core::Request) -> Result<serde_json::Value> {
71        let body = serde_json::to_string(req).map_err(|e| RpcError::Client(e.to_string()))?;
72
73        let req = self
74            .client
75            .post(self.endpoint_url.as_str())
76            .header("content-type", "application/json")
77            .header("accept", "application/json")
78            .body(body);
79
80        let resp = req
81            .send()
82            .await
83            .map_err(|e| RpcError::Client(e.to_string()))?
84            .error_for_status()
85            .map_err(|e| RpcError::Client(e.to_string()))?
86            .bytes()
87            .await
88            .map_err(|e| RpcError::ParseError(e.to_string(), Box::new(e)))?;
89
90        let jsonrpc_resp = jsonrpc_core::Response::from_json(&String::from_utf8_lossy(&resp))
91            .map_err(|e| RpcError::ParseError(e.to_string(), Box::new(e)))?;
92
93        match jsonrpc_resp {
94            jsonrpc_core::Response::Single(resp) => match resp {
95                jsonrpc_core::Output::Success(success) => Ok(success.result),
96                jsonrpc_core::Output::Failure(failure) => {
97                    Err(RpcError::JsonClientError(failure.error))
98                }
99            },
100            jsonrpc_core::Response::Batch(_) => Err(RpcError::Client(
101                "Batch response is not supported".to_string(),
102            )),
103        }
104    }
105
106    /// Establishes a WebRTC connection with a remote peer using HTTP as the signaling channel.
107    ///
108    /// This function allows two peers to establish a WebRTC connection using HTTP,
109    /// which can be useful in scenarios where a direct peer-to-peer connection is not possible due to firewall restrictions or other network issues.
110    /// The function sends ICE candidates and Session Description Protocol (SDP) messages over HTTP as a form of signaling to establish the connection.
111    ///
112    /// Takes a URL for an HTTP server that will be used as the signaling channel to exchange ICE candidates and SDP with the remote peer.
113    /// Returns a Did that can be used to refer to this connection in subsequent WebRTC operations.
114    pub async fn connect_peer_via_http(
115        &self,
116        req: &ConnectPeerViaHttpRequest,
117    ) -> Result<ConnectPeerViaHttpResponse> {
118        self.call_method(Method::ConnectPeerViaHttp, req).await
119    }
120
121    /// Attempts to connect to a peer using a DID stored in a Distributed Hash Table (DHT).
122    pub async fn connect_with_did(
123        &self,
124        req: &ConnectWithDidRequest,
125    ) -> Result<ConnectWithSeedResponse> {
126        self.call_method(Method::ConnectWithDid, req).await
127    }
128
129    /// Attempts to connect to a peer using a seed file located at the specified source path.
130    pub async fn connect_with_seed(
131        &self,
132        req: &ConnectWithSeedRequest,
133    ) -> Result<ConnectWithSeedResponse> {
134        self.call_method(Method::ConnectWithSeed, req).await
135    }
136
137    /// Lists all connected peers and their status.
138    ///
139    /// Returns an Output containing a formatted string representation of the list of peers if successful, or an anyhow::Error if an error occurred.
140    pub async fn list_peers(&self, req: &ListPeersRequest) -> Result<ListPeersResponse> {
141        self.call_method(Method::ListPeers, req).await
142    }
143
144    /// Creates a WebRTC offer for a manual peer handshake.
145    pub async fn create_offer(&self, req: &CreateOfferRequest) -> Result<CreateOfferResponse> {
146        self.call_method(Method::CreateOffer, req).await
147    }
148
149    /// Answers a WebRTC offer with a local session description.
150    pub async fn answer_offer(&self, req: &AnswerOfferRequest) -> Result<AnswerOfferResponse> {
151        self.call_method(Method::AnswerOffer, req).await
152    }
153
154    /// Accepts a WebRTC answer and completes the manual handshake.
155    pub async fn accept_answer(&self, req: &AcceptAnswerRequest) -> Result<AcceptAnswerResponse> {
156        self.call_method(Method::AcceptAnswer, req).await
157    }
158
159    /// Disconnects from the peer with the specified DID.
160    pub async fn disconnect(&self, req: &DisconnectRequest) -> Result<DisconnectResponse> {
161        self.call_method(Method::Disconnect, req).await
162    }
163
164    /// Sends a namespace-scoped backend message to a destination DID.
165    pub async fn send_backend_message(
166        &self,
167        req: &SendBackendMessageRequest,
168    ) -> Result<SendBackendMessageResponse> {
169        self.call_method(Method::SendBackendMessage, req).await
170    }
171
172    /// Starts an end-to-end encrypted handshake with a destination DID.
173    pub async fn send_e2e_handshake(
174        &self,
175        req: &SendE2eHandshakeRequest,
176    ) -> Result<SendE2eHandshakeResponse> {
177        self.call_method(Method::SendE2eHandshake, req).await
178    }
179
180    /// Sends an encrypted end-to-end message stream to a destination DID.
181    pub async fn send_e2e_message(
182        &self,
183        req: &SendE2eMessageRequest,
184    ) -> Result<SendE2eMessageResponse> {
185        self.call_method(Method::SendE2eMessage, req).await
186    }
187
188    /// Publishes a message to the specified topic.
189    pub async fn publish_message_to_topic(
190        &self,
191        req: &PublishMessageToTopicRequest,
192    ) -> Result<PublishMessageToTopicResponse> {
193        self.call_method(Method::PublishMessageToTopic, req).await
194    }
195
196    /// Fetches stored messages for a topic after the requested offset.
197    pub async fn fetch_topic_messages(
198        &self,
199        req: &FetchTopicMessagesRequest,
200    ) -> Result<FetchTopicMessagesResponse> {
201        self.call_method(Method::FetchTopicMessages, req).await
202    }
203
204    /// Registers a new service with the given name.
205    pub async fn register_service(
206        &self,
207        req: &RegisterServiceRequest,
208    ) -> Result<RegisterServiceResponse> {
209        self.call_method(Method::RegisterService, req).await
210    }
211
212    /// Looks up the DIDs of services registered with the given name.
213    pub async fn lookup_service(
214        &self,
215        req: &LookupServiceRequest,
216    ) -> Result<LookupServiceResponse> {
217        self.call_method(Method::LookupService, req).await
218    }
219
220    /// Looks up signed online-node descriptors.
221    pub async fn lookup_online_nodes(
222        &self,
223        req: &LookupOnlineNodesRequest,
224    ) -> Result<LookupOnlineNodesResponse> {
225        self.call_method(Method::LookupOnlineNodes, req).await
226    }
227
228    /// Looks up signed onion-exit descriptors.
229    pub async fn lookup_onion_exits(
230        &self,
231        req: &LookupOnionExitsRequest,
232    ) -> Result<LookupOnionExitsResponse> {
233        self.call_method(Method::LookupOnionExits, req).await
234    }
235
236    /// Builds an onion route from live presence and exit descriptors.
237    pub async fn build_onion_route(
238        &self,
239        req: &BuildOnionRouteRequest,
240    ) -> Result<BuildOnionRouteResponse> {
241        self.call_method(Method::BuildOnionRoute, req).await
242    }
243
244    /// Query for swarm inspect info.
245    pub async fn node_info(&self, req: &NodeInfoRequest) -> Result<NodeInfoResponse> {
246        self.call_method(Method::NodeInfo, req).await
247    }
248
249    /// Query local measurement counters for a peer.
250    pub async fn peer_measurement(
251        &self,
252        req: &PeerMeasurementRequest,
253    ) -> Result<PeerMeasurementResponse> {
254        self.call_method(Method::PeerMeasurement, req).await
255    }
256
257    /// Query local measurement counters for all connected peers.
258    pub async fn list_peer_measurements(
259        &self,
260        req: &ListPeerMeasurementsRequest,
261    ) -> Result<ListPeerMeasurementsResponse> {
262        self.call_method(Method::ListPeerMeasurements, req).await
263    }
264
265    /// Returns the DID of the local node.
266    pub async fn node_did(&self, req: &NodeDidRequest) -> Result<NodeDidResponse> {
267        self.call_method(Method::NodeDid, req).await
268    }
269}