Flatten protobuf message namespace

* Remove `FromClient`/`FromServer` distinction.
* Remove `subscribe` concept - clients will need to handle
  unprompted messages from the server.

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Max Brunsfeld
2021-06-15 13:06:50 -07:00
co-authored by Nathan Sobo
parent 546ce14cb9
commit e88f33851e
5 changed files with 158 additions and 312 deletions
+51 -180
View File
@@ -2,7 +2,7 @@ use anyhow::{anyhow, Result};
use futures::future::Either;
use gpui::executor::Background;
use postage::{
barrier, mpsc,
barrier, oneshot,
prelude::{Sink, Stream},
};
use smol::{
@@ -14,18 +14,16 @@ use std::{
collections::HashMap,
future::Future,
sync::{
atomic::{self, AtomicI32},
atomic::{self, AtomicU32},
Arc,
},
};
use zed_rpc::proto::{
self, MessageStream, RequestMessage, SendMessage, ServerMessage, SubscribeMessage,
};
use zed_rpc::proto::{self, EnvelopedMessage, MessageStream, RequestMessage};
pub struct RpcClient {
response_channels: Arc<Mutex<HashMap<i32, (mpsc::Sender<proto::from_server::Variant>, bool)>>>,
response_channels: Arc<Mutex<HashMap<u32, oneshot::Sender<proto::Envelope>>>>,
outgoing: Mutex<MessageStream<BoxedWriter>>,
next_message_id: AtomicI32,
next_message_id: AtomicU32,
_drop_tx: barrier::Sender,
}
@@ -50,16 +48,14 @@ impl RpcClient {
response_channels,
outgoing: Mutex::new(MessageStream::new(Box::pin(conn_tx))),
_drop_tx,
next_message_id: AtomicI32::new(0),
next_message_id: AtomicU32::new(0),
})
}
async fn handle_incoming<Conn>(
conn: ReadHalf<Conn>,
mut drop_rx: barrier::Receiver,
response_channels: Arc<
Mutex<HashMap<i32, (mpsc::Sender<proto::from_server::Variant>, bool)>>,
>,
response_channels: Arc<Mutex<HashMap<u32, oneshot::Sender<proto::Envelope>>>>,
) where
Conn: AsyncRead + Unpin,
{
@@ -68,36 +64,27 @@ impl RpcClient {
let mut stream = MessageStream::new(conn);
loop {
let read_message = stream.read_message::<proto::FromServer>();
let read_message = stream.read_message();
smol::pin!(read_message);
match futures::future::select(read_message, &mut dropped).await {
Either::Left((Ok(incoming), _)) => {
if let Some(variant) = incoming.variant {
if let Some(request_id) = incoming.request_id {
let channel = response_channels.lock().await.remove(&request_id);
if let Some((mut tx, oneshot)) = channel {
if tx.send(variant).await.is_ok() {
if !oneshot {
response_channels
.lock()
.await
.insert(request_id, (tx, false));
}
}
} else {
log::warn!(
"received RPC response to unknown request id {}",
request_id
);
}
if let Some(responding_to) = incoming.responding_to {
let channel = response_channels.lock().await.remove(&responding_to);
if let Some(mut tx) = channel {
tx.send(incoming).await.ok();
} else {
log::warn!(
"received RPC response to unknown request {}",
responding_to
);
}
} else {
log::warn!("received RPC message with no content");
// unprompted message from server
}
}
Either::Left((Err(error), _)) => {
log::warn!("invalid incoming RPC message {:?}", error);
log::warn!("received invalid RPC message {:?}", error);
}
Either::Right(_) => break,
}
@@ -111,69 +98,37 @@ impl RpcClient {
let this = self.clone();
async move {
let message_id = this.next_message_id.fetch_add(1, atomic::Ordering::SeqCst);
let (tx, mut rx) = mpsc::channel(1);
this.response_channels
.lock()
.await
.insert(message_id, (tx, true));
let (tx, mut rx) = oneshot::channel();
this.response_channels.lock().await.insert(message_id, tx);
this.outgoing
.lock()
.await
.write_message(&proto::FromClient {
id: message_id,
variant: Some(req.to_variant()),
})
.write_message(&req.into_envelope(message_id, None))
.await?;
let response = rx
.recv()
.await
.expect("response channel was unexpectedly dropped");
T::Response::from_variant(response)
T::Response::from_envelope(response)
.ok_or_else(|| anyhow!("received response of the wrong t"))
}
}
pub fn send<T: SendMessage>(self: &Arc<Self>, message: T) -> impl Future<Output = Result<()>> {
pub fn send<T: EnvelopedMessage>(
self: &Arc<Self>,
message: T,
) -> impl Future<Output = Result<()>> {
let this = self.clone();
async move {
let message_id = this.next_message_id.fetch_add(1, atomic::Ordering::SeqCst);
this.outgoing
.lock()
.await
.write_message(&proto::FromClient {
id: message_id,
variant: Some(message.to_variant()),
})
.write_message(&message.into_envelope(message_id, None))
.await?;
Ok(())
}
}
pub fn subscribe<T: SubscribeMessage>(
self: &Arc<Self>,
subscription: T,
) -> impl Future<Output = Result<impl Stream<Item = Result<T::Event>>>> {
let this = self.clone();
async move {
let message_id = this.next_message_id.fetch_add(1, atomic::Ordering::SeqCst);
let (tx, rx) = mpsc::channel(256);
this.response_channels
.lock()
.await
.insert(message_id, (tx, false));
this.outgoing
.lock()
.await
.write_message(&proto::FromClient {
id: message_id,
variant: Some(subscription.to_variant()),
})
.await?;
Ok(rx.map(|event| {
T::Event::from_variant(event).ok_or_else(|| anyhow!("invalid event {:?}"))
}))
}
}
}
#[cfg(test)]
@@ -199,133 +154,49 @@ mod tests {
let mut server_stream = MessageStream::new(server_conn);
let client = RpcClient::new(client_conn, executor.clone());
let client_req = client.request(proto::from_client::Auth {
let client_req = client.request(proto::Auth {
user_id: 42,
access_token: "token".to_string(),
});
smol::pin!(client_req);
let server_req = send_recv(
&mut client_req,
server_stream.read_message::<proto::FromClient>(),
)
.await
.unwrap();
let server_req = send_recv(&mut client_req, server_stream.read_message())
.await
.unwrap();
assert_eq!(
server_req.variant,
Some(proto::from_client::Variant::Auth(
proto::from_client::Auth {
user_id: 42,
access_token: "token".to_string()
}
))
server_req.payload,
Some(proto::envelope::Payload::Auth(proto::Auth {
user_id: 42,
access_token: "token".to_string()
}))
);
// Respond to another request to ensure requests are properly matched up.
server_stream
.write_message(&proto::FromServer {
request_id: Some(999),
variant: Some(proto::from_server::Variant::AuthResponse(
proto::from_server::AuthResponse {
credentials_valid: false,
},
)),
})
.write_message(
&proto::AuthResponse {
credentials_valid: false,
}
.into_envelope(1000, Some(999)),
)
.await
.unwrap();
server_stream
.write_message(&proto::FromServer {
request_id: Some(server_req.id),
variant: Some(proto::from_server::Variant::AuthResponse(
proto::from_server::AuthResponse {
credentials_valid: true,
},
)),
})
.write_message(
&proto::AuthResponse {
credentials_valid: true,
}
.into_envelope(1001, Some(server_req.id)),
)
.await
.unwrap();
assert_eq!(
client_req.await.unwrap(),
proto::from_server::AuthResponse {
proto::AuthResponse {
credentials_valid: true
}
);
}
#[gpui::test]
async fn test_subscribe(cx: gpui::TestAppContext) {
let executor = cx.read(|app| app.background_executor().clone());
let socket_dir_path = TempDir::new("subscribe").unwrap();
let socket_path = socket_dir_path.path().join(".sock");
let listener = UnixListener::bind(&socket_path).unwrap();
let client_conn = UnixStream::connect(&socket_path).await.unwrap();
let (server_conn, _) = listener.accept().await.unwrap();
let mut server_stream = MessageStream::new(server_conn);
let client = RpcClient::new(client_conn, executor.clone());
let mut events = client
.subscribe(proto::from_client::SubscribeToPathRequests {})
.await
.unwrap();
let subscription = server_stream
.read_message::<proto::FromClient>()
.await
.unwrap();
assert_eq!(
subscription.variant,
Some(proto::from_client::Variant::SubscribeToPathRequests(
proto::from_client::SubscribeToPathRequests {}
))
);
server_stream
.write_message(&proto::FromServer {
request_id: Some(subscription.id),
variant: Some(proto::from_server::Variant::PathRequest(
proto::from_server::PathRequest {
path: b"path-1".to_vec(),
},
)),
})
.await
.unwrap();
server_stream
.write_message(&proto::FromServer {
request_id: Some(99999),
variant: Some(proto::from_server::Variant::PathRequest(
proto::from_server::PathRequest {
path: b"path-2".to_vec(),
},
)),
})
.await
.unwrap();
server_stream
.write_message(&proto::FromServer {
request_id: Some(subscription.id),
variant: Some(proto::from_server::Variant::PathRequest(
proto::from_server::PathRequest {
path: b"path-3".to_vec(),
},
)),
})
.await
.unwrap();
assert_eq!(
events.recv().await.unwrap().unwrap(),
proto::from_server::PathRequest {
path: b"path-1".to_vec()
}
);
assert_eq!(
events.recv().await.unwrap().unwrap(),
proto::from_server::PathRequest {
path: b"path-3".to_vec()
}
);
}
#[gpui::test]
async fn test_drop_client(cx: gpui::TestAppContext) {
let executor = cx.read(|app| app.background_executor().clone());
@@ -362,7 +233,7 @@ mod tests {
let client = RpcClient::new(client_conn, executor.clone());
let err = client
.request(proto::from_client::Auth {
.request(proto::Auth {
user_id: 42,
access_token: "token".to_string(),
})
+2 -2
View File
@@ -673,8 +673,8 @@ impl Workspace {
let rpc_client = RpcClient::new(stream, executor);
let auth_response = rpc_client
.request(proto::from_client::Auth {
user_id: user_id.parse::<i32>()?,
.request(proto::Auth {
user_id: user_id.parse::<u64>()?,
access_token,
})
.await?;
+5 -9
View File
@@ -32,7 +32,7 @@ use std::{
sync::{Arc, Weak},
time::{Duration, SystemTime, UNIX_EPOCH},
};
use zed_rpc::proto::{self, from_client::PathAndDigest};
use zed_rpc::proto;
use self::{char_bag::CharBag, ignore::IgnoreStack};
@@ -234,23 +234,19 @@ impl Worktree {
self.rpc_client = Some(client.clone());
let snapshot = self.snapshot();
cx.spawn(|_this, cx| async move {
let files = cx
let paths = cx
.background_executor()
.spawn(async move {
snapshot
.paths()
.map(|path| PathAndDigest {
path: path.as_os_str().as_bytes().to_vec(),
digest: Default::default(),
})
.map(|path| path.as_os_str().as_bytes().to_vec())
.collect()
})
.await;
let share_response = client
.request(proto::from_client::ShareWorktree {
worktree_id: 0,
files,
.request(proto::ShareWorktree {
worktree: Some(proto::Worktree { paths }),
})
.await?;