rings_node/native/endpoint/
mod.rs1mod http_error;
3mod ws;
4
5use std::net::SocketAddr;
6use std::sync::Arc;
7
8use axum::extract::ConnectInfo;
9use axum::extract::Request;
10use axum::extract::State;
11use axum::extract::WebSocketUpgrade;
12use axum::http::HeaderValue;
13use axum::response::IntoResponse;
14use axum::routing::get;
15use axum::routing::post;
16use axum::Router;
17use jsonrpc_core::MetaIoHandler;
18use rings_gateway::GatewayStatus;
19use rings_gateway::GatewayStatusHandle;
20use rings_rpc::protos::rings_node::NodeInfoResponse;
21use tokio::net::TcpListener;
22use tower_http::cors::CorsLayer;
23
24use self::http_error::HttpError;
25use crate::processor::Processor;
26
27#[derive(Clone)]
29pub struct JsonRpcState<M>
30where M: jsonrpc_core::Middleware<Arc<Processor>>
31{
32 processor: Arc<Processor>,
33 io_handler: MetaIoHandler<Arc<Processor>, M>,
34}
35
36#[derive(Clone)]
38#[allow(dead_code)]
39pub struct WsState {
40 processor: Arc<Processor>,
41}
42
43#[derive(Clone)]
45pub struct StatusState {
46 processor: Arc<Processor>,
47}
48
49#[derive(Clone)]
51pub struct GatewayStatusState {
52 status: GatewayStatusHandle,
53}
54
55struct ExternalRpcMiddleware;
56struct InternalRpcMiddleware;
57
58pub async fn run_internal_api(port: u16, processor: Arc<Processor>) -> anyhow::Result<()> {
60 run_internal_api_with_gateway(port, processor, None).await
61}
62
63pub async fn run_internal_api_with_gateway(
65 port: u16,
66 processor: Arc<Processor>,
67 gateway: Option<GatewayStatusHandle>,
68) -> anyhow::Result<()> {
69 let gateway_configured = gateway.is_some();
70 let binding_addr = SocketAddr::from(([127, 0, 0, 1], port));
71
72 let jsonrpc_handler = MetaIoHandler::with_middleware(InternalRpcMiddleware);
73 let jsonrpc_state = Arc::new(JsonRpcState {
74 processor: processor.clone(),
75 io_handler: jsonrpc_handler,
76 });
77
78 let ws_state = Arc::new(WsState {
79 processor: processor.clone(),
80 });
81
82 let status_state = Arc::new(StatusState { processor });
83
84 let mut router = Router::new()
85 .route(
86 "/",
87 post(jsonrpc_io_handler).with_state(jsonrpc_state.clone()),
88 )
89 .route("/ws", get(ws_handler).with_state(ws_state))
90 .route("/status", get(status_handler).with_state(status_state));
91 if let Some(status) = gateway {
92 router = router.route(
93 "/gateway/status",
94 get(gateway_status_handler).with_state(Arc::new(GatewayStatusState { status })),
95 );
96 }
97 let axum_make_service = router
98 .layer(CorsLayer::permissive())
99 .layer(axum::middleware::from_fn(node_info_header))
100 .into_make_service_with_connect_info::<SocketAddr>();
101
102 println!("JSON-RPC endpoint: http://{binding_addr}");
103 println!("WebSocket endpoint: http://{binding_addr}/ws");
104 if gateway_configured {
105 println!("Gateway status endpoint: http://{binding_addr}/gateway/status");
106 }
107 let listener = TcpListener::bind(binding_addr).await?;
108 axum::serve(listener, axum_make_service).await?;
109 Ok(())
110}
111
112pub async fn run_external_api(addr: String, processor: Arc<Processor>) -> anyhow::Result<()> {
114 let binding_addr: SocketAddr = addr.parse()?;
115
116 let jsonrpc_handler = MetaIoHandler::with_middleware(ExternalRpcMiddleware);
117 let jsonrpc_state = Arc::new(JsonRpcState {
118 processor: processor.clone(),
119 io_handler: jsonrpc_handler,
120 });
121
122 let status_state = Arc::new(StatusState { processor });
123
124 let router = Router::new()
125 .route(
126 "/",
127 post(jsonrpc_io_handler).with_state(jsonrpc_state.clone()),
128 )
129 .route("/status", get(status_handler).with_state(status_state));
130 let axum_make_service = router
131 .layer(CorsLayer::permissive())
132 .layer(axum::middleware::from_fn(node_info_header))
133 .into_make_service_with_connect_info::<SocketAddr>();
134
135 println!("JSON-RPC endpoint: http://{addr}");
136 let listener = TcpListener::bind(binding_addr).await?;
137 axum::serve(listener, axum_make_service).await?;
138 Ok(())
139}
140
141async fn jsonrpc_io_handler<M>(
142 State(state): State<Arc<JsonRpcState<M>>>,
143 body: String,
144) -> Result<JsonResponse, HttpError>
145where
146 M: jsonrpc_core::Middleware<Arc<Processor>>,
147{
148 let r = state
149 .io_handler
150 .handle_request(&body, state.processor.clone())
151 .await
152 .ok_or(HttpError::BadRequest)?;
153 Ok(JsonResponse(r))
154}
155
156async fn node_info_header(req: Request, next: axum::middleware::Next) -> axum::response::Response {
157 let mut res = next.run(req).await;
158 let headers = res.headers_mut();
159
160 if let Ok(version) = HeaderValue::from_str(crate::util::build_version().as_str()) {
161 headers.insert("X-NODE-VERSION", version);
162 }
163 res
164}
165
166async fn status_handler(
167 State(state): State<Arc<StatusState>>,
168) -> Result<axum::Json<NodeInfoResponse>, HttpError> {
169 let info = state
170 .processor
171 .get_node_info()
172 .await
173 .map_err(|_| HttpError::Internal)?;
174 Ok(axum::Json(info))
175}
176
177async fn gateway_status_handler(
178 State(state): State<Arc<GatewayStatusState>>,
179) -> axum::Json<GatewayStatus> {
180 axum::Json(state.status.snapshot())
181}
182
183#[derive(Debug, Clone)]
185pub struct JsonResponse(String);
186
187impl IntoResponse for JsonResponse {
188 fn into_response(self) -> axum::response::Response {
189 ([("content-type", "application/json")], self.0).into_response()
190 }
191}
192
193async fn ws_handler(
194 State(state): State<Arc<WsState>>,
195 ws: WebSocketUpgrade,
196 ConnectInfo(addr): ConnectInfo<SocketAddr>,
197) -> impl IntoResponse {
198 tracing::debug!("ws connected, remote: {}", addr);
199 ws.on_upgrade(move |socket| self::ws::handle_socket(state, socket))
200}
201
202mod jsonrpc_middleware_impl {
203 use std::future::Future;
204
205 use jsonrpc_core::futures_util::future;
206 use jsonrpc_core::futures_util::future::Either;
207 use jsonrpc_core::futures_util::FutureExt;
208 use jsonrpc_core::middleware::NoopCallFuture;
209 use jsonrpc_core::middleware::NoopFuture;
210 use jsonrpc_core::*;
211 use rings_rpc::protos::rings_node_handler::ExternalRpcHandler;
212 use rings_rpc::protos::rings_node_handler::InternalRpcHandler;
213
214 use super::*;
215
216 impl Middleware<Arc<Processor>> for InternalRpcMiddleware {
217 type Future = NoopFuture;
218 type CallFuture = NoopCallFuture;
219
220 fn on_call<F, X>(
221 &self,
222 call: Call,
223 meta: Arc<Processor>,
224 next: F,
225 ) -> Either<Self::CallFuture, X>
226 where
227 F: Fn(Call, Arc<Processor>) -> X + Send + Sync,
228 X: Future<Output = Option<Output>> + Send + 'static,
229 {
230 match call {
231 Call::MethodCall(req) => {
232 let fut = InternalRpcHandler
233 .handle_request(meta, req.method, req.params.into())
234 .then(move |res| {
235 future::ready(Some(Output::from(res, req.id, req.jsonrpc)))
236 });
237 Either::Left(Box::pin(fut))
238 }
239 _ => Either::Left(Box::pin(next(call, meta))),
240 }
241 }
242 }
243
244 impl Middleware<Arc<Processor>> for ExternalRpcMiddleware {
245 type Future = NoopFuture;
246 type CallFuture = NoopCallFuture;
247
248 fn on_call<F, X>(
249 &self,
250 call: Call,
251 meta: Arc<Processor>,
252 next: F,
253 ) -> Either<Self::CallFuture, X>
254 where
255 F: Fn(Call, Arc<Processor>) -> X + Send + Sync,
256 X: Future<Output = Option<Output>> + Send + 'static,
257 {
258 match call {
259 Call::MethodCall(req) => {
260 let fut = ExternalRpcHandler
261 .handle_request(meta, req.method, req.params.into())
262 .then(move |res| {
263 future::ready(Some(Output::from(res, req.id, req.jsonrpc)))
264 });
265 Either::Left(Box::pin(fut))
266 }
267 _ => Either::Left(Box::pin(next(call, meta))),
268 }
269 }
270 }
271}