tenderdash_abci/
server.rs

1//! Tenderdash ABCI Server.
2mod codec;
3mod generic;
4
5#[cfg(feature = "tcp")]
6use std::{
7    net::{IpAddr, SocketAddr, SocketAddrV4, SocketAddrV6},
8    str::FromStr,
9};
10
11use futures::Future;
12#[cfg(feature = "tcp")]
13use tokio::net::TcpListener;
14#[cfg(feature = "unix")]
15use tokio::net::UnixListener;
16use tokio::{
17    runtime::{Handle, Runtime},
18    task::JoinHandle,
19};
20pub use tokio_util::sync::CancellationToken;
21
22use self::generic::GenericServer;
23use crate::{Error, application::RequestDispatcher};
24
25#[cfg(not(any(feature = "tcp", feature = "unix")))]
26compile_error!("At least one of `tcp` or `unix` features must be enabled");
27
28/// ABCI Server handle.
29///
30/// Use [`Server::handle_connection()`] to accept connection and process all
31/// traffic in this connection. Each incoming connection will be processed using
32/// `app`.
33pub trait Server {
34    /// Process one incoming connection.
35    ///
36    /// Returns when the connection is terminated, [CancellationToken::cancel()]
37    /// is called or RequestDispatcher returns `None`.
38    ///
39    /// It is safe to call this method multiple times after it finishes;
40    /// however, errors must be examined and handled, as the connection
41    /// should not terminate. One exception is [Error::Cancelled], which
42    /// means server shutdown was requested.
43    fn next_client(&self) -> Result<(), Error>;
44
45    #[deprecated = "use `next_client()`"]
46    fn handle_connection(&self) -> Result<(), Error> {
47        self.next_client()
48    }
49}
50
51/// ABCI server builder that creates and starts ABCI server
52///
53/// Create new server with [`ServerBuilder::new()`], configure it as needed, and
54/// finalize using [`ServerBuilder::build()`]. This will create and start new
55/// ABCI server.
56///
57/// Use [`Server::next_client()`] to accept connection from ABCI client
58/// (Tenderdash) and start processing incoming requests. Each incoming
59/// connection will be processed using `app`.
60///
61/// # Examples
62///
63/// ```no_run
64/// struct MyAbciApplication {};
65/// impl tenderdash_abci::Application for MyAbciApplication {};
66/// let app = MyAbciApplication {};
67/// let bind_address = "unix:///tmp/abci.sock";
68/// let server = tenderdash_abci::ServerBuilder::new(app, &bind_address).build().expect("server failed");
69/// loop {
70///     if let Err(tenderdash_abci::Error::Cancelled()) = server.next_client() {
71///         break;
72///     }
73/// }
74/// ```
75pub struct ServerBuilder<D>
76where
77    D: RequestDispatcher,
78{
79    app: D,
80    bind_address: String,
81    cancel: Option<CancellationToken>,
82    server_runtime: Option<ServerRuntime>,
83}
84
85impl<'a, App: RequestDispatcher + 'a> ServerBuilder<App> {
86    /// Create new server builder.
87    ///
88    /// # Arguments
89    ///
90    /// * `address` - address in URI format, pointing either to TCP address and
91    ///   port (eg. `tcp://0.0.0.0:1234`, `tcp://[::1]:1234`) or Unix socket
92    ///   (`unix:///var/run/abci.sock`)
93    /// * `app` - request dispatcher, most likely implementation of Application
94    ///   trait
95    pub fn new(app: App, address: &str) -> Self {
96        Self {
97            app,
98            bind_address: address.to_string(),
99            cancel: None,
100            server_runtime: None,
101        }
102    }
103
104    /// Build and start the ABCI server.
105    ///
106    /// # Return
107    ///
108    /// Returns [`Server`] which provides [`Server::next_client()`]
109    /// method. Call it in a loop to accept and process incoming
110    /// connections.
111    pub fn build(self) -> Result<Box<dyn Server + 'a>, crate::Error> {
112        let bind_address =
113            url::Url::parse(self.bind_address.as_ref()).expect("invalid bind address");
114        if bind_address.scheme() != "tcp" && bind_address.scheme() != "unix" {
115            panic!("app_address must be either tcp:// or unix://");
116        }
117        let server_runtime: ServerRuntime = self.server_runtime.unwrap_or_default();
118
119        let _guard = server_runtime.handle.enter();
120
121        // No cancel is defined, so we add some "mock"
122        let cancel = self.cancel.unwrap_or_default();
123
124        let server = match bind_address.scheme() {
125            #[cfg(feature = "tcp")]
126            "tcp" => Box::new(GenericServer::<App, TcpListener>::bind(
127                self.app,
128                parse_tcp_uri(bind_address),
129                cancel,
130                server_runtime,
131            )?) as Box<dyn Server + 'a>,
132            #[cfg(feature = "unix")]
133            "unix" => Box::new(GenericServer::<App, UnixListener>::bind(
134                self.app,
135                bind_address.path(),
136                cancel,
137                server_runtime,
138            )?) as Box<dyn Server + 'a>,
139            _ => panic!(
140                "listen address uses unsupported scheme `{}`",
141                bind_address.scheme()
142            ),
143        };
144
145        Ok(server)
146    }
147    /// Set a [CancellationToken] token to support graceful shutdown.
148    ///
149    /// Call [`CancellationToken::cancel()`] to stop the server gracefully.
150    ///
151    /// [`CancellationToken::cancel()`]: tokio_util::sync::CancellationToken::cancel()
152    pub fn with_cancel_token(self, cancel: CancellationToken) -> Self {
153        Self {
154            cancel: Some(cancel),
155            ..self
156        }
157    }
158    /// Set tokio [Runtime](tokio::runtime::Runtime) to use.
159    ///
160    /// By default, current tokio runtime is used. If no runtime is active
161    /// ([Handle::try_current()] returns error), new multi-threaded runtime
162    /// is started. If this is not what you want, use
163    /// [ServerBuilder::with_runtime()] to provide handler to correct Tokio
164    /// runtime.
165    ///
166    /// # Example
167    ///
168    /// ```
169    /// use tokio::runtime::{Handle, Runtime};
170    /// use tenderdash_abci::{RequestDispatcher, ServerBuilder, CancellationToken, Application};
171    ///
172    /// // Your custom RequestDispatcher implementation
173    /// struct MyApp;
174    ///
175    /// impl Application for MyApp {}
176    ///
177    /// // Create a Tokio runtime
178    /// let runtime = Runtime::new().unwrap();
179    /// let runtime_handle = runtime.handle().clone();
180    ///
181    /// // Create an instance of your RequestDispatcher implementation
182    /// let app = MyApp;
183    ///
184    /// // Create cancellation token
185    /// let cancel = CancellationToken::new();
186    /// # cancel.cancel();
187    /// // Create a ServerBuilder instance and set the runtime using with_runtime()
188    ///     
189    /// let server = ServerBuilder::new(app, "tcp://0.0.0.0:17534")
190    ///     .with_runtime(runtime_handle)
191    ///     .with_cancel_token(cancel)
192    ///     .build();
193    /// ```
194    ///
195    /// In this example, we first create a Tokio runtime and get its handle.
196    /// Then we create an instance of our `MyApp` struct that implements the
197    /// `RequestDispatcher` trait. We create a `ServerBuilder` instance by
198    /// calling `new()` with our `MyApp` instance and then use the
199    /// `with_runtime()` method to set the runtime handle. Finally, you can
200    /// continue building your server and eventually run it.
201    ///
202    /// [Handle::try_current()]: tokio::runtime::Handle::try_current()
203    pub fn with_runtime(self, runtime_handle: Handle) -> Self {
204        Self {
205            server_runtime: Some(ServerRuntime {
206                _runtime: None,
207                handle: runtime_handle,
208            }),
209            ..self
210        }
211    }
212}
213
214/// Server runtime that must be alive for the whole lifespan of the server
215pub struct ServerRuntime {
216    /// Runtime stored here to ensure it is never dropped
217    _runtime: Option<Runtime>,
218    pub handle: Handle,
219}
220
221impl ServerRuntime {
222    pub fn block_on<F: std::future::Future>(&self, future: F) -> F::Output {
223        self.handle.block_on(future)
224    }
225
226    pub fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
227    where
228        F: Future + Send + 'static,
229        F::Output: Send + 'static,
230    {
231        self.handle.spawn(future)
232    }
233}
234
235impl Default for ServerRuntime {
236    /// Return default server runtime.
237    ///
238    /// If tokio runtime is already initialized and entered, returns handle to
239    /// it. Otherwise, creates new runtime and returns handle AND the
240    /// runtime itself.
241    fn default() -> Self {
242        match Handle::try_current() {
243            Ok(runtime_handle) => Self {
244                handle: runtime_handle,
245                _runtime: None,
246            },
247            Err(_) => {
248                let rt = tokio::runtime::Builder::new_multi_thread()
249                    .worker_threads(4)
250                    .enable_all()
251                    .build()
252                    .expect("cannot create runtime");
253                tracing::trace!("created new runtime");
254                Self {
255                    handle: rt.handle().clone(),
256                    _runtime: Some(rt),
257                }
258            },
259        }
260    }
261}
262
263#[deprecated = "use `ServerBuilder::new(app, &bind_address).build()` instead"]
264pub fn start_server<'a, App: RequestDispatcher + 'a, Addr>(
265    bind_address: Addr,
266    app: App,
267) -> Result<Box<dyn Server + 'a>, crate::Error>
268where
269    Addr: AsRef<str>,
270{
271    ServerBuilder::new(app, bind_address.as_ref()).build()
272}
273#[cfg(feature = "tcp")]
274fn parse_tcp_uri(uri: url::Url) -> SocketAddr {
275    let host = uri.host_str().unwrap();
276    // remove '[' and ']' from ipv6 address, as per https://github.com/servo/rust-url/issues/770
277    let host = host.replace(['[', ']'], "");
278    let port = uri.port().expect("missing tcp port");
279
280    let ip = IpAddr::from_str(host.as_str())
281        .unwrap_or_else(|e| panic!("invalid listen address {}: {}", host, e));
282    match ip {
283        IpAddr::V4(a) => SocketAddr::V4(SocketAddrV4::new(a, port)),
284        IpAddr::V6(a) => SocketAddr::V6(SocketAddrV6::new(a, port, 0, 0)),
285    }
286}
287
288#[cfg(test)]
289mod tests {
290    use crate::server::parse_tcp_uri;
291
292    #[test]
293    fn test_parse_tcp_uri() {
294        struct TestCase<'a> {
295            uri: &'a str,
296            expect: &'a str,
297        }
298
299        let test_cases = [
300            TestCase {
301                uri: "tcp://0.0.0.0:1234",
302                expect: "0.0.0.0:1234",
303            },
304            TestCase {
305                uri: "tcp://[::]:1234",
306                expect: "[::]:1234",
307            },
308            TestCase {
309                uri: "tcp://[::1]:1234",
310                expect: "[::1]:1234",
311            },
312            TestCase {
313                uri: "tcp://[::ffff:ac11:1]:5678",
314                expect: "[::ffff:172.17.0.1]:5678",
315            },
316        ];
317
318        for test_case in test_cases {
319            let uri = url::Url::parse(test_case.uri).unwrap();
320
321            let addr = parse_tcp_uri(uri);
322            assert_eq!(test_case.expect, addr.to_string());
323        }
324    }
325}