//! Route harness requests into its V8 inbox and preserve incremental NDJSON replies. use bytes::Bytes; use http::{Request, Response}; use http_body_util::{BodyExt, Full}; use serde_json::{Value, json}; use std::{ pin::Pin, task::{Context, Poll}, }; use tokio::sync::mpsc; struct Body(mpsc::UnboundedReceiver); impl http_body::Body for Body { type Data = Bytes; type Error = wasmtime_wasi_http::Error; fn poll_frame( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll, Self::Error>>> { self.0.poll_recv(cx).map(|value| { value.map(|value| { let mut bytes = serde_json::to_vec(&value).expect("harness JSON"); bytes.push(b'\n'); Ok(http_body::Frame::data(Bytes::from(bytes))) }) }) } } fn json_response(status: u16, value: Value) -> Response { Response::builder() .status(status) .header("content-type", "application/json") .body( Full::new(Bytes::from(value.to_string())) .map_err(|never| match never {}) .boxed_unsync(), ) .unwrap() } pub(super) async fn handle( api: &super::Api, caller: crate::cred::Caller, mut request: Request, ) -> Response { if request.method() == http::Method::GET && request.uri().path() == "/harness/credential-requests" { *request.uri_mut() = "/cred/requests?state=pending".parse().unwrap(); let response = super::handle(api, caller, request).await; let mut value: Value = serde_json::from_slice(response.body()).expect("credential JSON"); if let Some(requests) = value.as_array_mut() { for request in requests { request["approvalUrl"] = json!(format!("/#approve/{}", request["id"].as_str().unwrap())); } value = json!({"requests":value}); } return json_response(response.status().as_u16(), value); } let sender = api.harness.lock().unwrap().clone(); let Some(sender) = sender else { return json_response(503, json!({"error":"harness_unavailable"})); }; let body = if request.body().is_empty() { Value::Null } else { match serde_json::from_slice(request.body()) { Ok(body) => body, Err(_) => return json_response(400, json!({"error":"invalid_json"})), } }; let (reply, mut receiver) = mpsc::unbounded_channel(); let message = crate::harness::input::Input::Api { id: uuid::Uuid::new_v4().to_string(), method: request.method().to_string(), path: request.uri().path_and_query().unwrap().to_string(), body, reply, }; if sender.send(message).is_err() { return json_response(503, json!({"error":"harness_unavailable"})); } let Some(first) = receiver.recv().await else { return json_response(503, json!({"error":"harness_stopped"})); }; let status = first["status"].as_u64().unwrap_or(500) as u16; if first["stream"] == true { Response::builder() .status(status) .header("content-type", "application/x-ndjson") .body(Body(receiver).boxed_unsync()) .unwrap() } else { json_response(status, first["body"].clone()) } }