host: expose harness state and actions over the world API
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
//! HTTP requests share the harness inbox. Replies never leave the V8 thread as JS values.
|
||||
use deno_core::{OpState, op2};
|
||||
use serde_json::Value;
|
||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
pub(super) struct Reply {
|
||||
pub sender: UnboundedSender<Value>,
|
||||
pub finished: std::sync::Arc<tokio::sync::Notify>,
|
||||
}
|
||||
pub(super) type Replies = HashMap<String, Reply>;
|
||||
|
||||
#[op2]
|
||||
pub fn op_harness_reply(
|
||||
state: &mut OpState,
|
||||
#[string] id: String,
|
||||
#[serde] value: serde_json::Value,
|
||||
) {
|
||||
let replies = state.borrow_mut::<Replies>();
|
||||
if value.is_null() {
|
||||
if let Some(reply) = replies.remove(&id) {
|
||||
reply.finished.notify_one();
|
||||
}
|
||||
} else if let Some(reply) = replies.get(&id) {
|
||||
let _ = reply.sender.send(value);
|
||||
}
|
||||
}
|
||||
|
||||
#[op2]
|
||||
pub async fn op_harness_closed(state: Rc<RefCell<OpState>>, #[string] id: String) {
|
||||
let reply = state
|
||||
.borrow()
|
||||
.borrow::<Replies>()
|
||||
.get(&id)
|
||||
.map(|reply| (reply.sender.clone(), reply.finished.clone()));
|
||||
if let Some((sender, finished)) = reply {
|
||||
tokio::select! {
|
||||
_ = sender.closed() => {},
|
||||
_ = finished.notified() => {},
|
||||
}
|
||||
}
|
||||
state.borrow_mut().borrow_mut::<Replies>().remove(&id);
|
||||
}
|
||||
@@ -15,6 +15,14 @@ pub enum Input {
|
||||
#[serde(rename = "requestId")]
|
||||
request_id: String,
|
||||
},
|
||||
Api {
|
||||
id: String,
|
||||
method: String,
|
||||
path: String,
|
||||
body: Value,
|
||||
#[serde(skip)]
|
||||
reply: mpsc::UnboundedSender<Value>,
|
||||
},
|
||||
Stop,
|
||||
}
|
||||
|
||||
@@ -31,8 +39,20 @@ pub(super) struct Inbox(pub tokio::sync::Mutex<Receiver>);
|
||||
#[serde]
|
||||
pub async fn op_input(state: Rc<RefCell<OpState>>) -> Result<serde_json::Value, JsErrorBox> {
|
||||
let inbox = state.borrow().borrow::<Rc<Inbox>>().clone();
|
||||
serde_json::to_value(inbox.0.lock().await.recv().await)
|
||||
.map_err(|error| JsErrorBox::generic(error.to_string()))
|
||||
let input = inbox.0.lock().await.recv().await;
|
||||
if let Some(Input::Api { id, reply, .. }) = &input {
|
||||
state
|
||||
.borrow_mut()
|
||||
.borrow_mut::<super::http::Replies>()
|
||||
.insert(
|
||||
id.clone(),
|
||||
super::http::Reply {
|
||||
sender: reply.clone(),
|
||||
finished: Default::default(),
|
||||
},
|
||||
);
|
||||
}
|
||||
serde_json::to_value(input).map_err(|error| JsErrorBox::generic(error.to_string()))
|
||||
}
|
||||
|
||||
pub(super) fn console(sender: Sender) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Durable V8 harness, host-side SQLite storage, and world tool operations.
|
||||
mod credentials;
|
||||
mod environment;
|
||||
mod http;
|
||||
pub mod input;
|
||||
pub(crate) mod loader;
|
||||
use crate::{exec::Exec, world::World};
|
||||
@@ -88,6 +89,8 @@ deno_core::extension!(
|
||||
environment::op_exec,
|
||||
environment::op_exec_next,
|
||||
input::op_input,
|
||||
http::op_harness_reply,
|
||||
http::op_harness_closed,
|
||||
environment::op_watch,
|
||||
environment::op_watch_next,
|
||||
loader::op_modules,
|
||||
@@ -181,6 +184,10 @@ pub async fn run_module_with_input(
|
||||
..Default::default()
|
||||
});
|
||||
runtime.op_state().borrow_mut().put(loader);
|
||||
runtime
|
||||
.op_state()
|
||||
.borrow_mut()
|
||||
.put(http::Replies::default());
|
||||
runtime
|
||||
.op_state()
|
||||
.borrow_mut()
|
||||
@@ -252,6 +259,17 @@ pub async fn run_console(
|
||||
cred: Arc<crate::cred::store::Cred>,
|
||||
) -> anyhow::Result<()> {
|
||||
let (sender, receiver) = input::channel();
|
||||
run_console_with_input(world, exec, cred, sender, receiver).await
|
||||
}
|
||||
|
||||
/// Run the console with the inbox shared by the host HTTP API.
|
||||
pub async fn run_console_with_input(
|
||||
world: World,
|
||||
exec: Arc<dyn Exec>,
|
||||
cred: Arc<crate::cred::store::Cred>,
|
||||
sender: input::Sender,
|
||||
receiver: input::Receiver,
|
||||
) -> anyhow::Result<()> {
|
||||
input::console(sender.clone());
|
||||
let signals = tokio::spawn(input::stop_signal(sender)?);
|
||||
let source = concat!(
|
||||
|
||||
Reference in New Issue
Block a user