Skip to content

Commit

Permalink
Library: Add compatibility for legacy Tokio 0.2 (#40)
Browse files Browse the repository at this point in the history
Adds support to the library for tokio 0.2 backward-compatibility. This should hopefully benefit, and prevent lavalink-rs from being blocked on this feature.

These can be reached using, e.g., `gateway-tokio-02`, `driver-tokio-02`, `serenity-rustls-tokio-02`, and `serenity-native-tokio-02` features.

Naturally, this requires some jiggering about with features and the underlying CI, which has been taken care of. Twilight can't be handled in this way, as their last tokio 0.2 version uses the deprecated Discord Gateway v6.
  • Loading branch information
FelixMcFelix authored Feb 4, 2021
1 parent b245309 commit aaab975
Show file tree
Hide file tree
Showing 24 changed files with 350 additions and 143 deletions.
16 changes: 11 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
uses: actions-rs/clippy-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --all-features
args: --features full-doc

test:
name: Test
Expand All @@ -39,6 +39,7 @@ jobs:
- Windows
- driver only
- gateway only
- legacy tokio

include:
- name: beta
Expand All @@ -51,8 +52,13 @@ jobs:
os: windows-latest
- name: driver only
features: driver rustls
dont-test: true
- name: gateway only
features: serenity-rustls
dont-test: true
- name: legacy tokio
features: serenity-rustls-tokio-02 driver-tokio-02
dont-test: true

steps:
- name: Checkout sources
Expand Down Expand Up @@ -84,18 +90,18 @@ jobs:

- name: Build all features
if: matrix.features == ''
run: cargo build --all-features
run: cargo build --features full-doc

- name: Test all features
if: matrix.features == ''
run: cargo test --all-features
run: cargo test --features full-doc

- name: Build some features
if: matrix.features
run: cargo build --no-default-features --features "${{ matrix.features }}"

- name: Test some features
if: matrix.features
if: ${{ !matrix.dont-test && matrix.features }}
run: cargo test --no-default-features --features "${{ matrix.features }}"

doc:
Expand Down Expand Up @@ -131,7 +137,7 @@ jobs:
env:
RUSTDOCFLAGS: -D broken_intra_doc_links
run: |
cargo doc --no-deps --all-features
cargo doc --no-deps --features full-doc
examples:
name: Examples
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
env:
RUSTDOCFLAGS: -D broken_intra_doc_links
run: |
cargo doc --no-deps --features default,twilight-rustls,builtin-queue,stock-zlib
cargo doc --no-deps --features full-doc
- name: Prepare docs
shell: bash -e -O extglob {0}
Expand Down
80 changes: 66 additions & 14 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ features = ["tokio-runtime"]
optional = true
version = "0.11"

[dependencies.async-tungstenite-compat]
package = "async-tungstenite"
default-features = false
features = ["tokio-runtime"]
optional = true
version = "0.9"

[dependencies.audiopus]
optional = true
version = "0.2"
Expand Down Expand Up @@ -62,7 +69,7 @@ version = "0.8"

[dependencies.serenity]
optional = true
version = "0.10"
version = "^0.10.2"
default-features = false
features = ["voice", "gateway"]

Expand All @@ -83,6 +90,12 @@ optional = true
version = "1.0"
default-features = false

[dependencies.tokio-compat]
optional = true
package = "tokio"
version = "0.2"
default-features = false

[dependencies.twilight-gateway]
optional = true
version = "0.3"
Expand Down Expand Up @@ -115,20 +128,35 @@ criterion = "0.3"
utils = { path = "utils" }

[features]
# Core features
default = [
"serenity-rustls",
"driver",
"gateway",
]
gateway = [
"gateway-core",
"tokio/sync",
]
gateway-core = [
"dashmap",
"flume",
"parking_lot",
"tokio/sync",
]
driver = [
"async-trait",
"async-tungstenite",
"driver-core",
"tokio/fs",
"tokio/io-util",
"tokio/macros",
"tokio/net",
"tokio/process",
"tokio/rt",
"tokio/sync",
"tokio/time",
]
driver-core = [
"async-trait",
"audiopus",
"byteorder",
"discortp",
Expand All @@ -138,21 +166,13 @@ driver = [
"serenity-voice-model",
"spin_sleep",
"streamcatcher",
"tokio/fs",
"tokio/io-util",
"tokio/macros",
"tokio/net",
"tokio/process",
"tokio/rt",
"tokio/sync",
"tokio/time",
"typemap_rev",
"url",
"uuid",
"xsalsa20poly1305",
]
rustls = ["async-tungstenite/tokio-rustls"]
native = ["async-tungstenite/tokio-native-tls"]
rustls = ["async-tungstenite/tokio-rustls", "rustls-marker"]
native = ["async-tungstenite/tokio-native-tls", "native-marker"]
serenity-rustls = ["serenity/rustls_backend", "rustls", "gateway", "serenity-deps"]
serenity-native = ["serenity/native_tls_backend", "native", "gateway", "serenity-deps"]
twilight-rustls = ["twilight", "twilight-gateway/rustls", "rustls", "gateway"]
Expand All @@ -162,9 +182,41 @@ simd-zlib = ["twilight-gateway/simd-zlib"]
stock-zlib = ["twilight-gateway/stock-zlib"]
serenity-deps = ["async-trait"]

rustls-marker = []
native-marker = []

# Tokio 0.2 Compatibility features
# These should probably be dropped around the same time as serenity drop them.
rustls-tokio-02 = ["async-tungstenite-compat/tokio-rustls", "rustls-marker", "tokio-02-marker"]
native-tokio-02 = ["async-tungstenite-compat/tokio-native-tls", "native-marker", "tokio-02-marker"]
serenity-rustls-tokio-02 = ["serenity/rustls_tokio_0_2_backend", "rustls-tokio-02", "gateway-tokio-02", "serenity-deps"]
serenity-native-tokio-02 = ["serenity/native_tls_tokio_0_2_backend", "native-tokio-02", "gateway-tokio-02", "serenity-deps"]
gateway-tokio-02 = [
"gateway-core",
"tokio-02-marker",
"tokio-compat/sync",
]
driver-tokio-02 = [
"async-tungstenite-compat",
"driver-core",
"tokio-02-marker",
"tokio-compat/fs",
"tokio-compat/io-util",
"tokio-compat/macros",
"tokio-compat/net",
"tokio-compat/process",
"tokio-compat/rt-core",
"tokio-compat/sync",
"tokio-compat/time",
]
tokio-02-marker = []

# Behaviour altering features.
youtube-dlc = []
builtin-queue = []

# Used for docgen/testing/benchmarking.
full-doc = ["default", "twilight-rustls", "builtin-queue", "stock-zlib"]
internals = []

[[bench]]
Expand All @@ -179,4 +231,4 @@ required-features = ["internals"]
harness = false

[package.metadata.docs.rs]
features = ["default", "twilight-rustls", "builtin-queue", "stock-zlib"]
features = ["full-doc"]
5 changes: 4 additions & 1 deletion build.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#[cfg(all(feature = "driver", not(any(feature = "rustls", feature = "native"))))]
#[cfg(all(
feature = "driver",
not(any(feature = "rustls-marker", feature = "native-marker"))
))]
compile_error!(
"You have the `driver` feature enabled: \
either the `rustls` or `native` feature must be
Expand Down
12 changes: 6 additions & 6 deletions src/constants.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
//! Constants affecting driver function and API handling.
#[cfg(feature = "driver")]
#[cfg(feature = "driver-core")]
use audiopus::{Bitrate, SampleRate};
#[cfg(feature = "driver")]
#[cfg(feature = "driver-core")]
use discortp::rtp::RtpType;
use std::time::Duration;

#[cfg(feature = "driver")]
#[cfg(feature = "driver-core")]
/// The voice gateway version used by the library.
pub const VOICE_GATEWAY_VERSION: u8 = crate::model::constants::GATEWAY_VERSION;

#[cfg(feature = "driver")]
#[cfg(feature = "driver-core")]
/// Sample rate of audio to be sent to Discord.
pub const SAMPLE_RATE: SampleRate = SampleRate::Hz48000;

Expand All @@ -23,7 +23,7 @@ pub const AUDIO_FRAME_RATE: usize = 50;
/// Length of time between any two audio frames.
pub const TIMESTEP_LENGTH: Duration = Duration::from_millis(1000 / AUDIO_FRAME_RATE as u64);

#[cfg(feature = "driver")]
#[cfg(feature = "driver-core")]
/// Default bitrate for audio.
pub const DEFAULT_BITRATE: Bitrate = Bitrate::BitsPerSecond(128_000);

Expand Down Expand Up @@ -70,6 +70,6 @@ pub const SILENT_FRAME: [u8; 3] = [0xf8, 0xff, 0xfe];
/// The one (and only) RTP version.
pub const RTP_VERSION: u8 = 2;

#[cfg(feature = "driver")]
#[cfg(feature = "driver-core")]
/// Profile type used by Discord's Opus audio traffic.
pub const RTP_PROFILE_TYPE: RtpType = RtpType::Dynamic(120);
39 changes: 26 additions & 13 deletions src/driver/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,18 @@ use discortp::discord::{IpDiscoveryPacket, IpDiscoveryType, MutableIpDiscoveryPa
use error::{Error, Result};
use flume::Sender;
use std::{net::IpAddr, str::FromStr, sync::Arc};
use tokio::net::UdpSocket;
#[cfg(not(feature = "tokio-02-marker"))]
use tokio::{net::UdpSocket, spawn};
#[cfg(feature = "tokio-02-marker")]
use tokio_compat::{net::UdpSocket, spawn};
use tracing::{debug, info, instrument};
use url::Url;
use xsalsa20poly1305::{aead::NewAead, XSalsa20Poly1305 as Cipher};

#[cfg(all(feature = "rustls", not(feature = "native")))]
#[cfg(all(feature = "rustls-marker", not(feature = "native-marker")))]
use ws::create_rustls_client;

#[cfg(feature = "native")]
#[cfg(feature = "native-marker")]
use ws::create_native_tls_client;

pub(crate) struct Connection {
Expand All @@ -43,10 +46,10 @@ impl Connection {
) -> Result<Connection> {
let url = generate_url(&mut info.endpoint)?;

#[cfg(all(feature = "rustls", not(feature = "native")))]
#[cfg(all(feature = "rustls-marker", not(feature = "native-marker")))]
let mut client = create_rustls_client(url).await?;

#[cfg(feature = "native")]
#[cfg(feature = "native-marker")]
let mut client = create_native_tls_client(url).await?;

let mut hello = None;
Expand Down Expand Up @@ -97,7 +100,11 @@ impl Connection {
return Err(Error::CryptoModeUnavailable);
}

#[cfg(not(feature = "tokio-02-marker"))]
let udp = UdpSocket::bind("0.0.0.0:0").await?;
#[cfg(feature = "tokio-02-marker")]
let mut udp = UdpSocket::bind("0.0.0.0:0").await?;

udp.connect((ready.ip, ready.port)).await?;

// Follow Discord's IP Discovery procedures, in case NAT tunnelling is needed.
Expand All @@ -124,7 +131,7 @@ impl Connection {
}

// We could do something clever like binary search,
// but possibility of UDP spoofing preclueds us from
// but possibility of UDP spoofing precludes us from
// making the assumption we can find a "left edge" of '\0's.
let nul_byte_index = view
.get_address_raw()
Expand Down Expand Up @@ -162,8 +169,14 @@ impl Connection {
let (udp_sender_msg_tx, udp_sender_msg_rx) = flume::unbounded();
let (udp_receiver_msg_tx, udp_receiver_msg_rx) = flume::unbounded();

let udp_rx = Arc::new(udp);
let udp_tx = Arc::clone(&udp_rx);
#[cfg(not(feature = "tokio-02-marker"))]
let (udp_rx, udp_tx) = {
let udp_rx = Arc::new(udp);
let udp_tx = Arc::clone(&udp_rx);
(udp_rx, udp_tx)
};
#[cfg(feature = "tokio-02-marker")]
let (udp_rx, udp_tx) = udp.split();

let ssrc = ready.ssrc;

Expand All @@ -182,22 +195,22 @@ impl Connection {
.mixer
.send(MixerMessage::SetConn(mix_conn, ready.ssrc))?;

tokio::spawn(ws_task::runner(
spawn(ws_task::runner(
interconnect.clone(),
ws_msg_rx,
client,
ssrc,
hello.heartbeat_interval,
));

tokio::spawn(udp_rx::runner(
spawn(udp_rx::runner(
interconnect.clone(),
udp_receiver_msg_rx,
cipher,
config.clone(),
udp_rx,
));
tokio::spawn(udp_tx::runner(udp_sender_msg_rx, ssrc, udp_tx));
spawn(udp_tx::runner(udp_sender_msg_rx, ssrc, udp_tx));

Ok(Connection {
info,
Expand All @@ -212,10 +225,10 @@ impl Connection {
// Thread may have died, we want to send to prompt a clean exit
// (if at all possible) and then proceed as normal.

#[cfg(all(feature = "rustls", not(feature = "native")))]
#[cfg(all(feature = "rustls-marker", not(feature = "native-marker")))]
let mut client = create_rustls_client(url).await?;

#[cfg(feature = "native")]
#[cfg(feature = "native-marker")]
let mut client = create_native_tls_client(url).await?;

client
Expand Down
6 changes: 5 additions & 1 deletion src/driver/tasks/message/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ mod ws;
pub use self::{core::*, disposal::*, events::*, mixer::*, udp_rx::*, udp_tx::*, ws::*};

use flume::Sender;
#[cfg(not(feature = "tokio-02-marker"))]
use tokio::spawn;
#[cfg(feature = "tokio-02-marker")]
use tokio_compat::spawn;
use tracing::info;

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -38,7 +42,7 @@ impl Interconnect {
self.events = evt_tx;

let ic = self.clone();
tokio::spawn(async move {
spawn(async move {
info!("Event processor restarted.");
super::events::runner(ic, evt_rx).await;
info!("Event processor finished.");
Expand Down
Loading

0 comments on commit aaab975

Please sign in to comment.