2024-01-30 17:19:34 +01:00
|
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
|
2023-11-19 19:12:29 +01:00
|
|
|
use futures_util::SinkExt;
|
|
|
|
use log::*;
|
|
|
|
|
2024-01-19 14:55:23 +01:00
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
2024-01-19 15:48:59 +01:00
|
|
|
use tokio::time::Instant;
|
2024-01-19 14:55:23 +01:00
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
|
|
use wasmtimer::std::Instant;
|
|
|
|
|
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
2024-01-19 15:48:59 +01:00
|
|
|
use tokio::time::sleep_until;
|
2024-01-19 14:55:23 +01:00
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
|
|
use wasmtimer::tokio::sleep_until;
|
|
|
|
|
|
|
|
use std::time::Duration;
|
|
|
|
|
|
|
|
use tokio::sync::mpsc::{Receiver, Sender};
|
|
|
|
|
2023-11-22 14:23:33 +01:00
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
|
|
use tokio::task;
|
2023-11-19 19:12:29 +01:00
|
|
|
|
2023-11-14 11:13:02 +01:00
|
|
|
use super::*;
|
2023-11-19 17:08:53 +01:00
|
|
|
use crate::types;
|
2023-11-14 11:13:02 +01:00
|
|
|
|
2023-11-14 15:43:08 +01:00
|
|
|
/// The amount of time we wait for a heartbeat ack before resending our heartbeat in ms
|
Primitive voice implementation (feature/voice) (#457)
* Add Webrtc Identify & Ready
* Add more webrtc typings
* Attempt an untested voice gateway implementation
* fmt
* Merge with main
* Same allow as for voice as normal gateway
* Test error observer
* Minor updates
* More derives
* Even more derives
* Small types update
* e
* Minor doc fixes
* Modernise voice gateway
* Add default impl for voicegatewayerror
* Make voice event fields pub
* Event updates via the scientific method
* ??
* Fix bad request in voice gateway init
* Voice gateway updates
* Fix error failing to 'deserialize' properly
* Update voice identify
* Clarify FIXME related to #430
* Update to v7
* Create seperate voice_gateway.rs and voice_udp.rs
* Restructure voice to new module
* fix: deserialization error in speaking bitflags
* feat: kinda janky ip discovery impl
* feat: return ip discovery data + minor update
* feat: packet parsing!
* fix: voice works again
* feat: add voice_media_sink_wants
(comitting uncommited changes to merge)
* chore: rename events/webrtc to events/voice_gateway
* Add UdpHandle
* chore: clippy + other misc updates
* fix: attempt to fix failing wasm build
* chore: yes clippy, that is indeed an unneeded return statement
* feat: add VoiceData struct
* feat: add VoiceData reference to UdpHandler
* feat: decryption?
* chore: formatting
* feat: add ssrc definition (op 12)
* feat: add untested sending & asbtract nonce generation
* feat: Public api! (sorta)
* small updates
* feat: add sequence number
* chore: yes
* feat: merge VoiceHandler into official development
* chore: yes clippy, you are special
* fix: duplicated gateway events
* feat: first try at vgw wasm compat
* fix: blunder
* fix: gateway connect using wrong url
* fix: properly using encrypted data, bad practice for buffer creation
* chore: split voice udp
* feat: udp error handling, create udp/backends
* fix: its the same
* chore: clarify UDP on WASM
* api: split voice gateway and udp features, test for voice gateway in WASM
* feat: new encryption modes, minor code quality
* docs: document voice encryption modes
* chore: unused imports
* chore: update getrandom version to match wasm version
* chore: update on packet size FIXME
* drop buf asap
* Okay can't do that actually
* tests: add nonce test
* normal tests work?
* docs: fix doc warning, fix incorrect refrences to 'webrtc'
* chore: json isn't a doc test
* tests: better gateway auth test
* testing tests
* update voice heartbeat, fix the new test issue
* committed too much
* fix: unused import
* fix: use ip discovery address as string, not as Vec<u8>
* chore: less obnoxious logging
* chore: better unimplemented voice modes handling
* chore: remove unused variable
* chore: use matches macro
* add voice examples, make gateway ones clearer
* rename voice example
* chore: remove unused VoiceHandler
* fix: implement gateway Reconnect and InvalidSession
* Typo
Co-authored-by: Flori <39242991+bitfl0wer@users.noreply.github.com>
* Fix a bunch of typos
Co-authored-by: Flori <39242991+bitfl0wer@users.noreply.github.com>
* fix: error handling while loading native certs
* fix: guh
* use be for nonce bytes
* fix: refactor gw and vgw closures
* remove outdated docs
---------
Co-authored-by: Flori <39242991+bitfl0wer@users.noreply.github.com>
2024-04-16 17:18:21 +02:00
|
|
|
pub const HEARTBEAT_ACK_TIMEOUT: u64 = 2000;
|
2023-11-14 15:43:08 +01:00
|
|
|
|
2023-11-14 11:13:02 +01:00
|
|
|
/// Handles sending heartbeats to the gateway in another thread
|
|
|
|
#[allow(dead_code)] // FIXME: Remove this, once HeartbeatHandler is used
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub(super) struct HeartbeatHandler {
|
|
|
|
/// How ofter heartbeats need to be sent at a minimum
|
|
|
|
pub heartbeat_interval: Duration,
|
|
|
|
/// The send channel for the heartbeat thread
|
|
|
|
pub send: Sender<HeartbeatThreadCommunication>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl HeartbeatHandler {
|
|
|
|
pub fn new(
|
|
|
|
heartbeat_interval: Duration,
|
2023-11-19 21:15:10 +01:00
|
|
|
websocket_tx: Arc<Mutex<Sink>>,
|
2023-11-14 11:13:02 +01:00
|
|
|
kill_rc: tokio::sync::broadcast::Receiver<()>,
|
2023-11-19 17:08:53 +01:00
|
|
|
) -> Self {
|
2023-11-14 11:13:02 +01:00
|
|
|
let (send, receive) = tokio::sync::mpsc::channel(32);
|
|
|
|
let kill_receive = kill_rc.resubscribe();
|
|
|
|
|
2023-11-22 14:23:33 +01:00
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
|
|
task::spawn(async move {
|
|
|
|
Self::heartbeat_task(websocket_tx, heartbeat_interval, receive, kill_receive).await;
|
|
|
|
});
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
|
|
wasm_bindgen_futures::spawn_local(async move {
|
2023-11-19 17:08:53 +01:00
|
|
|
Self::heartbeat_task(websocket_tx, heartbeat_interval, receive, kill_receive).await;
|
2023-11-14 11:13:02 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
Self {
|
|
|
|
heartbeat_interval,
|
|
|
|
send,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The main heartbeat task;
|
|
|
|
///
|
|
|
|
/// Can be killed by the kill broadcast;
|
|
|
|
/// If the websocket is closed, will die out next time it tries to send a heartbeat;
|
|
|
|
pub async fn heartbeat_task(
|
2023-11-19 21:15:10 +01:00
|
|
|
websocket_tx: Arc<Mutex<Sink>>,
|
2023-11-14 11:13:02 +01:00
|
|
|
heartbeat_interval: Duration,
|
2023-11-19 19:12:29 +01:00
|
|
|
mut receive: Receiver<HeartbeatThreadCommunication>,
|
2023-11-14 11:13:02 +01:00
|
|
|
mut kill_receive: tokio::sync::broadcast::Receiver<()>,
|
|
|
|
) {
|
2024-01-19 14:55:23 +01:00
|
|
|
let mut last_heartbeat_timestamp: Instant = Instant::now();
|
2023-11-14 11:13:02 +01:00
|
|
|
let mut last_heartbeat_acknowledged = true;
|
|
|
|
let mut last_seq_number: Option<u64> = None;
|
2024-01-21 17:07:30 +01:00
|
|
|
|
2023-11-14 11:13:02 +01:00
|
|
|
loop {
|
|
|
|
let timeout = if last_heartbeat_acknowledged {
|
|
|
|
heartbeat_interval
|
|
|
|
} else {
|
|
|
|
// If the server hasn't acknowledged our heartbeat we should resend it
|
|
|
|
Duration::from_millis(HEARTBEAT_ACK_TIMEOUT)
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut should_send = false;
|
|
|
|
|
|
|
|
tokio::select! {
|
|
|
|
() = sleep_until(last_heartbeat_timestamp + timeout) => {
|
|
|
|
should_send = true;
|
|
|
|
}
|
|
|
|
Some(communication) = receive.recv() => {
|
|
|
|
// If we received a seq number update, use that as the last seq number
|
|
|
|
if communication.sequence_number.is_some() {
|
|
|
|
last_seq_number = communication.sequence_number;
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(op_code) = communication.op_code {
|
|
|
|
match op_code {
|
|
|
|
GATEWAY_HEARTBEAT => {
|
|
|
|
// As per the api docs, if the server sends us a Heartbeat, that means we need to respond with a heartbeat immediately
|
|
|
|
should_send = true;
|
|
|
|
}
|
|
|
|
GATEWAY_HEARTBEAT_ACK => {
|
|
|
|
// The server received our heartbeat
|
|
|
|
last_heartbeat_acknowledged = true;
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
Primitive voice implementation (feature/voice) (#457)
* Add Webrtc Identify & Ready
* Add more webrtc typings
* Attempt an untested voice gateway implementation
* fmt
* Merge with main
* Same allow as for voice as normal gateway
* Test error observer
* Minor updates
* More derives
* Even more derives
* Small types update
* e
* Minor doc fixes
* Modernise voice gateway
* Add default impl for voicegatewayerror
* Make voice event fields pub
* Event updates via the scientific method
* ??
* Fix bad request in voice gateway init
* Voice gateway updates
* Fix error failing to 'deserialize' properly
* Update voice identify
* Clarify FIXME related to #430
* Update to v7
* Create seperate voice_gateway.rs and voice_udp.rs
* Restructure voice to new module
* fix: deserialization error in speaking bitflags
* feat: kinda janky ip discovery impl
* feat: return ip discovery data + minor update
* feat: packet parsing!
* fix: voice works again
* feat: add voice_media_sink_wants
(comitting uncommited changes to merge)
* chore: rename events/webrtc to events/voice_gateway
* Add UdpHandle
* chore: clippy + other misc updates
* fix: attempt to fix failing wasm build
* chore: yes clippy, that is indeed an unneeded return statement
* feat: add VoiceData struct
* feat: add VoiceData reference to UdpHandler
* feat: decryption?
* chore: formatting
* feat: add ssrc definition (op 12)
* feat: add untested sending & asbtract nonce generation
* feat: Public api! (sorta)
* small updates
* feat: add sequence number
* chore: yes
* feat: merge VoiceHandler into official development
* chore: yes clippy, you are special
* fix: duplicated gateway events
* feat: first try at vgw wasm compat
* fix: blunder
* fix: gateway connect using wrong url
* fix: properly using encrypted data, bad practice for buffer creation
* chore: split voice udp
* feat: udp error handling, create udp/backends
* fix: its the same
* chore: clarify UDP on WASM
* api: split voice gateway and udp features, test for voice gateway in WASM
* feat: new encryption modes, minor code quality
* docs: document voice encryption modes
* chore: unused imports
* chore: update getrandom version to match wasm version
* chore: update on packet size FIXME
* drop buf asap
* Okay can't do that actually
* tests: add nonce test
* normal tests work?
* docs: fix doc warning, fix incorrect refrences to 'webrtc'
* chore: json isn't a doc test
* tests: better gateway auth test
* testing tests
* update voice heartbeat, fix the new test issue
* committed too much
* fix: unused import
* fix: use ip discovery address as string, not as Vec<u8>
* chore: less obnoxious logging
* chore: better unimplemented voice modes handling
* chore: remove unused variable
* chore: use matches macro
* add voice examples, make gateway ones clearer
* rename voice example
* chore: remove unused VoiceHandler
* fix: implement gateway Reconnect and InvalidSession
* Typo
Co-authored-by: Flori <39242991+bitfl0wer@users.noreply.github.com>
* Fix a bunch of typos
Co-authored-by: Flori <39242991+bitfl0wer@users.noreply.github.com>
* fix: error handling while loading native certs
* fix: guh
* use be for nonce bytes
* fix: refactor gw and vgw closures
* remove outdated docs
---------
Co-authored-by: Flori <39242991+bitfl0wer@users.noreply.github.com>
2024-04-16 17:18:21 +02:00
|
|
|
Ok(_) = kill_receive.recv() => {
|
|
|
|
log::trace!("GW: Closing heartbeat task");
|
|
|
|
break;
|
|
|
|
}
|
2023-11-14 11:13:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if should_send {
|
|
|
|
trace!("GW: Sending Heartbeat..");
|
|
|
|
|
|
|
|
let heartbeat = types::GatewayHeartbeat {
|
|
|
|
op: GATEWAY_HEARTBEAT,
|
|
|
|
d: last_seq_number,
|
|
|
|
};
|
|
|
|
|
|
|
|
let heartbeat_json = serde_json::to_string(&heartbeat).unwrap();
|
|
|
|
|
2023-11-19 17:08:53 +01:00
|
|
|
let msg = GatewayMessage(heartbeat_json);
|
2023-11-14 11:13:02 +01:00
|
|
|
|
2023-11-19 17:08:53 +01:00
|
|
|
let send_result = websocket_tx.lock().await.send(msg.into()).await;
|
2023-11-14 11:13:02 +01:00
|
|
|
if send_result.is_err() {
|
|
|
|
// We couldn't send, the websocket is broken
|
2024-01-31 22:27:53 +01:00
|
|
|
warn!("GW: Couldn't send heartbeat, websocket seems broken");
|
2023-11-14 11:13:02 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2024-01-19 14:55:23 +01:00
|
|
|
last_heartbeat_timestamp = Instant::now();
|
2023-11-14 11:13:02 +01:00
|
|
|
last_heartbeat_acknowledged = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Used for communications between the heartbeat and gateway thread.
|
|
|
|
/// Either signifies a sequence number update, a heartbeat ACK or a Heartbeat request by the server
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
|
|
pub(super) struct HeartbeatThreadCommunication {
|
|
|
|
/// The opcode for the communication we received, if relevant
|
|
|
|
pub(super) op_code: Option<u8>,
|
|
|
|
/// The sequence number we got from discord, if any
|
|
|
|
pub(super) sequence_number: Option<u64>,
|
|
|
|
}
|