use crate::{connection::*, router::*, server::*};
use async_trait::async_trait;
use kaspa_core::{
info,
task::service::{AsyncService, AsyncServiceError, AsyncServiceFuture},
trace, warn,
};
use kaspa_rpc_core::api::ops::RpcApiOps;
use kaspa_rpc_service::service::RpcCoreService;
use kaspa_utils::triggers::SingleTrigger;
use std::sync::Arc;
use tokio::sync::oneshot::{channel as oneshot_channel, Sender as OneshotSender};
use workflow_rpc::server::prelude::*;
pub use workflow_rpc::server::{Encoding as WrpcEncoding, WebSocketConfig, WebSocketCounters};
static MAX_WRPC_MESSAGE_SIZE: usize = 1024 * 1024 * 128;
pub struct Options {
pub listen_address: String,
pub grpc_proxy_address: Option<String>,
pub verbose: bool,
}
impl Default for Options {
fn default() -> Self {
Options { listen_address: "127.0.0.1:17110".to_owned(), verbose: false, grpc_proxy_address: None }
}
}
pub struct KaspaRpcHandler {
pub server: Server,
pub options: Arc<Options>,
}
impl KaspaRpcHandler {
pub fn new(
tasks: usize,
encoding: WrpcEncoding,
core_service: Option<Arc<RpcCoreService>>,
options: Arc<Options>,
) -> KaspaRpcHandler {
KaspaRpcHandler { server: Server::new(tasks, encoding, core_service, options.clone()), options }
}
}
#[async_trait]
impl RpcHandler for KaspaRpcHandler {
type Context = Connection;
async fn handshake(
self: Arc<Self>,
peer: &SocketAddr,
_sender: &mut WebSocketSender,
_receiver: &mut WebSocketReceiver,
messenger: Arc<Messenger>,
) -> WebSocketResult<Connection> {
let connection = self.server.connect(peer, messenger).await.map_err(|err| err.to_string())?;
Ok(connection)
}
async fn disconnect(self: Arc<Self>, ctx: Self::Context, _result: WebSocketResult<()>) {
self.server.disconnect(ctx).await;
}
}
pub struct WrpcService {
options: Arc<Options>,
server: RpcServer,
rpc_handler: Arc<KaspaRpcHandler>,
shutdown: SingleTrigger,
}
impl WrpcService {
pub fn new(
tasks: usize,
core_service: Option<Arc<RpcCoreService>>,
encoding: &Encoding,
counters: Arc<WebSocketCounters>,
options: Options,
) -> Self {
let options = Arc::new(options);
let rpc_handler = Arc::new(KaspaRpcHandler::new(tasks, *encoding, core_service, options.clone()));
let router = Arc::new(Router::new(rpc_handler.server.clone()));
let server = RpcServer::new_with_encoding::<Server, Connection, RpcApiOps, Id64>(
*encoding,
rpc_handler.clone(),
router.interface.clone(),
Some(counters),
false,
);
WrpcService { options, server, rpc_handler, shutdown: SingleTrigger::default() }
}
pub fn serve(self: Arc<Self>) -> OneshotSender<()> {
let (termination_sender, termination_receiver) = oneshot_channel::<()>();
let listen_address = self.options.listen_address.clone();
self.rpc_handler.server.start();
let service = self.clone();
tokio::spawn(async move {
let _ = termination_receiver.await;
service.server.stop().unwrap_or_else(|err| warn!("wRPC unable to signal shutdown: `{err}`"));
service.server.join().await.unwrap_or_else(|err| warn!("wRPC error: `{err}"));
});
info!("WRPC Server starting on: {}", listen_address);
tokio::spawn(async move {
let config = WebSocketConfig { max_message_size: Some(MAX_WRPC_MESSAGE_SIZE), ..Default::default() };
match self.server.bind(&listen_address).await {
Ok(listener) => {
let serve_result = self.server.listen(listener, Some(config)).await;
match serve_result {
Ok(_) => info!("WRPC Server stopped on: {}", listen_address),
Err(err) => panic!("WRPC Server {listen_address} stopped with error: {err:?}"),
}
}
Err(err) => panic!("WRPC Server bind error on {listen_address}: {err:?}"),
}
});
termination_sender
}
}
const WRPC_SERVER: &str = "wrpc-service";
impl AsyncService for WrpcService {
fn ident(self: Arc<Self>) -> &'static str {
WRPC_SERVER
}
fn start(self: Arc<Self>) -> AsyncServiceFuture {
trace!("{} starting", WRPC_SERVER);
let shutdown_signal = self.shutdown.listener.clone();
trace!("{} running the wRPC server", WRPC_SERVER);
let terminate_server = self.clone().serve();
Box::pin(async move {
shutdown_signal.await;
self.clone()
.rpc_handler
.server
.join()
.await
.map_err(|err| AsyncServiceError::Service(format!("Notification system error: `{err}`")))?;
drop(terminate_server);
Ok(())
})
}
fn signal_exit(self: Arc<Self>) {
trace!("sending an exit signal to {}", WRPC_SERVER);
self.shutdown.trigger.trigger();
}
fn stop(self: Arc<Self>) -> AsyncServiceFuture {
Box::pin(async move {
trace!("{} stopped", WRPC_SERVER);
Ok(())
})
}
}