Compare commits

...
6 Commits
14 changed files with 316 additions and 70 deletions

No files matched your search

Generated
+73 -27
View File
@@ -138,11 +138,23 @@ version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319"
dependencies = [ dependencies = [
"concurrent-queue", "concurrent-queue 1.2.2",
"event-listener", "event-listener 2.5.2",
"futures-core", "futures-core",
] ]
[[package]]
name = "async-channel"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a"
dependencies = [
"concurrent-queue 2.5.0",
"event-listener-strategy",
"futures-core",
"pin-project-lite 0.2.13",
]
[[package]] [[package]]
name = "async-executor" name = "async-executor"
version = "1.4.1" version = "1.4.1"
@@ -150,7 +162,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965"
dependencies = [ dependencies = [
"async-task", "async-task",
"concurrent-queue", "concurrent-queue 1.2.2",
"fastrand", "fastrand",
"futures-lite", "futures-lite",
"once_cell", "once_cell",
@@ -163,7 +175,7 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd8b508d585e01084059b60f06ade4cb7415cd2e4084b71dd1cb44e7d3fb9880" checksum = "fd8b508d585e01084059b60f06ade4cb7415cd2e4084b71dd1cb44e7d3fb9880"
dependencies = [ dependencies = [
"async-channel", "async-channel 1.6.1",
"async-executor", "async-executor",
"async-io", "async-io",
"async-lock", "async-lock",
@@ -180,7 +192,7 @@ version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5e18f61464ae81cde0a23e713ae8fd299580c54d697a35820cfd0625b8b0e07" checksum = "e5e18f61464ae81cde0a23e713ae8fd299580c54d697a35820cfd0625b8b0e07"
dependencies = [ dependencies = [
"concurrent-queue", "concurrent-queue 1.2.2",
"futures-lite", "futures-lite",
"libc", "libc",
"log", "log",
@@ -199,7 +211,7 @@ version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b"
dependencies = [ dependencies = [
"event-listener", "event-listener 2.5.2",
] ]
[[package]] [[package]]
@@ -211,7 +223,7 @@ dependencies = [
"async-io", "async-io",
"blocking", "blocking",
"cfg-if", "cfg-if",
"event-listener", "event-listener 2.5.2",
"futures-lite", "futures-lite",
"libc", "libc",
"once_cell", "once_cell",
@@ -237,7 +249,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d"
dependencies = [ dependencies = [
"async-attributes", "async-attributes",
"async-channel", "async-channel 1.6.1",
"async-global-executor", "async-global-executor",
"async-io", "async-io",
"async-lock", "async-lock",
@@ -531,7 +543,7 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc" checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc"
dependencies = [ dependencies = [
"async-channel", "async-channel 1.6.1",
"async-task", "async-task",
"atomic-waker", "atomic-waker",
"fastrand", "fastrand",
@@ -729,6 +741,15 @@ dependencies = [
"cache-padded", "cache-padded",
] ]
[[package]]
name = "concurrent-queue"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
dependencies = [
"crossbeam-utils",
]
[[package]] [[package]]
name = "config" name = "config"
version = "0.13.3" version = "0.13.3"
@@ -833,13 +854,9 @@ dependencies = [
[[package]] [[package]]
name = "crossbeam-utils" name = "crossbeam-utils"
version = "0.8.8" version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
dependencies = [
"cfg-if",
"lazy_static",
]
[[package]] [[package]]
name = "crypto-bigint" name = "crypto-bigint"
@@ -1295,6 +1312,27 @@ version = "2.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71"
[[package]]
name = "event-listener"
version = "5.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba"
dependencies = [
"concurrent-queue 2.5.0",
"parking",
"pin-project-lite 0.2.13",
]
[[package]]
name = "event-listener-strategy"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1"
dependencies = [
"event-listener 5.3.1",
"pin-project-lite 0.2.13",
]
[[package]] [[package]]
name = "fake-simd" name = "fake-simd"
version = "0.1.2" version = "0.1.2"
@@ -1976,13 +2014,13 @@ version = "1.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "334e04b4d781f436dc315cb1e7515bd96826426345d498149e4bde36b67f8ee9" checksum = "334e04b4d781f436dc315cb1e7515bd96826426345d498149e4bde36b67f8ee9"
dependencies = [ dependencies = [
"async-channel", "async-channel 1.6.1",
"castaway", "castaway",
"crossbeam-utils", "crossbeam-utils",
"curl", "curl",
"curl-sys", "curl-sys",
"encoding_rs", "encoding_rs",
"event-listener", "event-listener 2.5.2",
"futures-lite", "futures-lite",
"http", "http",
"log", "log",
@@ -2266,6 +2304,12 @@ dependencies = [
"linked-hash-map", "linked-hash-map",
] ]
[[package]]
name = "lru_time_cache"
version = "0.11.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9106e1d747ffd48e6be5bb2d97fa706ed25b144fbee4d5c02eae110cd8d6badd"
[[package]] [[package]]
name = "maplit" name = "maplit"
version = "1.0.2" version = "1.0.2"
@@ -3408,8 +3452,9 @@ dependencies = [
[[package]] [[package]]
name = "revolt-bonfire" name = "revolt-bonfire"
version = "0.7.5" version = "0.7.7"
dependencies = [ dependencies = [
"async-channel 2.3.1",
"async-std", "async-std",
"async-tungstenite", "async-tungstenite",
"authifier", "authifier",
@@ -3418,6 +3463,7 @@ dependencies = [
"futures", "futures",
"log", "log",
"lru 0.7.6", "lru 0.7.6",
"lru_time_cache",
"once_cell", "once_cell",
"querystring", "querystring",
"redis-kiss", "redis-kiss",
@@ -3436,7 +3482,7 @@ dependencies = [
[[package]] [[package]]
name = "revolt-config" name = "revolt-config"
version = "0.7.5" version = "0.7.7"
dependencies = [ dependencies = [
"async-std", "async-std",
"cached", "cached",
@@ -3452,7 +3498,7 @@ dependencies = [
[[package]] [[package]]
name = "revolt-database" name = "revolt-database"
version = "0.7.5" version = "0.7.7"
dependencies = [ dependencies = [
"async-lock", "async-lock",
"async-recursion", "async-recursion",
@@ -3498,9 +3544,9 @@ dependencies = [
[[package]] [[package]]
name = "revolt-delta" name = "revolt-delta"
version = "0.7.5" version = "0.7.7"
dependencies = [ dependencies = [
"async-channel", "async-channel 1.6.1",
"async-std", "async-std",
"authifier", "authifier",
"bitfield", "bitfield",
@@ -3544,7 +3590,7 @@ dependencies = [
[[package]] [[package]]
name = "revolt-models" name = "revolt-models"
version = "0.7.5" version = "0.7.7"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"iso8601-timestamp 0.2.11", "iso8601-timestamp 0.2.11",
@@ -3561,7 +3607,7 @@ dependencies = [
[[package]] [[package]]
name = "revolt-permissions" name = "revolt-permissions"
version = "0.7.5" version = "0.7.7"
dependencies = [ dependencies = [
"async-std", "async-std",
"async-trait", "async-trait",
@@ -3576,7 +3622,7 @@ dependencies = [
[[package]] [[package]]
name = "revolt-presence" name = "revolt-presence"
version = "0.7.5" version = "0.7.7"
dependencies = [ dependencies = [
"async-std", "async-std",
"log", "log",
@@ -3587,7 +3633,7 @@ dependencies = [
[[package]] [[package]]
name = "revolt-result" name = "revolt-result"
version = "0.7.5" version = "0.7.7"
dependencies = [ dependencies = [
"revolt_okapi", "revolt_okapi",
"revolt_rocket_okapi", "revolt_rocket_okapi",
@@ -4362,7 +4408,7 @@ version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5" checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5"
dependencies = [ dependencies = [
"async-channel", "async-channel 1.6.1",
"futures-core", "futures-core",
"futures-io", "futures-io",
] ]
+4 -2
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-bonfire" name = "revolt-bonfire"
version = "0.7.5" version = "0.7.7"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"
edition = "2021" edition = "2021"
@@ -14,6 +14,8 @@ lru = "0.7.6"
ulid = "0.5.0" ulid = "0.5.0"
once_cell = "1.9.0" once_cell = "1.9.0"
redis-kiss = "0.1.4" redis-kiss = "0.1.4"
lru_time_cache = "0.11.11"
async-channel = "2.3.1"
# parsing # parsing
querystring = "1.1.0" querystring = "1.1.0"
@@ -39,7 +41,7 @@ revolt-result = { path = "../core/result" }
revolt-models = { path = "../core/models" } revolt-models = { path = "../core/models" }
revolt-config = { path = "../core/config" } revolt-config = { path = "../core/config" }
revolt-database = { path = "../core/database" } revolt-database = { path = "../core/database" }
revolt-permissions = { version = "0.7.5", path = "../core/permissions" } revolt-permissions = { version = "0.7.7", path = "../core/permissions" }
revolt-presence = { path = "../core/presence", features = ["redis-is-patched"] } revolt-presence = { path = "../core/presence", features = ["redis-is-patched"] }
# redis # redis
+10
View File
@@ -92,6 +92,7 @@ impl State {
/// Generate a Ready packet for the current user /// Generate a Ready packet for the current user
pub async fn generate_ready_payload(&mut self, db: &Database) -> Result<EventV1> { pub async fn generate_ready_payload(&mut self, db: &Database) -> Result<EventV1> {
let user = self.clone_user(); let user = self.clone_user();
self.cache.is_bot = user.bot.is_some();
// Find all relationships to the user. // Find all relationships to the user.
let mut user_ids: HashSet<String> = user let mut user_ids: HashSet<String> = user
@@ -191,6 +192,10 @@ impl State {
for server in &servers { for server in &servers {
self.insert_subscription(server.id.clone()).await; self.insert_subscription(server.id.clone()).await;
if self.cache.is_bot {
self.insert_subscription(format!("{}u", server.id)).await;
}
} }
for channel in &channels { for channel in &channels {
@@ -397,6 +402,11 @@ impl State {
emojis: _, emojis: _,
} => { } => {
self.insert_subscription(id.clone()).await; self.insert_subscription(id.clone()).await;
if self.cache.is_bot {
self.insert_subscription(format!("{}u", id)).await;
}
self.cache.servers.insert(id.clone(), server.clone().into()); self.cache.servers.insert(id.clone(), server.clone().into());
let member = Member { let member = Member {
id: MemberCompositeKey { id: MemberCompositeKey {
+44 -1
View File
@@ -1,10 +1,12 @@
use std::{ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
sync::Arc, sync::Arc,
time::Duration,
}; };
use async_std::sync::RwLock; use async_std::sync::{Mutex, RwLock};
use lru::LruCache; use lru::LruCache;
use lru_time_cache::{LruCache as LruTimeCache, TimedEntry};
use revolt_database::{Channel, Member, Server, User}; use revolt_database::{Channel, Member, Server, User};
/// Enumeration representing some change in subscriptions /// Enumeration representing some change in subscriptions
@@ -34,6 +36,7 @@ pub enum SubscriptionStateChange {
#[derive(Debug)] #[derive(Debug)]
pub struct Cache { pub struct Cache {
pub user_id: String, pub user_id: String,
pub is_bot: bool,
pub users: HashMap<String, User>, pub users: HashMap<String, User>,
pub channels: HashMap<String, Channel>, pub channels: HashMap<String, Channel>,
@@ -47,6 +50,7 @@ impl Default for Cache {
fn default() -> Self { fn default() -> Self {
Cache { Cache {
user_id: Default::default(), user_id: Default::default(),
is_bot: false,
users: Default::default(), users: Default::default(),
channels: Default::default(), channels: Default::default(),
@@ -65,7 +69,9 @@ pub struct State {
pub session_id: String, pub session_id: String,
pub private_topic: String, pub private_topic: String,
pub state: SubscriptionStateChange, pub state: SubscriptionStateChange,
pub subscribed: Arc<RwLock<HashSet<String>>>, pub subscribed: Arc<RwLock<HashSet<String>>>,
pub active_servers: Arc<Mutex<LruTimeCache<String, ()>>>,
} }
impl State { impl State {
@@ -86,6 +92,10 @@ impl State {
State { State {
cache, cache,
subscribed: Arc::new(RwLock::new(subscribed)), subscribed: Arc::new(RwLock::new(subscribed)),
active_servers: Arc::new(Mutex::new(LruTimeCache::with_expiry_duration_and_capacity(
Duration::from_secs(900),
5,
))),
session_id, session_id,
private_topic, private_topic,
state: SubscriptionStateChange::Reset, state: SubscriptionStateChange::Reset,
@@ -94,6 +104,39 @@ impl State {
/// Apply currently queued state /// Apply currently queued state
pub async fn apply_state(&mut self) -> SubscriptionStateChange { pub async fn apply_state(&mut self) -> SubscriptionStateChange {
// Check if we need to change subscriptions to member event topics
if !self.cache.is_bot {
enum Server {
Subscribe(String),
Unsubscribe(String),
}
let active_server_changes: Vec<Server> = {
let mut active_servers = self.active_servers.lock().await;
active_servers
.notify_iter()
.map(|e| match e {
TimedEntry::Valid(k, _) => Server::Subscribe(format!("{}u", k)),
TimedEntry::Expired(k, _) => Server::Unsubscribe(format!("{}u", k)),
})
.collect()
// It is bad practice to open more than one Mutex at once and could
// lead to a deadlock, so instead we choose to collect the changes.
};
for entry in active_server_changes {
match entry {
Server::Subscribe(k) => {
self.insert_subscription(k).await;
}
Server::Unsubscribe(k) => {
self.remove_subscription(&k).await;
}
}
}
}
// Flush changes to subscriptions
let state = std::mem::replace(&mut self.state, SubscriptionStateChange::None); let state = std::mem::replace(&mut self.state, SubscriptionStateChange::None);
let mut subscribed = self.subscribed.write().await; let mut subscribed = self.subscribed.write().await;
if let SubscriptionStateChange::Change { add, remove } = &state { if let SubscriptionStateChange::Change { add, remove } = &state {
+163 -24
View File
@@ -3,12 +3,13 @@ use std::{collections::HashSet, net::SocketAddr, sync::Arc};
use async_tungstenite::WebSocketStream; use async_tungstenite::WebSocketStream;
use authifier::AuthifierEvent; use authifier::AuthifierEvent;
use fred::{ use fred::{
error::{RedisError, RedisErrorKind},
interfaces::{ClientLike, EventInterface, PubsubInterface}, interfaces::{ClientLike, EventInterface, PubsubInterface},
types::RedisConfig, types::RedisConfig,
}; };
use futures::{ use futures::{
channel::oneshot, channel::oneshot,
pin_mut, select, join, pin_mut, select,
stream::{SplitSink, SplitStream}, stream::{SplitSink, SplitStream},
FutureExt, SinkExt, StreamExt, TryStreamExt, FutureExt, SinkExt, StreamExt, TryStreamExt,
}; };
@@ -22,6 +23,7 @@ use revolt_presence::{create_session, delete_session};
use async_std::{ use async_std::{
net::TcpStream, net::TcpStream,
sync::{Mutex, RwLock}, sync::{Mutex, RwLock},
task::spawn,
}; };
use revolt_result::create_error; use revolt_result::create_error;
use sentry::Level; use sentry::Level;
@@ -127,22 +129,44 @@ pub async fn client(db: &'static Database, stream: TcpStream, addr: SocketAddr)
} }
{ {
// Setup channels and mutexes
let write = Mutex::new(write); let write = Mutex::new(write);
let subscribed = state.subscribed.clone(); let subscribed = state.subscribed.clone();
let active_servers = state.active_servers.clone();
let (topic_signal_s, topic_signal_r) = async_channel::unbounded();
// TODO: this needs to be rewritten
// Create channels through which the tasks can signal to each other they need to clean up
let (kill_signal_1_s, kill_signal_1_r) = async_channel::bounded(1);
let (kill_signal_2_s, kill_signal_2_r) = async_channel::bounded(1);
// Create a PubSub connection to poll on. // Create a PubSub connection to poll on.
let listener = listener(db, &mut state, addr, &config, &write).fuse(); let listener = listener_with_kill_signal(
// Read from WebSocket stream. db,
let worker = worker(addr, subscribed, user_id.clone(), &config, read, &write).fuse(); &mut state,
addr,
// Pin both tasks. &config,
pin_mut!(listener, worker); topic_signal_r,
kill_signal_1_r,
// Wait for either disconnect or for listener to die. &write,
select!( kill_signal_2_s,
() = listener => {},
() = worker => {}
); );
// Read from WebSocket stream.
let worker = worker_with_kill_signal(
addr,
subscribed,
active_servers,
user_id.clone(),
&config,
topic_signal_s,
kill_signal_2_r,
read,
&write,
kill_signal_1_s,
);
join!(listener, worker);
} }
// Clean up presence session. // Clean up presence session.
let last_session = delete_session(&user_id, session_id).await; let last_session = delete_session(&user_id, session_id).await;
@@ -153,15 +177,40 @@ pub async fn client(db: &'static Database, stream: TcpStream, addr: SocketAddr)
} }
} }
#[allow(clippy::too_many_arguments)]
async fn listener_with_kill_signal(
db: &'static Database,
state: &mut State,
addr: SocketAddr,
config: &ProtocolConfiguration,
topic_signal_r: async_channel::Receiver<()>,
kill_signal_r: async_channel::Receiver<()>,
write: &Mutex<WsWriter>,
kill_signal_s: async_channel::Sender<()>,
) {
listener(
db,
state,
addr,
config,
topic_signal_r,
kill_signal_r,
write,
)
.await;
kill_signal_s.send(()).await.ok();
}
async fn listener( async fn listener(
db: &'static Database, db: &'static Database,
state: &mut State, state: &mut State,
addr: SocketAddr, addr: SocketAddr,
config: &ProtocolConfiguration, config: &ProtocolConfiguration,
topic_signal_r: async_channel::Receiver<()>,
kill_signal_r: async_channel::Receiver<()>,
write: &Mutex<WsWriter>, write: &Mutex<WsWriter>,
) { ) {
let redis_config = RedisConfig::from_url(&REDIS_URI).unwrap(); let redis_config = RedisConfig::from_url(&REDIS_URI).unwrap();
let subscriber = match fred::types::Builder::from_config(redis_config).build_subscriber_client() let subscriber = match fred::types::Builder::from_config(redis_config).build_subscriber_client()
{ {
Ok(subscriber) => subscriber, Ok(subscriber) => subscriber,
@@ -178,15 +227,29 @@ async fn listener(
return; return;
}; };
// Handle Redis connection dropping
let (clean_up_s, clean_up_r) = async_channel::bounded(1);
let clean_up_s = Arc::new(Mutex::new(clean_up_s));
subscriber.on_error(move |err| {
if let RedisErrorKind::Canceled = err.kind() {
let clean_up_s = clean_up_s.clone();
spawn(async move {
clean_up_s.lock().await.send(()).await.ok();
});
}
Ok(())
});
let mut message_rx = subscriber.message_rx(); let mut message_rx = subscriber.message_rx();
loop { 'out: loop {
// Check for state changes for subscriptions. // Check for state changes for subscriptions.
match state.apply_state().await { match state.apply_state().await {
SubscriptionStateChange::Reset => { SubscriptionStateChange::Reset => {
if let Err(err) = subscriber.unsubscribe_all().await { if let Err(err) = subscriber.unsubscribe_all().await {
error!("Unsubscribe all failed: {err:?}"); error!("Unsubscribe all failed: {err:?}");
sentry::capture_error(&err); sentry::capture_error(&err);
return; break 'out;
} }
let subscribed = state.subscribed.read().await; let subscribed = state.subscribed.read().await;
@@ -194,7 +257,7 @@ async fn listener(
if let Err(err) = subscriber.subscribe(id).await { if let Err(err) = subscriber.subscribe(id).await {
error!("Subscribe failed: {err:?}"); error!("Subscribe failed: {err:?}");
sentry::capture_error(&err); sentry::capture_error(&err);
return; break 'out;
} }
} }
@@ -209,7 +272,7 @@ async fn listener(
if let Err(err) = subscriber.unsubscribe(id).await { if let Err(err) = subscriber.unsubscribe(id).await {
error!("Unsubscribe failed: {err:?}"); error!("Unsubscribe failed: {err:?}");
sentry::capture_error(&err); sentry::capture_error(&err);
return; break 'out;
} }
} }
@@ -220,20 +283,36 @@ async fn listener(
if let Err(err) = subscriber.subscribe(id).await { if let Err(err) = subscriber.subscribe(id).await {
error!("Subscribe failed: {err:?}"); error!("Subscribe failed: {err:?}");
sentry::capture_error(&err); sentry::capture_error(&err);
return; break 'out;
} }
} }
} }
SubscriptionStateChange::None => {} SubscriptionStateChange::None => {}
} }
let t1 = message_rx.recv().fuse();
let t2 = topic_signal_r.recv().fuse();
let t3 = kill_signal_r.recv().fuse();
let t4 = clean_up_r.recv().fuse();
pin_mut!(t1, t2, t3, t4);
select! {
_ = t4 => {
break 'out;
},
_ = t3 => {
break 'out;
},
_ = t2 => {},
message = t1 => {
// Handle incoming events. // Handle incoming events.
let message = match message_rx.recv().await { let message = match message {
Ok(message) => message, Ok(message) => message,
Err(e) => { Err(e) => {
error!("Error while consuming pub/sub messages: {e:?}"); error!("Error while consuming pub/sub messages: {e:?}");
sentry::capture_error(&e); sentry::capture_error(&e);
return; break 'out;
} }
}; };
@@ -264,7 +343,7 @@ async fn listener(
error!("{}", err); error!("{}", err);
sentry::capture_message(&err, Level::Error); sentry::capture_message(&err, Level::Error);
return; break 'out;
}; };
if let EventV1::Auth(auth) = &event { if let EventV1::Auth(auth) = &event {
@@ -300,26 +379,74 @@ async fn listener(
sentry::capture_message(&err, Level::Warning); sentry::capture_message(&err, Level::Warning);
} }
return; break 'out;
} }
if let EventV1::Logout = event { if let EventV1::Logout = event {
info!("User {addr:?} received log out event!"); info!("User {addr:?} received log out event!");
return; break 'out;
} }
} }
}
}
if let Err(err) = subscriber.quit().await {
error!("{}", err);
sentry::capture_error(&err);
}
} }
#[allow(clippy::too_many_arguments)]
async fn worker_with_kill_signal(
addr: SocketAddr,
subscribed: Arc<RwLock<HashSet<String>>>,
active_servers: Arc<Mutex<lru_time_cache::LruCache<String, ()>>>,
user_id: String,
config: &ProtocolConfiguration,
topic_signal_s: async_channel::Sender<()>,
kill_signal_r: async_channel::Receiver<()>,
read: WsReader,
write: &Mutex<WsWriter>,
kill_signal_s: async_channel::Sender<()>,
) {
worker(
addr,
subscribed,
active_servers,
user_id,
config,
topic_signal_s,
kill_signal_r,
read,
write,
)
.await;
kill_signal_s.send(()).await.ok();
}
#[allow(clippy::too_many_arguments)]
async fn worker( async fn worker(
addr: SocketAddr, addr: SocketAddr,
subscribed: Arc<RwLock<HashSet<String>>>, subscribed: Arc<RwLock<HashSet<String>>>,
active_servers: Arc<Mutex<lru_time_cache::LruCache<String, ()>>>,
user_id: String, user_id: String,
config: &ProtocolConfiguration, config: &ProtocolConfiguration,
topic_signal_s: async_channel::Sender<()>,
kill_signal_r: async_channel::Receiver<()>,
mut read: WsReader, mut read: WsReader,
write: &Mutex<WsWriter>, write: &Mutex<WsWriter>,
) { ) {
loop { loop {
let result = read.try_next().await; let t1 = read.try_next().fuse();
let t2 = kill_signal_r.recv().fuse();
pin_mut!(t1, t2);
select! {
_ = t2 => {
return;
},
result = t1 => {
let msg = match result { let msg = match result {
Ok(Some(msg)) => msg, Ok(Some(msg)) => msg,
Ok(None) => { Ok(None) => {
@@ -368,6 +495,16 @@ async fn worker(
.p(channel.clone()) .p(channel.clone())
.await; .await;
} }
ClientMessage::Subscribe { server_id } => {
let mut servers = active_servers.lock().await;
let has_item = servers.contains_key(&server_id);
servers.insert(server_id, ());
if !has_item {
// Poke the listener to adjust subscriptions
topic_signal_s.send(()).await.ok();
}
}
ClientMessage::Ping { data, responded } => { ClientMessage::Ping { data, responded } => {
if responded.is_none() { if responded.is_none() {
write write
@@ -381,4 +518,6 @@ async fn worker(
_ => {} _ => {}
} }
} }
}
}
} }
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-config" name = "revolt-config"
version = "0.7.5" version = "0.7.7"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
authors = ["Paul Makles <me@insrt.uk>"] authors = ["Paul Makles <me@insrt.uk>"]
+6 -6
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-database" name = "revolt-database"
version = "0.7.5" version = "0.7.7"
edition = "2021" edition = "2021"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"
authors = ["Paul Makles <me@insrt.uk>"] authors = ["Paul Makles <me@insrt.uk>"]
@@ -23,13 +23,13 @@ default = ["mongodb", "async-std-runtime", "tasks"]
[dependencies] [dependencies]
# Core # Core
revolt-config = { version = "0.7.5", path = "../config" } revolt-config = { version = "0.7.7", path = "../config" }
revolt-result = { version = "0.7.5", path = "../result" } revolt-result = { version = "0.7.7", path = "../result" }
revolt-models = { version = "0.7.5", path = "../models", features = [ revolt-models = { version = "0.7.7", path = "../models", features = [
"validator", "validator",
] } ] }
revolt-presence = { version = "0.7.5", path = "../presence" } revolt-presence = { version = "0.7.7", path = "../presence" }
revolt-permissions = { version = "0.7.5", path = "../permissions", features = [ revolt-permissions = { version = "0.7.7", path = "../permissions", features = [
"serde", "serde",
"bson", "bson",
] } ] }
+6 -1
View File
@@ -249,7 +249,7 @@ impl EventV1 {
// TODO: this should be captured by member list in the future and not immediately fanned out to users // TODO: this should be captured by member list in the future and not immediately fanned out to users
if let Ok(members) = db.fetch_all_memberships(&id).await { if let Ok(members) = db.fetch_all_memberships(&id).await {
for member in members { for member in members {
self.clone().p(member.id.server).await; self.clone().server(member.id.server).await;
} }
} }
} }
@@ -259,6 +259,11 @@ impl EventV1 {
self.p(format!("{id}!")).await; self.p(format!("{id}!")).await;
} }
/// Publish server member event
pub async fn server(self, id: String) {
self.p(format!("{id}u")).await;
}
/// Publish internal global event /// Publish internal global event
pub async fn global(self) { pub async fn global(self) {
self.p("global".to_string()).await; self.p("global".to_string()).await;
@@ -8,5 +8,6 @@ pub enum ClientMessage {
Authenticate { token: String }, Authenticate { token: String },
BeginTyping { channel: String }, BeginTyping { channel: String },
EndTyping { channel: String }, EndTyping { channel: String },
Subscribe { server_id: String },
Ping { data: Ping, responded: Option<()> }, Ping { data: Ping, responded: Option<()> },
} }
+3 -3
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-models" name = "revolt-models"
version = "0.7.5" version = "0.7.7"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
authors = ["Paul Makles <me@insrt.uk>"] authors = ["Paul Makles <me@insrt.uk>"]
@@ -19,8 +19,8 @@ default = ["serde", "partials", "rocket"]
[dependencies] [dependencies]
# Core # Core
revolt-config = { version = "0.7.5", path = "../config" } revolt-config = { version = "0.7.7", path = "../config" }
revolt-permissions = { version = "0.7.5", path = "../permissions" } revolt-permissions = { version = "0.7.7", path = "../permissions" }
# Utility # Utility
regex = "1" regex = "1"
+2 -2
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-permissions" name = "revolt-permissions"
version = "0.7.5" version = "0.7.7"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
authors = ["Paul Makles <me@insrt.uk>"] authors = ["Paul Makles <me@insrt.uk>"]
@@ -21,7 +21,7 @@ async-std = { version = "1.8.0", features = ["attributes"] }
[dependencies] [dependencies]
# Core # Core
revolt-result = { version = "0.7.5", path = "../result" } revolt-result = { version = "0.7.7", path = "../result" }
# Utility # Utility
auto_ops = "0.3.0" auto_ops = "0.3.0"
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-presence" name = "revolt-presence"
version = "0.7.5" version = "0.7.7"
edition = "2021" edition = "2021"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"
authors = ["Paul Makles <me@insrt.uk>"] authors = ["Paul Makles <me@insrt.uk>"]
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-result" name = "revolt-result"
version = "0.7.5" version = "0.7.7"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
authors = ["Paul Makles <me@insrt.uk>"] authors = ["Paul Makles <me@insrt.uk>"]
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "revolt-delta" name = "revolt-delta"
version = "0.7.5" version = "0.7.7"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"
authors = ["Paul Makles <paulmakles@gmail.com>"] authors = ["Paul Makles <paulmakles@gmail.com>"]
edition = "2018" edition = "2018"