Skip to content

Commit 4a683d2

Browse files
mmirateEugeny
authored andcommitted
Add client-sent keepalives
This is analogous to OpenSSH's ServerAliveInterval option.
1 parent 02899e9 commit 4a683d2

File tree

2 files changed

+26
-0
lines changed

2 files changed

+26
-0
lines changed

russh/src/client/mod.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,12 +767,17 @@ impl Session {
767767
let mut opening_cipher = Box::new(clear::Key) as Box<dyn OpeningKey + Send>;
768768
std::mem::swap(&mut opening_cipher, &mut self.common.cipher.remote_to_local);
769769

770+
let time_for_keepalive = tokio::time::sleep_until(self.common.config.keepalive_deadline());
770771
let reading = start_reading(stream_read, buffer, opening_cipher);
771772
pin!(reading);
773+
pin!(time_for_keepalive);
772774

773775
#[allow(clippy::panic)] // false positive in select! macro
774776
while !self.common.disconnected {
775777
tokio::select! {
778+
() = &mut time_for_keepalive => {
779+
self.send_keepalive(true);
780+
}
776781
r = &mut reading => {
777782
let (stream_read, buffer, mut opening_cipher) = match r {
778783
Ok((_, stream_read, buffer, opening_cipher)) => (stream_read, buffer, opening_cipher),
@@ -812,6 +817,7 @@ impl Session {
812817

813818
std::mem::swap(&mut opening_cipher, &mut self.common.cipher.remote_to_local);
814819
reading.set(start_reading(stream_read, buffer, opening_cipher));
820+
time_for_keepalive.as_mut().reset(self.common.config.keepalive_deadline());
815821
}
816822
msg = self.receiver.recv(), if !self.is_rekeying() => {
817823
match msg {
@@ -829,6 +835,7 @@ impl Session {
829835
Err(_) => break
830836
}
831837
}
838+
time_for_keepalive.as_mut().reset(self.common.config.keepalive_deadline());
832839
}
833840
msg = self.inbound_channel_receiver.recv(), if !self.is_rekeying() => {
834841
match msg {
@@ -1260,10 +1267,18 @@ pub struct Config {
12601267
pub preferred: negotiation::Preferred,
12611268
/// Time after which the connection is garbage-collected.
12621269
pub inactivity_timeout: Option<std::time::Duration>,
1270+
/// If nothing is sent or received for this amount of time, send a keepalive message.
1271+
pub keepalive_interval: Option<std::time::Duration>,
12631272
/// Whether to expect and wait for an authentication call.
12641273
pub anonymous: bool,
12651274
}
12661275

1276+
impl Config {
1277+
fn keepalive_deadline(&self) -> tokio::time::Instant {
1278+
tokio::time::Instant::now() + self.keepalive_interval.unwrap_or(std::time::Duration::from_secs(86400*365))
1279+
}
1280+
}
1281+
12671282
impl Default for Config {
12681283
fn default() -> Config {
12691284
Config {
@@ -1277,6 +1292,7 @@ impl Default for Config {
12771292
maximum_packet_size: 32768,
12781293
preferred: Default::default(),
12791294
inactivity_timeout: None,
1295+
keepalive_interval: None,
12801296
anonymous: false,
12811297
}
12821298
}

russh/src/client/session.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,16 @@ impl Session {
288288
}
289289
}
290290

291+
pub fn send_keepalive(&mut self, want_reply: bool) {
292+
if let Some(ref mut enc) = self.common.encrypted {
293+
push_packet!(enc.write, {
294+
enc.write.push(msg::GLOBAL_REQUEST);
295+
enc.write.extend_ssh_string(b"keepalive@libssh2.org");
296+
enc.write.push(want_reply as u8);
297+
});
298+
}
299+
}
300+
291301
pub fn data(&mut self, channel: ChannelId, data: CryptoVec) {
292302
if let Some(ref mut enc) = self.common.encrypted {
293303
enc.data(channel, data)

0 commit comments

Comments
 (0)