cuprated/rpc/
server.rs

1//! RPC server initialization and main loop.
2
3use std::{
4    net::{IpAddr, SocketAddr},
5    time::Duration,
6};
7
8use anyhow::Error;
9use tokio::net::TcpListener;
10use tower::limit::rate::RateLimitLayer;
11use tower_http::limit::RequestBodyLimitLayer;
12use tracing::{field::display, info, warn};
13
14use cuprate_rpc_interface::{RouterBuilder, RpcHandlerDummy};
15
16use crate::{config::RpcConfig, rpc::CupratedRpcHandler};
17
18/// Initialize the RPC server(s).
19///
20/// # Panics
21/// This function will panic if:
22/// - the server(s) could not be started
23/// - unrestricted RPC is started on non-local
24///   address without override option
25pub fn init_rpc_servers(config: RpcConfig) {
26    for ((enable, addr, request_byte_limit), restricted) in [
27        (
28            (
29                config.unrestricted.enable,
30                config.unrestricted.address,
31                config.unrestricted.request_byte_limit,
32            ),
33            false,
34        ),
35        (
36            (
37                config.restricted.enable,
38                config.restricted.address,
39                config.restricted.request_byte_limit,
40            ),
41            true,
42        ),
43    ] {
44        if !enable {
45            info!(restricted, "Skipping RPC server");
46            continue;
47        }
48
49        if !restricted && !cuprate_helper::net::ip_is_local(addr.ip()) {
50            if config
51                .unrestricted
52                .i_know_what_im_doing_allow_public_unrestricted_rpc
53            {
54                warn!(
55                    address = display(addr),
56                    "Starting unrestricted RPC on non-local address, this is dangerous!"
57                );
58            } else {
59                panic!("Refusing to start unrestricted RPC on a non-local address ({addr})");
60            }
61        }
62
63        tokio::task::spawn(async move {
64            run_rpc_server(restricted, addr, request_byte_limit)
65                .await
66                .unwrap();
67        });
68    }
69}
70
71/// This initializes and runs an RPC server.
72///
73/// The function will only return when the server itself returns or an error occurs.
74async fn run_rpc_server(
75    restricted: bool,
76    address: SocketAddr,
77    request_byte_limit: usize,
78) -> Result<(), Error> {
79    info!(
80        restricted,
81        address = display(&address),
82        "Starting RPC server"
83    );
84
85    // Create the router.
86    //
87    // TODO: impl more layers, rate-limiting, configuration, etc.
88    let state = RpcHandlerDummy { restricted };
89    // TODO:
90    // - add functions that are `all()` but for restricted RPC
91    // - enable aliases automatically `other_get_height` + `other_getheight`?
92    //
93    // FIXME:
94    // - `json_rpc` is 1 endpoint; `RouterBuilder` operates at the
95    //   level endpoint; we can't selectively enable certain `json_rpc` methods
96    let router = RouterBuilder::new().fallback().build().with_state(state);
97
98    // Add restrictive layers if restricted RPC.
99    //
100    // TODO: <https://github.com/Cuprate/cuprate/issues/445>
101    let router = if request_byte_limit != 0 {
102        router.layer(RequestBodyLimitLayer::new(request_byte_limit))
103    } else {
104        router
105    };
106
107    // Start the server.
108    //
109    // TODO: impl custom server code, don't use axum.
110    let listener = TcpListener::bind(address).await?;
111    axum::serve(listener, router).await?;
112
113    Ok(())
114}