/* This file is part of DarkFi (https://dark.fi)
*
* Copyright (C) 2020-2026 Dyne.org foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
use std::{collections::HashSet, future::Future, io::ErrorKind, sync::Arc};
use async_trait::async_trait;
use futures::future::try_join_all;
use parking_lot::Mutex as SyncMutex;
use smol::{
io::{BufReader, ReadHalf, WriteHalf},
lock::{Mutex, MutexGuard},
};
use tinyjson::JsonValue;
use tracing::{debug, info, warn};
use url::Url;
use super::{
common::{
http_read_from_stream_request, http_write_to_stream, read_from_stream, write_to_stream,
INIT_BUF_SIZE,
},
jsonrpc::*,
settings::RpcSettings,
};
use crate::{
net::transport::{Listener, PtListener, PtStream},
system::{StoppableTask, StoppableTaskPtr},
util::logger::verbose,
Error, Result,
};
/// Asynchronous trait implementing a handler for incoming JSON-RPC requests.
#[async_trait]
pub trait RequestHandler: Sync + Send {
async fn handle_request(&self, req: JsonRequest) -> JsonResult;
async fn pong(&self, id: i64, _params: JsonValue) -> JsonResult {
JsonResponse::new(JsonValue::String("pong".to_string()), id).into()
}
async fn connections_mut(&self) -> MutexGuard<'life0, HashSet>;
async fn connections(&self) -> Vec {
self.connections_mut().await.iter().cloned().collect()
}
async fn mark_connection(&self, task: StoppableTaskPtr) {
self.connections_mut().await.insert(task);
}
async fn unmark_connection(&self, task: StoppableTaskPtr) {
self.connections_mut().await.remove(&task);
}
async fn active_connections(&self) -> usize {
self.connections_mut().await.len()
}
async fn stop_connections(&self) {
info!(target: "rpc::server", "[RPC] Server stopped, closing connections");
for (i, task) in self.connections().await.iter().enumerate() {
debug!(target: "rpc::server", "Stopping connection #{i}");
task.stop().await;
}
}
}
#[derive(Default)]
struct ConnectionTaskState {
closing: bool,
tasks: HashSet,
}
#[derive(Default)]
struct ConnectionTasks {
state: SyncMutex,
}
impl ConnectionTasks {
/// Register and start a child while holding the task-set lock. This prevents
/// a fast child from finishing before it has been registered and prevents
/// new children from racing with connection shutdown.
fn start<'a, MainFut>(
self: &Arc,
task: StoppableTaskPtr,
main: MainFut,
ex: Arc>,
) where
MainFut: Future