mod websocket_adapter;
use crate::bidirectional_proxy::BidirectionalProxy;
use crate::internal_proxy::Error::NotConnected;
use crate::once_nonlock::OnceNonLock;
#[cfg(not(target_arch = "wasm32"))]
use std::net::TcpStream;
use std::sync::{Arc, LazyLock, Mutex};
#[derive(Debug)]
pub enum Error {
NotConnected,
}
static INTERNAL_PROXY: LazyLock<InternalProxy> = LazyLock::new(InternalProxy::new);
#[derive(Debug)]
pub struct InternalProxy {
#[cfg(feature = "logwise")]
buffered_notification_sender: std::sync::mpsc::Sender<crate::jrpc::Notification>,
buffered_notification_receiver: Mutex<std::sync::mpsc::Receiver<crate::jrpc::Notification>>,
bidirectional_proxy: Arc<OnceNonLock<BidirectionalProxy>>,
}
fn bidi_fn(msg: Box<[u8]>) -> Option<Box<[u8]>> {
eprintln!(
"ip: received bidi message: {:?}",
String::from_utf8_lossy(&msg)
);
let request: Result<crate::jrpc::Request, _> = serde_json::from_slice(&msg);
match request {
Ok(request) => {
eprintln!("ip: received request: {:?}", request);
let response = crate::mcp::dispatch_in_target(request);
let response_bytes = serde_json::to_vec(&response).unwrap();
eprintln!(
"ip: sending response {:?}",
String::from_utf8_lossy(&response_bytes)
);
Some(response_bytes.into_boxed_slice())
}
Err(e) => {
todo!(
"Not implemented yet: Received request from internal proxy: {:?}",
e
);
}
}
}
const ADDR: &str = "127.0.0.1:1985";
impl InternalProxy {
fn new() -> Self {
let (_sender, receiver) = std::sync::mpsc::channel();
let m = InternalProxy {
#[cfg(feature = "logwise")]
buffered_notification_sender: _sender,
buffered_notification_receiver: Mutex::new(receiver),
bidirectional_proxy: Arc::new(OnceNonLock::new()),
};
m.reconnect_if_possible();
m
}
fn reconnect_if_possible(&self) {
#[cfg(not(target_arch = "wasm32"))]
self.bidirectional_proxy.try_get_or_init(|| {
let s = TcpStream::connect(ADDR);
match s {
Ok(stream) => {
let write_stream = stream
.try_clone()
.expect("Failed to clone stream for writing");
let read_stream = stream;
let stream = crate::bidirectional_proxy::BidirectionalProxy::new(
write_stream,
read_stream,
bidi_fn,
);
Some(stream)
}
Err(_e) => None,
}
});
#[cfg(target_arch = "wasm32")]
{
let f = self.bidirectional_proxy.init_async(async move || {
if web_sys::window().is_none() {
crate::internal_proxy::websocket_adapter::patch_close();
}
let stream = websocket_adapter::adapter().await;
match stream {
Ok(stream) => {
let stream = crate::bidirectional_proxy::BidirectionalProxy::new(
stream.0, stream.1, bidi_fn,
);
Some(stream)
}
Err(e) => {
crate::logging::log(&format!("ip: Failed to connect to {}: {}", ADDR, e));
None
}
}
});
wasm_bindgen_futures::spawn_local(f)
}
}
pub fn send_notification(&self, notification: crate::jrpc::Notification) -> Result<(), Error> {
self.send_buffered_if_possible();
if let Some(proxy) = self.bidirectional_proxy.get() {
let msg = serde_json::to_string(¬ification).map_err(|_| NotConnected)?;
proxy.send(msg.as_bytes()).map_err(|_| NotConnected)
} else {
Err(NotConnected)
}
}
#[cfg(feature = "logwise")]
pub fn buffer_notification(&self, notification: crate::jrpc::Notification) {
self.buffered_notification_sender
.send(notification)
.unwrap();
self.send_buffered_if_possible();
}
fn send_buffered_if_possible(&self) {
self.reconnect_if_possible();
if let Some(proxy) = self.bidirectional_proxy.get() {
let mut take = Vec::new();
if let Ok(buffered_receiver) = self.buffered_notification_receiver.try_lock() {
while let Ok(notification) = buffered_receiver.try_recv() {
take.push(notification);
}
} else {
crate::logging::log("ip: Send contended");
}
for notification in take {
let msg = serde_json::to_string(¬ification).unwrap();
if let Err(e) = proxy.send(msg.as_bytes()) {
crate::logging::log(&format!(
"ip: Failed to send buffered notification: {}",
e
));
}
}
}
}
pub fn current() -> &'static InternalProxy {
&INTERNAL_PROXY
}
}