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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
#![warn(missing_docs)]
//! JSON-RPC handler for both feature=browser and feature=node.
//! We support running the JSON-RPC server in either native or browser environment.
//! For the native environment, we use jsonrpc_core to handle requests.
//! For the browser environment, we utilize a Simple MessageHandler to process the requests.

use core::future::Future;
use std::pin::Pin;

#[cfg(feature = "browser")]
pub use self::browser::build_handler;
#[cfg(feature = "browser")]
pub use self::browser::HandlerType;
#[cfg(feature = "node")]
pub use self::default::build_handler;
#[cfg(feature = "node")]
pub use self::default::HandlerType;
use super::server;
use super::server::RpcMeta;
use crate::prelude::jsonrpc_core::Params;
use crate::prelude::jsonrpc_core::Result;
use crate::prelude::jsonrpc_core::Value;
use crate::prelude::rings_rpc::method::Method;

/// Type of handler function
#[cfg(feature = "node")]
pub type MethodFnBox = Box<
    dyn Fn(Params, RpcMeta) -> Pin<Box<dyn Future<Output = Result<Value>> + Send>> + Send + Sync,
>;

/// Type of handler function, note that, in browser environment there is no Send and Sync
#[cfg(feature = "browser")]
pub type MethodFnBox = Box<dyn Fn(Params, RpcMeta) -> Pin<Box<dyn Future<Output = Result<Value>>>>>;

/// This macro is used to transform an asynchronous function item into
/// a boxed function reference that returns a pinned future.
macro_rules! pin {
    ($fn:path) => {
        Box::new(|params, meta| Box::pin($fn(params, meta)))
    };
}

/// This function will return a list of public functions for all interfaces.
/// If you need to define interfaces separately for the browser or native,
/// you should use cfg to control the conditions.
pub fn methods() -> Vec<(Method, MethodFnBox)> {
    vec![
        (
            Method::ConnectPeerViaHttp,
            pin!(server::connect_peer_via_http),
        ),
        (
            Method::ConnectPeerViaHttp,
            pin!(server::connect_peer_via_http),
        ),
        (Method::ConnectWithSeed, pin!(server::connect_with_seed)),
        (Method::AnswerOffer, pin!(server::answer_offer)),
        (Method::ConnectWithDid, pin!(server::connect_with_did)),
        (Method::CreateOffer, pin!(server::create_offer)),
        (Method::AcceptAnswer, pin!(server::accept_answer)),
        (Method::ListPeers, pin!(server::list_peers)),
        (Method::Disconnect, pin!(server::close_connection)),
        (Method::SendTo, pin!(server::send_raw_message)),
        (
            Method::SendHttpRequestMessage,
            pin!(server::send_http_request_message),
        ),
        (
            Method::SendSimpleText,
            pin!(server::send_simple_text_message),
        ),
        (Method::SendCustomMessage, pin!(server::send_custom_message)),
        (
            Method::PublishMessageToTopic,
            pin!(server::publish_message_to_topic),
        ),
        (
            Method::FetchMessagesOfTopic,
            pin!(server::fetch_messages_of_topic),
        ),
        (Method::RegisterService, pin!(server::register_service)),
        (Method::LookupService, pin!(server::lookup_service)),
        (Method::NodeInfo, pin!(server::node_info)),
        (Method::NodeDid, pin!(server::node_did)),
        #[cfg(feature = "node")]
        (Method::PollMessage, pin!(default::poll_backend_message)),
    ]
}

/// Implementation for browser
#[cfg(feature = "browser")]
pub mod browser {
    //! This module provides two main facilities:
    //! [MessageHandler]: It is used to serve JSON-RPC in the browser and
    //! handle incoming request requests from the browser.
    //! [build_handler]: It will be used to register and initialize interfaces.
    use std::collections::HashMap;
    use std::sync::Arc;

    use async_trait::async_trait;

    use super::*;
    use crate::prelude::jsonrpc_core::types::error::Error;
    use crate::prelude::jsonrpc_core::types::error::ErrorCode;
    use crate::prelude::jsonrpc_core::types::request::MethodCall;
    use crate::prelude::jsonrpc_core::types::response::Output;
    use crate::processor::Processor;

    /// Type of Messagehandler
    pub type HandlerType = MessageHandler<server::RpcMeta>;

