Share owned host API state between HTTP hooks and native pages
This commit is contained in:
+67
-65
@@ -1,6 +1,6 @@
|
||||
//! The host HTTP listener and console startup with one shared credentials store.
|
||||
//! The host HTTP listener and console startup share one owned API and credentials store.
|
||||
use super::{Api, respond};
|
||||
use crate::cred::{Caller, Cred, entry::EntryKey, webauthn::Passkeys};
|
||||
use crate::cred::{Caller, Cred};
|
||||
use std::{net::SocketAddr, path::Path, sync::Arc};
|
||||
|
||||
#[derive(Clone, Debug, clap::Args)]
|
||||
@@ -22,71 +22,58 @@ pub struct Listener {
|
||||
}
|
||||
impl Listener {
|
||||
pub fn start(cred: Arc<Cred>, host_dir: &Path, options: Options) -> anyhow::Result<Self> {
|
||||
let api = Arc::new(Api::open(
|
||||
cred,
|
||||
host_dir,
|
||||
&options.api_rp_id,
|
||||
&options.api_origin,
|
||||
)?);
|
||||
Self::start_with_api(api, options)
|
||||
}
|
||||
pub fn start_with_api(api: Arc<Api>, options: Options) -> anyhow::Result<Self> {
|
||||
let server = Arc::new(
|
||||
tiny_http::Server::http(options.api_listen)
|
||||
.map_err(|_| anyhow::anyhow!("host API address unavailable"))?,
|
||||
);
|
||||
let serving = server.clone();
|
||||
let host_dir = host_dir.to_owned();
|
||||
let (ready, started) = std::sync::mpsc::sync_channel(1);
|
||||
let thread = std::thread::spawn(move || {
|
||||
let setup = || -> anyhow::Result<_> {
|
||||
Ok((
|
||||
rusqlite::Connection::open(host_dir.join("cred.sqlite"))?,
|
||||
EntryKey::open(&host_dir.join("entry.key"))?,
|
||||
))
|
||||
};
|
||||
let serve = || -> anyhow::Result<()> {
|
||||
let (db, entry) = setup()?;
|
||||
let passkeys = Passkeys::new(&db, &options.api_rp_id, &options.api_origin)?;
|
||||
let api = Api::new(cred, passkeys, entry);
|
||||
let _ = ready.send(true);
|
||||
for mut request in serving.incoming_requests() {
|
||||
let response = (|| -> anyhow::Result<_> {
|
||||
let mut body = Vec::new();
|
||||
request.as_reader().read_to_end(&mut body)?;
|
||||
let request = http::Request::builder()
|
||||
.method(request.method().as_str())
|
||||
.uri(request.url())
|
||||
.body(bytes::Bytes::from(body))?;
|
||||
Ok(respond(
|
||||
&api,
|
||||
Caller {
|
||||
program: "/host/browser".into(),
|
||||
pid: std::process::id(),
|
||||
conversations: Vec::new(),
|
||||
},
|
||||
request,
|
||||
for mut request in serving.incoming_requests() {
|
||||
let response = (|| -> anyhow::Result<_> {
|
||||
let mut body = Vec::new();
|
||||
request.as_reader().read_to_end(&mut body)?;
|
||||
let request = http::Request::builder()
|
||||
.method(request.method().as_str())
|
||||
.uri(request.url())
|
||||
.body(bytes::Bytes::from(body))?;
|
||||
Ok(respond(
|
||||
&api,
|
||||
Caller {
|
||||
program: "/host/browser".into(),
|
||||
pid: std::process::id(),
|
||||
conversations: Vec::new(),
|
||||
},
|
||||
request,
|
||||
))
|
||||
})();
|
||||
let response = response.unwrap_or_else(|_| {
|
||||
http::Response::builder()
|
||||
.status(400)
|
||||
.header("content-type", "application/json")
|
||||
.body(bytes::Bytes::from_static(
|
||||
b"{\"error\":\"invalid_request\",\"message\":\"invalid_request\"}",
|
||||
))
|
||||
})();
|
||||
let response = response.unwrap_or_else(|_| {
|
||||
http::Response::builder()
|
||||
.status(400)
|
||||
.header("content-type", "application/json")
|
||||
.body(bytes::Bytes::from_static(
|
||||
b"{\"error\":\"invalid_request\",\"message\":\"invalid_request\"}",
|
||||
))
|
||||
.expect("static response")
|
||||
});
|
||||
let content_type = response.headers()["content-type"].as_bytes();
|
||||
let reply = tiny_http::Response::from_data(response.body().to_vec())
|
||||
.with_status_code(response.status().as_u16())
|
||||
.with_header(
|
||||
tiny_http::Header::from_bytes(b"Content-Type", content_type)
|
||||
.expect("static header"),
|
||||
);
|
||||
let _ = request.respond(reply);
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
if serve().is_err() {
|
||||
let _ = ready.send(false);
|
||||
.expect("static response")
|
||||
});
|
||||
let content_type = response.headers()["content-type"].as_bytes();
|
||||
let reply = tiny_http::Response::from_data(response.body().to_vec())
|
||||
.with_status_code(response.status().as_u16())
|
||||
.with_header(
|
||||
tiny_http::Header::from_bytes(b"Content-Type", content_type)
|
||||
.expect("static header"),
|
||||
);
|
||||
let _ = request.respond(reply);
|
||||
}
|
||||
});
|
||||
if !started.recv().unwrap_or(false) {
|
||||
let _ = thread.join();
|
||||
anyhow::bail!("host API initialization failed");
|
||||
}
|
||||
Ok(Self {
|
||||
server,
|
||||
thread: Some(thread),
|
||||
@@ -105,7 +92,7 @@ impl Drop for Listener {
|
||||
}
|
||||
}
|
||||
|
||||
/// Preserve the console lifecycle while sharing Cred with the host page listener.
|
||||
/// Preserve the console lifecycle with one API for guests and host pages.
|
||||
pub async fn run_with_userland(
|
||||
path: &Path,
|
||||
userland: Option<&Path>,
|
||||
@@ -113,12 +100,26 @@ pub async fn run_with_userland(
|
||||
) -> anyhow::Result<()> {
|
||||
let world = crate::world::World::open_with_userland(path, userland)?;
|
||||
let cred = Arc::new(Cred::open(&world.host_dir)?);
|
||||
let _listener = Listener::start(cred.clone(), &world.host_dir, options)?;
|
||||
let runtime = crate::process::Runtime::new(crate::world::World {
|
||||
root: world.root.clone(),
|
||||
host_dir: world.host_dir.clone(),
|
||||
processes: world.processes.clone(),
|
||||
})?;
|
||||
let api = Arc::new(Api::open(
|
||||
cred.clone(),
|
||||
&world.host_dir,
|
||||
&options.api_rp_id,
|
||||
&options.api_origin,
|
||||
)?);
|
||||
let runtime = crate::process::Runtime::new_with_hooks(
|
||||
crate::world::World {
|
||||
root: world.root.clone(),
|
||||
host_dir: world.host_dir.clone(),
|
||||
processes: world.processes.clone(),
|
||||
},
|
||||
cred.clone(),
|
||||
crate::process::default_script(),
|
||||
Some(api.clone()),
|
||||
)?;
|
||||
let listener = Listener::start_with_api(api, options)?;
|
||||
runtime
|
||||
.network_policy()
|
||||
.deny_host_port(listener.address().port());
|
||||
let services = crate::process::supervise(runtime.clone());
|
||||
let result = tokio::task::spawn_blocking(move || -> anyhow::Result<()> {
|
||||
tokio::runtime::Builder::new_current_thread()
|
||||
@@ -146,5 +147,6 @@ pub async fn run_with_userland(
|
||||
anyhow::anyhow!(text)
|
||||
});
|
||||
services.shutdown().await;
|
||||
drop(listener);
|
||||
result
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user