Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 676032b

Browse files
committed
Ping the client every 30s to keep the connection alive
1 parent 65bdcf8 commit 676032b

File tree

2 files changed

+35
-6
lines changed

2 files changed

+35
-6
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ serde = { version = "1.0.126", features = ["derive"] }
2121
serde_json = "1.0.64"
2222
url = "2.2.2"
2323

24-
tokio = { version = "1.6.1", features = ["fs", "process", "macros", "rt", "rt-multi-thread"] }
24+
tokio = { version = "1.6.1", features = ["fs", "process", "macros", "rt", "rt-multi-thread", "time"] }
2525
tokio-util = { version = "0.6.7", features = ["codec"] }
2626
warp = { git = "https://github.com/kazk/warp", branch = "permessage-deflate", default-features = false, features = ["websocket"] }
2727

src/api/proxy.rs

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::{convert::Infallible, process::Stdio, str::FromStr};
22

33
use futures_util::{
44
future::{select, Either},
5-
SinkExt, StreamExt,
5+
stream, SinkExt, StreamExt,
66
};
77
use tokio::{fs, process::Command};
88
use url::Url;
@@ -108,7 +108,19 @@ async fn connected(
108108
let mut server_send = lsp::framed::writer(server.stdin.take().unwrap());
109109
let mut server_recv = lsp::framed::reader(server.stdout.take().unwrap());
110110
let (mut client_send, client_recv) = ws.split();
111-
let mut client_recv = client_recv.filter_map(filter_map_warp_ws_message).boxed();
111+
let client_recv = client_recv
112+
.filter_map(filter_map_warp_ws_message)
113+
// Chain this with `Done` so we know when the client disconnects
114+
.chain(stream::once(async { Ok(Message::Done) }));
115+
// Tick every 30s so we can ping the client to keep the connection alive
116+
let ticks = stream::unfold(
117+
tokio::time::interval(std::time::Duration::from_secs(30)),
118+
|mut interval| async move {
119+
interval.tick().await;
120+
Some((Ok(Message::Tick), interval))
121+
},
122+
);
123+
let mut client_recv = stream::select(client_recv, ticks).boxed();
112124

113125
let mut client_msg = client_recv.next();
114126
let mut server_msg = server_recv.next();
@@ -145,15 +157,26 @@ async fn connected(
145157
tracing::info!("received Close message");
146158
}
147159

160+
// Ping the client to keep the connection alive
161+
Some(Ok(Message::Tick)) => {
162+
tracing::debug!("pinging the client");
163+
client_send.send(warp::ws::Message::ping(vec![])).await?;
164+
}
165+
166+
// Connection closed
167+
Some(Ok(Message::Done)) => {
168+
tracing::info!("connection closed");
169+
break;
170+
}
171+
148172
// WebSocket Error
149173
Some(Err(err)) => {
150174
tracing::error!("websocket error: {}", err);
151175
}
152176

153-
// Connection closed
154177
None => {
155-
tracing::info!("connection closed");
156-
break;
178+
// Unreachable because of the interval stream
179+
unreachable!("should never yield None");
157180
}
158181
}
159182

@@ -206,13 +229,19 @@ async fn connected(
206229
}
207230

208231
// Type to describe a message from the client conveniently.
232+
#[allow(clippy::large_enum_variant)]
209233
enum Message {
210234
// Valid LSP message
211235
Message(lsp::Message),
212236
// Invalid JSON
213237
Invalid(String),
214238
// Close message
215239
Close,
240+
// Ping the client to keep the connection alive.
241+
// Note that this is from the interval stream and not actually from client.
242+
Tick,
243+
// Client disconnected. Necessary because the combined stream is infinite.
244+
Done,
216245
}
217246

218247
// Parse the message and ignore anything we don't care.

0 commit comments

Comments
 (0)