Files
oak-gpui/crates/rpc/src/conn.rs
T
Max Brunsfeld fab115e549 Adjust test connection to treat the half-open state more realistically
When a network connection is lost without being explicitly closed by the
other end, writes to that connection will error, but reads will just wait
indefinitely.

This allows the tests to exercise our heartbeat logic.
2022-03-04 16:47:55 -08:00

117 lines
4.0 KiB
Rust

use async_tungstenite::tungstenite::{Error as WebSocketError, Message as WebSocketMessage};
use futures::{SinkExt as _, StreamExt as _};
pub struct Connection {
pub(crate) tx:
Box<dyn 'static + Send + Unpin + futures::Sink<WebSocketMessage, Error = WebSocketError>>,
pub(crate) rx: Box<
dyn 'static
+ Send
+ Unpin
+ futures::Stream<Item = Result<WebSocketMessage, WebSocketError>>,
>,
}
impl Connection {
pub fn new<S>(stream: S) -> Self
where
S: 'static
+ Send
+ Unpin
+ futures::Sink<WebSocketMessage, Error = WebSocketError>
+ futures::Stream<Item = Result<WebSocketMessage, WebSocketError>>,
{
let (tx, rx) = stream.split();
Self {
tx: Box::new(tx),
rx: Box::new(rx),
}
}
pub async fn send(&mut self, message: WebSocketMessage) -> Result<(), WebSocketError> {
self.tx.send(message).await
}
#[cfg(any(test, feature = "test-support"))]
pub fn in_memory(
executor: std::sync::Arc<gpui::executor::Background>,
) -> (Self, Self, postage::barrier::Sender) {
use postage::prelude::Stream;
let (kill_tx, kill_rx) = postage::barrier::channel();
let (a_tx, a_rx) = channel(kill_rx.clone(), executor.clone());
let (b_tx, b_rx) = channel(kill_rx, executor);
return (
Self { tx: a_tx, rx: b_rx },
Self { tx: b_tx, rx: a_rx },
kill_tx,
);
fn channel(
kill_rx: postage::barrier::Receiver,
executor: std::sync::Arc<gpui::executor::Background>,
) -> (
Box<dyn Send + Unpin + futures::Sink<WebSocketMessage, Error = WebSocketError>>,
Box<
dyn Send + Unpin + futures::Stream<Item = Result<WebSocketMessage, WebSocketError>>,
>,
) {
use futures::channel::mpsc;
use std::{
io::{Error, ErrorKind},
sync::Arc,
};
let (tx, rx) = mpsc::unbounded::<WebSocketMessage>();
let tx = tx
.sink_map_err(|e| WebSocketError::from(Error::new(ErrorKind::Other, e)))
.with({
let kill_rx = kill_rx.clone();
let executor = Arc::downgrade(&executor);
move |msg| {
let mut kill_rx = kill_rx.clone();
let executor = executor.clone();
Box::pin(async move {
if let Some(executor) = executor.upgrade() {
executor.simulate_random_delay().await;
}
// Writes to a half-open TCP connection will error.
if kill_rx.try_recv().is_ok() {
std::io::Result::Err(
Error::new(ErrorKind::Other, "connection lost").into(),
)?;
}
Ok(msg)
})
}
});
let rx = rx.then({
let kill_rx = kill_rx.clone();
let executor = Arc::downgrade(&executor);
move |msg| {
let mut kill_rx = kill_rx.clone();
let executor = executor.clone();
Box::pin(async move {
if let Some(executor) = executor.upgrade() {
executor.simulate_random_delay().await;
}
// Reads from a half-open TCP connection will hang.
if kill_rx.try_recv().is_ok() {
futures::future::pending::<()>().await;
}
Ok(msg)
})
}
});
(Box::new(tx), Box::new(rx))
}
}
}