Constellation, Spacedust, Slingshot, UFOs: atproto crates and services for microcosm

subscriber sourced messages

Changed files
+54 -5
spacedust
+12
spacedust/src/error.rs
··· 23 } 24 25 #[derive(Debug, Error)] 26 pub enum DelayError { 27 #[error("delay ended")] 28 DelayEnded,
··· 23 } 24 25 #[derive(Debug, Error)] 26 + pub enum SubscriberUpdateError { 27 + #[error("failed to parse json for subscriber update: {0}")] 28 + FailedToParseMessage(serde_json::Error), 29 + #[error("more wantedSources were requested than allowed (max 1,000)")] 30 + TooManySourcesWanted, 31 + #[error("more wantedSubjectDids were requested than allowed (max 10,000)")] 32 + TooManyDidsWanted, 33 + #[error("more wantedSubjects were requested than allowed (max 50,000)")] 34 + TooManySubjectsWanted, 35 + } 36 + 37 + #[derive(Debug, Error)] 38 pub enum DelayError { 39 #[error("delay ended")] 40 DelayEnded,
+8 -1
spacedust/src/lib.rs
··· 8 use links::CollectedLink; 9 use jetstream::events::CommitEvent; 10 use tokio_tungstenite::tungstenite::Message; 11 - use serde::Serialize; 12 13 #[derive(Debug)] 14 pub struct FilterableProperties { ··· 84 // TODO: include the record too? would save clients a level of hydration 85 // ^^ no, not for now. until we backfill + support broader deletes at *least*. 86 }
··· 8 use links::CollectedLink; 9 use jetstream::events::CommitEvent; 10 use tokio_tungstenite::tungstenite::Message; 11 + use serde::{Deserialize, Serialize}; 12 + use server::MultiSubscribeQuery; 13 14 #[derive(Debug)] 15 pub struct FilterableProperties { ··· 85 // TODO: include the record too? would save clients a level of hydration 86 // ^^ no, not for now. until we backfill + support broader deletes at *least*. 87 } 88 + 89 + #[derive(Debug, Deserialize)] 90 + #[serde(tag = "type", content = "payload", rename_all = "snake_case")] 91 + pub enum SubscriberSourcedMessage { 92 + OptionsUpdate(MultiSubscribeQuery), 93 + }
+4 -2
spacedust/src/server.rs
··· 19 use serde::{Deserialize, Serialize}; 20 use tokio::sync::broadcast; 21 use tokio::time::Instant; 22 - use tokio_tungstenite::tungstenite::protocol::Role; 23 use tokio_util::sync::CancellationToken; 24 use async_trait::async_trait; 25 use std::collections::HashSet; ··· 315 let ws = tokio_tungstenite::WebSocketStream::from_raw_socket( 316 upgraded.into_inner(), 317 Role::Server, 318 - None, 319 ) 320 .await; 321
··· 19 use serde::{Deserialize, Serialize}; 20 use tokio::sync::broadcast; 21 use tokio::time::Instant; 22 + use tokio_tungstenite::tungstenite::protocol::{Role, WebSocketConfig}; 23 use tokio_util::sync::CancellationToken; 24 use async_trait::async_trait; 25 use std::collections::HashSet; ··· 315 let ws = tokio_tungstenite::WebSocketStream::from_raw_socket( 316 upgraded.into_inner(), 317 Role::Server, 318 + Some(WebSocketConfig::default().max_message_size( 319 + Some(10 * 2_usize.pow(20)) // 10MiB, matching jetstream 320 + )), 321 ) 322 .await; 323
+30 -2
spacedust/src/subscriber.rs
··· 1 use std::sync::Arc; 2 use tokio::time::interval; 3 use std::time::Duration; 4 use futures::StreamExt; 5 - use crate::{ClientMessage, FilterableProperties}; 6 use crate::server::MultiSubscribeQuery; 7 use futures::SinkExt; 8 use std::error::Error; ··· 27 } 28 29 pub async fn start( 30 - self, 31 ws: WebSocketStream<WebsocketConnectionRaw>, 32 mut receiver: broadcast::Receiver<Arc<ClientMessage>> 33 ) -> Result<(), Box<dyn Error>> { ··· 76 self.shutdown.cancel(); 77 } 78 } 79 Some(Ok(m)) => log::trace!("subscriber sent an unexpected message: {m:?}"), 80 Some(Err(e)) => { 81 log::error!("failed to receive subscriber message: {e:?}"); ··· 137 true 138 } 139 }
··· 1 + use crate::error::SubscriberUpdateError; 2 use std::sync::Arc; 3 use tokio::time::interval; 4 use std::time::Duration; 5 use futures::StreamExt; 6 + use crate::{ClientMessage, FilterableProperties, SubscriberSourcedMessage}; 7 use crate::server::MultiSubscribeQuery; 8 use futures::SinkExt; 9 use std::error::Error; ··· 28 } 29 30 pub async fn start( 31 + mut self, 32 ws: WebSocketStream<WebsocketConnectionRaw>, 33 mut receiver: broadcast::Receiver<Arc<ClientMessage>> 34 ) -> Result<(), Box<dyn Error>> { ··· 77 self.shutdown.cancel(); 78 } 79 } 80 + Some(Ok(Message::Text(raw))) => { 81 + if let Err(e) = self.query.update_from_raw(&raw) { 82 + log::error!("subscriber options could not be updated, dropping: {e:?}"); 83 + // TODO: send client an explanation 84 + self.shutdown.cancel(); 85 + } 86 + }, 87 Some(Ok(m)) => log::trace!("subscriber sent an unexpected message: {m:?}"), 88 Some(Err(e)) => { 89 log::error!("failed to receive subscriber message: {e:?}"); ··· 145 true 146 } 147 } 148 + 149 + 150 + 151 + impl MultiSubscribeQuery { 152 + pub fn update_from_raw(&mut self, s: &str) -> Result<(), SubscriberUpdateError> { 153 + let SubscriberSourcedMessage::OptionsUpdate(opts) = serde_json::from_str(s) 154 + .map_err(SubscriberUpdateError::FailedToParseMessage)?; 155 + if opts.wanted_sources.len() > 1_000 { 156 + return Err(SubscriberUpdateError::TooManySourcesWanted); 157 + } 158 + if opts.wanted_subject_dids.len() > 10_000 { 159 + return Err(SubscriberUpdateError::TooManyDidsWanted); 160 + } 161 + if opts.wanted_subjects.len() > 50_000 { 162 + return Err(SubscriberUpdateError::TooManySubjectsWanted); 163 + } 164 + *self = opts; 165 + Ok(()) 166 + } 167 + }