    /// The signature type of MessageHandler is
    /// consistent with the signature type MetaIoHandler in jsonrpc_core,
    /// but it is more simpler and does not have Send and Sync bounds.
    /// Here, the type T is likely to be RpcMeta indefinitely.
    #[derive(Clone)]
    pub struct MessageHandler<T: Clone> {
        /// MetaData for jsonrpc
        meta: T,
        /// Registered Methods
        methods: HashMap<String, Arc<MethodFnBox>>,
    }

    /// Map from Arc<Processor> to MessageHandler<server::RpcMeta>
    /// The value of is_auth of RpcmMet is set to true
    impl From<Arc<Processor>> for MessageHandler<server::RpcMeta> {
        fn from(p: Arc<Processor>) -> Self {
            let meta: server::RpcMeta = p.into();
            Self::new(meta)
        }
    }

    /// This trait defines the register function for method registration.
    pub trait MethodRegister {
        /// Registers a method with the given name and function.
        fn register(&mut self, name: &str, func: MethodFnBox);
    }

    /// A trait that defines the method handler for handling method requests.
    #[cfg_attr(feature = "browser", async_trait(?Send))]
    pub trait MethodHandler {
        /// Handles the incoming method request and returns the result.
        /// Please note that the function type here is MethodCall instead of
        /// Request in jsonrpc_core, which means that batch requests are not supported here.
        async fn handle_request(&self, request: MethodCall) -> Result<Output>;
    }

    impl MessageHandler<server::RpcMeta> {
        /// Create a new instance of message handler
        pub fn new(meta: server::RpcMeta) -> Self {
            Self {
                meta,
                methods: HashMap::new(),
            }
        }
    }

    impl<T: Clone> MethodRegister for MessageHandler<T> {
        fn register(&mut self, name: &str, func: MethodFnBox) {
            self.methods.insert(name.to_string(), Arc::new(func));
        }
    }

    #[cfg_attr(feature = "browser", async_trait(?Send))]
    #[cfg_attr(not(feature = "browser"), async_trait)]
    impl MethodHandler for MessageHandler<server::RpcMeta> {
        async fn handle_request(&self, request: MethodCall) -> Result<Output> {
            let output: Result<Value> = if let Some(handler) = self.methods.get(&request.method) {
                let ret = handler(request.params, self.meta.clone()).await?;
                Ok(ret)
            } else {
                Err(Error {
                    code: ErrorCode::MethodNotFound,
                    message: format!("method {} is not found", &request.method),
                    data: None,
                })
            };
            Ok(Output::from(output, request.id, None))
        }
    }

    /// Build handler add method with metadata.
    pub async fn build_handler(handler: &mut MessageHandler<server::RpcMeta>) {
        for m in methods() {
            handler.register(m.0.as_str(), m.1);
        }
    }
}

/// Implementation for native node
#[cfg(feature = "node")]
pub mod default {
    use super::*;
    use crate::error::Error as ServerError;
    use crate::prelude::jsonrpc_core::Error;
    use crate::prelude::jsonrpc_core::MetaIoHandler as MessageHandler;
    use crate::prelude::rings_rpc::response::CustomBackendMessage;

    /// Type of Messagehandler
    pub type HandlerType = MessageHandler<server::RpcMeta>;

    /// Build handler add method with metadata.
    pub async fn build_handler(handler: &mut MessageHandler<server::RpcMeta>) {
        for m in methods() {
            handler.add_method_with_meta(m.0.as_str(), m.1);
        }
    }

    /// This function is used to handle custom messages for the backend.
    pub async fn poll_backend_message(params: Params, meta: server::RpcMeta) -> Result<Value> {
        let receiver = if let Some(value) = meta.receiver {
            value
        } else {
            return Ok(serde_json::Value::Null);
        };

        let params: Vec<serde_json::Value> = params.parse()?;
        let wait_recv = params
            .get(0)
            .map(|v| v.as_bool().unwrap_or(false))
            .unwrap_or(false);
        let message = if wait_recv {
            let mut recv = receiver.lock().await;
            recv.recv().await.ok()
        } else {
            let mut recv = receiver.lock().await;
            recv.try_recv().ok()
        };

        let message = if let Some(msg) = message {
            serde_json::to_value(CustomBackendMessage::from(msg))
                .map_err(|_| Error::from(ServerError::EncodeError))?
        } else {
            serde_json::Value::Null
        };
        Ok(serde_json::json!({
            "message": message,
        }))
    }
}