Skip to content

Commit

Permalink
feat(plugin): add async io for the plugin
Browse files Browse the repository at this point in the history
Adding the support of the async io for reading to the
std io.

Link: #98
Signed-off-by: Vincenzo Palazzo <vincenzopalazzodev@gmail.com>
  • Loading branch information
vincenzopalazzo committed Mar 24, 2024
1 parent 56b385f commit 1c04dbe
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 18 deletions.
3 changes: 2 additions & 1 deletion plugin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ keywords = [ "plugin", "cln", "rpc", "lightning", "bitcoin" ]
readme = "README.md"

[dependencies]
clightningrpc-common = { version = "0.3.0-beta.4" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
clightningrpc-common = { version = "0.3.0-beta.4" }
log = { version = "0.4.17", optional = true }
mio = { version = "0.8.10", features = ["os-ext"] }

[features]
log = ["dep:log"]
70 changes: 70 additions & 0 deletions plugin/src/io.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//! async io module of the plugin io.
//!
//! Vincenzo Palazzo <vincenzopalazzo@member.fsf.org>
use std::io;
use std::io::{Read, Write};
use std::os::fd::AsRawFd;

const SERVER: mio::Token = mio::Token(0);

pub(crate) struct AsyncIO {
poll: mio::Poll,
}

impl AsyncIO {
/// Create a new instance of an AsyncIO
pub fn new() -> io::Result<Self> {
Ok(Self {
poll: mio::Poll::new()?,
})
}

pub fn register(&mut self) -> io::Result<()> {
let stdin = std::io::stdin().as_raw_fd();
let mut stdin = mio::unix::SourceFd(&stdin);
let stdout = std::io::stdout().as_raw_fd();
let mut stdout = mio::unix::SourceFd(&stdout);

self.poll
.registry()
.register(&mut stdin, SERVER, mio::Interest::READABLE)?;
self.poll
.registry()
.register(&mut stdout, SERVER, mio::Interest::WRITABLE)?;
Ok(())
}

pub fn into_loop<F: FnMut(String) -> String>(&mut self, mut async_callback: F) {

Check warning on line 37 in plugin/src/io.rs

View workflow job for this annotation

GitHub Actions / clippy

methods called `into_*` usually take `self` by value

warning: methods called `into_*` usually take `self` by value --> plugin/src/io.rs:37:50 | 37 | pub fn into_loop<F: FnMut(String) -> String>(&mut self, mut async_callback: F) { | ^^^^^^^^^ | = help: consider choosing a less ambiguous name = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#wrong_self_convention = note: `#[warn(clippy::wrong_self_convention)]` on by default
let mut events = mio::Events::with_capacity(1024);
loop {
self.poll.poll(&mut events, None).unwrap();

for event in events.iter() {
match event.token() {
SERVER => {
if event.is_readable() {
let mut reader = io::stdin().lock();
let mut buffer = String::new();
loop {
let mut byte = [0; 1];
reader.read_exact(&mut byte).unwrap();

// Append the byte to the buffer
buffer.push(byte[0] as char);

// Check if the buffer ends with double newline
if buffer.ends_with("\n\n") {
break; // Exit the loop
}
}
let resp = async_callback(buffer.clone());
io::stdout().write_all(resp.as_bytes()).unwrap();
io::stdout().flush().unwrap();
}
}
_ => unreachable!(),
}
}
}
}
}
1 change: 1 addition & 0 deletions plugin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#![crate_name = "clightningrpc_plugin"]
pub mod commands;
pub mod errors;
mod io;
pub mod macros;
pub mod plugin;
pub mod types;
29 changes: 12 additions & 17 deletions plugin/src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use crate::commands::builtin::{InitRPC, ManifestRPC};
use crate::commands::types::{CLNConf, RPCHookInfo, RPCMethodInfo};
use crate::commands::RPCCommand;
use crate::errors::PluginError;
use crate::io::AsyncIO;
use crate::types::{LogLevel, RpcOption};

#[cfg(feature = "log")]
Expand Down Expand Up @@ -257,9 +258,6 @@ impl<'a, T: 'a + Clone> Plugin<T> {
}

pub fn start(mut self) {
let reader = io::stdin();
let mut writer = io::stdout();
let mut buffer = String::new();
#[cfg(feature = "log")]
{
use std::str::FromStr;
Expand All @@ -276,29 +274,26 @@ impl<'a, T: 'a + Clone> Plugin<T> {
on_init: self.on_init.clone(),
}),
);
// FIXME: core lightning end with the double endline, so this can cause
// problem for some input reader.
// we need to parse the writer, and avoid this while loop
while let Ok(_) = reader.read_line(&mut buffer) {
let req_str = buffer.to_string();
buffer.clear();
let Ok(request) = serde_json::from_str::<Request<serde_json::Value>>(&req_str) else {
continue;
};
let mut asyncio = AsyncIO::new().unwrap();
asyncio.register().unwrap();
asyncio.into_loop(|buffer| {
self.log(
LogLevel::Info,
&format!("looping around the string: {buffer}"),
);
let request: Request<serde_json::Value> = serde_json::from_str(&buffer).unwrap();
if let Some(id) = request.id {
// when the id is specified this is a RPC or Hook, so we need to return a response
let response = self.call_rpc_method(&request.method, request.params);
let mut rpc_response = init_success_response(id);
self.write_respose(&response, &mut rpc_response);
writer
.write_all(serde_json::to_string(&rpc_response).unwrap().as_bytes())
.unwrap();
writer.flush().unwrap();
return serde_json::to_string(&rpc_response).unwrap();

Check warning on line 290 in plugin/src/plugin.rs

View workflow job for this annotation

GitHub Actions / clippy

unneeded `return` statement

warning: unneeded `return` statement --> plugin/src/plugin.rs:290:17 | 290 | return serde_json::to_string(&rpc_response).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_return = note: `#[warn(clippy::needless_return)]` on by default help: remove `return` | 290 - return serde_json::to_string(&rpc_response).unwrap(); 290 + serde_json::to_string(&rpc_response).unwrap() |
} else {
// in case of the id is None, we are receiving the notification, so the server is not
// interested in the answer.
self.handle_notification(&request.method, request.params);
return String::new();

Check warning on line 295 in plugin/src/plugin.rs

View workflow job for this annotation

GitHub Actions / clippy

unneeded `return` statement

warning: unneeded `return` statement --> plugin/src/plugin.rs:295:17 | 295 | return String::new(); | ^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_return help: remove `return` | 295 - return String::new(); 295 + String::new() |
}
}
});
}
}

0 comments on commit 1c04dbe

Please sign in to comment.