Share owned host API state between HTTP hooks and native pages

This commit is contained in:
2026-11-07 00:23:57 +00:00
parent 45d099c3e4
commit c91ecbf7a4
14 changed files with 261 additions and 133 deletions
+26 -8
View File
@@ -17,16 +17,34 @@ use std::{
time::{Duration, Instant},
};
pub struct Api<'a> {
pub struct Api {
pub cred: Arc<Cred>,
script: cred::quickjs::QuickJs,
pub passkeys: Mutex<Passkeys<'a>>,
pub passkeys: Mutex<Passkeys<rusqlite::Connection>>,
pub entry: EntryKey,
requests: Mutex<HashMap<u32, (Instant, u32)>>,
}
impl<'a> Api<'a> {
impl Api {
/// Own the connection and ceremony state shared by both transports.
pub fn open(
cred: Arc<Cred>,
host_dir: &std::path::Path,
rp_id: &str,
origin: &str,
) -> anyhow::Result<Self> {
let db = rusqlite::Connection::open(host_dir.join("cred.sqlite"))?;
Ok(Self::new(
cred,
Passkeys::new(db, rp_id, origin)?,
EntryKey::open(&host_dir.join("entry.key"))?,
))
}
/// Passkeys owns the RP configuration. Its database must remain host-only.
pub fn new(cred: impl Into<Arc<Cred>>, passkeys: Passkeys<'a>, entry: EntryKey) -> Self {
pub fn new(
cred: impl Into<Arc<Cred>>,
passkeys: Passkeys<rusqlite::Connection>,
entry: EntryKey,
) -> Self {
Self {
cred: cred.into(),
script: cred::quickjs::QuickJs::default(),
@@ -113,10 +131,10 @@ fn request_view(request: cred::requests::Request) -> Result<Value> {
}
/// Handle a request without a network transport. Errors contain only static error kinds.
pub async fn handle(ctx: &Api<'_>, caller: Caller, request: Request<Bytes>) -> Response<Bytes> {
pub async fn handle(ctx: &Api, caller: Caller, request: Request<Bytes>) -> Response<Bytes> {
respond(ctx, caller, request)
}
fn respond(ctx: &Api<'_>, caller: Caller, request: Request<Bytes>) -> Response<Bytes> {
fn respond(ctx: &Api, caller: Caller, request: Request<Bytes>) -> Response<Bytes> {
let result = if request.method() == http::Method::GET {
match pages::get(ctx, request.uri().path()) {
Ok(Some(response)) => return response,
@@ -143,7 +161,7 @@ fn respond(ctx: &Api<'_>, caller: Caller, request: Request<Bytes>) -> Response<B
.body(Bytes::from(serde_json::to_vec(&body).expect("JSON value")))
.expect("valid response")
}
fn route(ctx: &Api<'_>, caller: &Caller, request: Request<Bytes>) -> Result<Value> {
fn route(ctx: &Api, caller: &Caller, request: Request<Bytes>) -> Result<Value> {
let path: Vec<_> = request.uri().path().trim_matches('/').split('/').collect();
let method = request.method().as_str();
let body: Value = if request.body().is_empty() {
@@ -206,7 +224,7 @@ mod tests {
let db = rusqlite::Connection::open_in_memory()?;
let api = Api::new(
Cred::open(dir.path())?,
Passkeys::new(&db, "localhost", "http://localhost:8080")?,
Passkeys::new(db, "localhost", "http://localhost:8080")?,
EntryKey::open(&dir.path().join("entry.key"))?,
);
let caller = Caller::default();