use std::borrow::Cow;
use bytes::Bytes;
use connectrpc::{ConnectRpcService, dispatcher::Dispatcher};
use http::{Response, header};
use http_body_util::BodyExt;
use tauri::Runtime;
use tower::ServiceExt;
use crate::body::IpcRequestBody;
pub const SCHEME: &str = "ipc-connect";
pub(crate) fn register<R: Runtime, D: Dispatcher>(
builder: tauri::plugin::Builder<R>,
service: ConnectRpcService<D>,
) -> tauri::plugin::Builder<R> {
builder.register_asynchronous_uri_scheme_protocol(SCHEME, move |_ctx, request, responder| {
let service = service.clone();
tauri::async_runtime::spawn(async move {
responder.respond(handle(service, request).await);
});
})
}
async fn handle<D: Dispatcher>(
service: ConnectRpcService<D>,
request: http::Request<Vec<u8>>,
) -> Response<Cow<'static, [u8]>> {
if request.method() == http::Method::OPTIONS {
return cors(
Response::builder()
.header(header::ACCESS_CONTROL_ALLOW_HEADERS, "*")
.header(header::ACCESS_CONTROL_ALLOW_METHODS, "POST, OPTIONS")
.body(Cow::Borrowed(&[][..]))
.expect("static preflight response"),
);
}
let request = request.map(|body| IpcRequestBody::complete(Bytes::from(body)));
let Ok(response) = service.oneshot(request).await;
let (parts, body) = response.into_parts();
let Ok(collected) = body.collect().await;
cors(Response::from_parts(
parts,
Cow::Owned(collected.to_bytes().to_vec()),
))
}
fn cors(mut response: Response<Cow<'static, [u8]>>) -> Response<Cow<'static, [u8]>> {
let headers = response.headers_mut();
headers.insert(
header::ACCESS_CONTROL_ALLOW_ORIGIN,
header::HeaderValue::from_static("*"),
);
headers.insert(
header::ACCESS_CONTROL_EXPOSE_HEADERS,
header::HeaderValue::from_static("*"),
);
response
}