Fix websocket pinging behaviour
The websocket standard allows sending arbitrary pong frames at any time as a unidirectional heartbeat. With the connection's previous implementation of websocket pinging, this would result in a disconnect because the connection thought the server had missed its pong. The current implementation only results in false positives, not negatives, if the server accidentally sends an unprompted pong with the same payload as our last ping. It can be debated whether this is an actual *false* positive or just a plain positive though as the server is still alive.
This commit is contained in:
parent
47b9f5938c
commit
c385ae9d97
1 changed files with 20 additions and 11 deletions
31
src/conn.rs
31
src/conn.rs
|
|
@ -170,9 +170,13 @@ struct State {
|
||||||
|
|
||||||
packet_tx: mpsc::UnboundedSender<Data>,
|
packet_tx: mpsc::UnboundedSender<Data>,
|
||||||
|
|
||||||
// TODO An arbitrary pong frame may be sent unsolicited
|
// The server may send a pong frame with arbitrary payload unprompted at any
|
||||||
last_ws_ping: Option<u64>,
|
// time (see RFC 6455 5.5.3). Because of this, we can't just remember the
|
||||||
last_ws_pong: Option<Vec<u8>>,
|
// last pong payload.
|
||||||
|
ws_ping_counter: u64,
|
||||||
|
last_ws_ping: Option<Vec<u8>>,
|
||||||
|
last_ws_ping_replied_to: bool,
|
||||||
|
|
||||||
last_euph_ping: Option<Time>,
|
last_euph_ping: Option<Time>,
|
||||||
last_euph_pong: Option<Time>,
|
last_euph_pong: Option<Time>,
|
||||||
|
|
||||||
|
|
@ -194,8 +198,9 @@ impl State {
|
||||||
last_id: 0,
|
last_id: 0,
|
||||||
replies: Replies::new(TIMEOUT),
|
replies: Replies::new(TIMEOUT),
|
||||||
packet_tx,
|
packet_tx,
|
||||||
|
ws_ping_counter: 0,
|
||||||
last_ws_ping: None,
|
last_ws_ping: None,
|
||||||
last_ws_pong: None,
|
last_ws_ping_replied_to: false,
|
||||||
last_euph_ping: None,
|
last_euph_ping: None,
|
||||||
last_euph_pong: None,
|
last_euph_pong: None,
|
||||||
status: Status::Joining(Joining::default()),
|
status: Status::Joining(Joining::default()),
|
||||||
|
|
@ -254,7 +259,11 @@ impl State {
|
||||||
tungstenite::Message::Text(t) => self.on_packet(serde_json::from_str(&t)?, event_tx)?,
|
tungstenite::Message::Text(t) => self.on_packet(serde_json::from_str(&t)?, event_tx)?,
|
||||||
tungstenite::Message::Binary(_) => return Err("unexpected binary message".into()),
|
tungstenite::Message::Binary(_) => return Err("unexpected binary message".into()),
|
||||||
tungstenite::Message::Ping(_) => {}
|
tungstenite::Message::Ping(_) => {}
|
||||||
tungstenite::Message::Pong(p) => self.last_ws_pong = Some(p),
|
tungstenite::Message::Pong(p) => {
|
||||||
|
if self.last_ws_ping == Some(p) {
|
||||||
|
self.last_ws_ping_replied_to = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
tungstenite::Message::Close(_) => {}
|
tungstenite::Message::Close(_) => {}
|
||||||
tungstenite::Message::Frame(_) => {}
|
tungstenite::Message::Frame(_) => {}
|
||||||
}
|
}
|
||||||
|
|
@ -353,17 +362,17 @@ impl State {
|
||||||
|
|
||||||
async fn do_pings(&mut self, event_tx: &mpsc::UnboundedSender<Event>) -> InternalResult<()> {
|
async fn do_pings(&mut self, event_tx: &mpsc::UnboundedSender<Event>) -> InternalResult<()> {
|
||||||
// Check old ws ping
|
// Check old ws ping
|
||||||
let last_ws_ping_bytes = self.last_ws_ping.map(|n| n.to_be_bytes().to_vec());
|
if self.last_ws_ping.is_some() && !self.last_ws_ping_replied_to {
|
||||||
if self.last_ws_ping.is_some() && last_ws_ping_bytes != self.last_ws_pong {
|
|
||||||
return Err("server missed ws ping".into());
|
return Err("server missed ws ping".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send new ws ping
|
// Send new ws ping
|
||||||
let ws_ping = self.last_ws_ping.unwrap_or_default().wrapping_add(1);
|
let ws_payload = self.ws_ping_counter.to_be_bytes().to_vec();
|
||||||
let ws_ping_bytes = ws_ping.to_be_bytes().to_vec();
|
self.ws_ping_counter = self.ws_ping_counter.wrapping_add(1);
|
||||||
self.last_ws_ping = Some(ws_ping);
|
self.last_ws_ping = Some(ws_payload.clone());
|
||||||
|
self.last_ws_ping_replied_to = false;
|
||||||
self.ws_tx
|
self.ws_tx
|
||||||
.send(tungstenite::Message::Ping(ws_ping_bytes))
|
.send(tungstenite::Message::Ping(ws_payload))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Check old euph ping
|
// Check old euph ping
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue