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    pub async fn call_method<T>(&self, method: Method, req: &impl Serialize) -> Result<T>
49    where T: DeserializeOwned {
50        use jsonrpc_core::*;
51
52        let params = serde_json::to_value(req)
53            .map_err(|e| RpcError::Client(e.to_string()))?
54            .as_object()
55            .ok_or(RpcError::Client("params should be an object".to_string()))?
56            .clone();
57
58        let jsonrpc_request = Request::Single(Call::MethodCall(MethodCall {
59            jsonrpc: Some(Version::V2),
60            method: method.to_string(),
61            params: Params::Map(params),
62            id: Id::Num(1),
63        }));
64
65        let result = self.do_jsonrpc_request(&jsonrpc_request).await?;
66        serde_json::from_value(result).map_err(|e| RpcError::ParseError(e.to_string(), Box::new(e)))
67    }
68
69    async fn do_jsonrpc_request(&self, req: &jsonrpc_core::Request) -> Result<serde_json::Value> {
70        let body = serde_json::to_string(req).map_err(|e| RpcError::Client(e.to_string()))?;
71
72        let req = self
73            .client
74            .post(self.endpoint_url.as_str())
75            .header("content-type", "application/json")
76            .header("accept", "application/json")
77            .body(body);
78
79        let resp = req
80            .send()
81            .await
82            .map_err(|e| RpcError::Client(e.to_string()))?
83            .error_for_status()
84            .map_err(|e| RpcError::Client(e.to_string()))?
85            .bytes()
86            .await
87            .map_err(|e| RpcError::ParseError(e.to_string(), Box::new(e)))?;
88
89        let jsonrpc_resp = jsonrpc_core::Response::from_json(&String::from_utf8_lossy(&resp))
90            .map_err(|e| RpcError::ParseError(e.to_string(), Box::new(e)))?;
91
92        match jsonrpc_resp {
93            jsonrpc_core::Response::Single(resp) => match resp {
94                jsonrpc_core::Output::Success(success) => Ok(success.result),
95                jsonrpc_core::Output::Failure(failure) => {
96                    Err(RpcError::JsonClientError(failure.error))
97                }
98            },
99            jsonrpc_core::Response::Batch(_) => Err(RpcError::Client(
100                "Batch response is not supported".to_string(),
101            )),
102        }
103    }
104
105    /// Establishes a WebRTC connection with a remote peer using HTTP as the signaling channel.
106    ///
107    /// This function allows two peers to establish a WebRTC connection using HTTP,
108    /// which can be useful in scenarios where a direct peer-to-peer connection is not possible due to firewall restrictions or other network issues.
109    /// The function sends ICE candidates and Session Description Protocol (SDP) messages over HTTP as a form of signaling to establish the connection.
110    ///
111    /// 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.
112    /// Returns a Did that can be used to refer to this connection in subsequent WebRTC operations.
113    pub async fn connect_peer_via_http(
114        &self,
115        req: &ConnectPeerViaHttpRequest,
116    ) -> Result<ConnectPeerViaHttpResponse> {
117        self.call_method(Method::ConnectPeerViaHttp, req).await
118    }
119
120    /// Attempts to connect to a peer using a DID stored in a Distributed Hash Table (DHT).
121    pub async fn connect_with_did(
122        &self,
123        req: &ConnectWithDidRequest,
124    ) -> Result<ConnectWithSeedResponse> {
125        self.call_method(Method::ConnectWithDid, req).await
126    }
127
128    /// Attempts to connect to a peer using a seed file located at the specified source path.
129    pub async fn connect_with_seed(
130        &self,
131        req: &ConnectWithSeedRequest,
132    ) -> Result<ConnectWithSeedResponse> {
133        self.call_method(Method::ConnectWithSeed, req).await
134    }
135
136    /// Lists all connected peers and their status.
137    ///
138    /// Returns an Output containing a formatted string representation of the list of peers if successful, or an anyhow::Error if an error occurred.
139    pub async fn list_peers(&self, req: &ListPeersRequest) -> Result<ListPeersResponse> {
140        self.call_method(Method::ListPeers, req).await
141    }
142
143    pub async fn create_offer(&self, req: &CreateOfferRequest) -> Result<CreateOfferResponse> {
144        self.call_method(Method::CreateOffer, req).await
145    }
146
147    pub async fn answer_offer(&self, req: &AnswerOfferRequest) -> Result<AnswerOfferResponse> {
148        self.call_method(Method::AnswerOffer, req).await
149    }
150
151    pub async fn accept_answer(&self, req: &AcceptAnswerRequest) -> Result<AcceptAnswerResponse> {
152        self.call_method(Method::AcceptAnswer, req).await
153    }
154
155    /// Disconnects from the peer with the specified DID.
156    pub async fn disconnect(&self, req: &DisconnectRequest) -> Result<DisconnectResponse> {
157        self.call_method(Method::Disconnect, req).await
158    }
159
160    pub async fn send_backend_message(
161        &self,
162        req: &SendBackendMessageRequest,
163    ) -> Result<SendBackendMessageResponse> {
164        self.call_method(Method::SendBackendMessage, req).await
165    }
166
167    pub async fn send_e2e_handshake(
168        &self,
169        req: &SendE2eHandshakeRequest,
170    ) -> Result<SendE2eHandshakeResponse> {
171        self.call_method(Method::SendE2eHandshake, req).await
172    }
173
174    pub async fn send_e2e_message(
175        &self,
176        req: &SendE2eMessageRequest,
177    ) -> Result<SendE2eMessageResponse> {
178        self.call_method(Method::SendE2eMessage, req).await
179    }
180
181    /// Publishes a message to the specified topic.
182    pub async fn publish_message_to_topic(
183        &self,
184        req: &PublishMessageToTopicRequest,
185    ) -> Result<PublishMessageToTopicResponse> {
186        self.call_method(Method::PublishMessageToTopic, req).await
187    }
188
189    pub async fn fetch_topic_messages(
190        &self,
191        req: &FetchTopicMessagesRequest,
192    ) -> Result<FetchTopicMessagesResponse> {
193        self.call_method(Method::FetchTopicMessages, req).await
194    }
195
196    /// Registers a new service with the given name.
197    pub async fn register_service(
198        &self,
199        req: &RegisterServiceRequest,
200    ) -> Result<RegisterServiceResponse> {
201        self.call_method(Method::RegisterService, req).await
202    }
203
204    /// Looks up the DIDs of services registered with the given name.
205    pub async fn lookup_service(
206        &self,
207        req: &LookupServiceRequest,
208    ) -> Result<LookupServiceResponse> {
209        self.call_method(Method::LookupService, req).await
210    }
211
212    /// Query for swarm inspect info.
213    pub async fn node_info(&self, req: &NodeInfoRequest) -> Result<NodeInfoResponse> {
214        self.call_method(Method::NodeInfo, req).await
215    }
216
217    pub async fn node_did(&self, req: &NodeDidRequest) -> Result<NodeDidResponse> {
218        self.call_method(Method::NodeDid, req).await
219    }
220}