|
@@ -1,61 +1,154 @@
|
|
-use crate::{Connection, Error, Subscription};
|
|
|
|
|
|
+use crate::{connection::ConnectionId, subscription::SubscriptionManager, Connection, Error};
|
|
use futures_util::StreamExt;
|
|
use futures_util::StreamExt;
|
|
|
|
+use nostr_rs_client::{Error as ClientError, Pool};
|
|
use nostr_rs_storage_base::Storage;
|
|
use nostr_rs_storage_base::Storage;
|
|
-use nostr_rs_types::{
|
|
|
|
- relayer,
|
|
|
|
- types::{Event, SubscriptionId},
|
|
|
|
- Request, Response,
|
|
|
|
-};
|
|
|
|
-use parking_lot::{RwLock, RwLockReadGuard};
|
|
|
|
|
|
+use nostr_rs_types::{relayer, types::Event, Request, Response};
|
|
use std::{collections::HashMap, ops::Deref, sync::Arc};
|
|
use std::{collections::HashMap, ops::Deref, sync::Arc};
|
|
-use tokio::sync::mpsc;
|
|
|
|
-#[allow(unused_imports)]
|
|
|
|
use tokio::{
|
|
use tokio::{
|
|
- net::TcpStream,
|
|
|
|
|
|
+ net::{TcpListener, TcpStream},
|
|
sync::mpsc::{channel, Receiver, Sender},
|
|
sync::mpsc::{channel, Receiver, Sender},
|
|
};
|
|
};
|
|
|
|
+use tokio::{
|
|
|
|
+ sync::{mpsc, RwLock},
|
|
|
|
+ task::JoinHandle,
|
|
|
|
+};
|
|
|
|
|
|
-type SubId = u128;
|
|
|
|
-
|
|
|
|
-type Subscriptions = HashMap<SubId, (SubscriptionId, Sender<Response>)>;
|
|
|
|
-
|
|
|
|
-pub struct Relayer<T: Storage> {
|
|
|
|
|
|
+/// Relayer struct
|
|
|
|
+///
|
|
|
|
+pub struct Relayer<T: Storage + Send + Sync + 'static> {
|
|
/// Storage engine, if provided the services are going to persisted in disk,
|
|
/// Storage engine, if provided the services are going to persisted in disk,
|
|
/// otherwise all the messages are going to be ephemeral, making this
|
|
/// otherwise all the messages are going to be ephemeral, making this
|
|
/// relayer just a dumb proxy (that can be useful for privacy) but it won't
|
|
/// relayer just a dumb proxy (that can be useful for privacy) but it won't
|
|
/// be able to perform any optimization like prefetching content while offline
|
|
/// be able to perform any optimization like prefetching content while offline
|
|
storage: Option<T>,
|
|
storage: Option<T>,
|
|
- /// Keeps a map between the internal subscription ID and the subscription
|
|
|
|
- /// type. One subscription ID may have multiple subscription types.
|
|
|
|
- ///
|
|
|
|
- /// Each connection keeps a list of the subscription ID provided by the user
|
|
|
|
- /// (String) and the internal, globally recognized subscription ID which is
|
|
|
|
- /// internal (SubId)
|
|
|
|
- subscriptions_ids_index: RwLock<HashMap<SubId, Vec<Subscription>>>,
|
|
|
|
- /// Each subscription type that is active has a list of subscriptions.
|
|
|
|
|
|
+ /// x
|
|
|
|
+ subscriptions: Arc<SubscriptionManager>,
|
|
|
|
+ clients: RwLock<HashMap<ConnectionId, Connection>>,
|
|
|
|
+ /// This Sender can be used to send requests from anywhere to the relayer.
|
|
|
|
+ send_to_relayer: Sender<(ConnectionId, Request)>,
|
|
|
|
+ /// This Receiver is the relayer the way the relayer receives messages
|
|
|
|
+ relayer_receiver: Option<Receiver<(ConnectionId, Request)>>,
|
|
|
|
+ /// Client pool
|
|
///
|
|
///
|
|
- /// A single REQ can be subscribed to multiple subscription types, specially
|
|
|
|
- /// when it is translated in OR filters. It is designed this way to allow a
|
|
|
|
- /// fast iteration and match quickly filters.
|
|
|
|
- subscriptions: RwLock<HashMap<Subscription, RwLock<Subscriptions>>>,
|
|
|
|
- clients: RwLock<HashMap<u128, Connection>>,
|
|
|
|
- #[allow(dead_code)]
|
|
|
|
- sender: Sender<(u128, Request)>,
|
|
|
|
|
|
+ /// A relayer can optionally be connected to a pool of clients to get foreign events.
|
|
|
|
+ client_pool: Option<(Pool, JoinHandle<()>)>,
|
|
}
|
|
}
|
|
|
|
|
|
-impl<T: Storage> Relayer<T> {
|
|
|
|
- pub fn new(storage: Option<T>) -> (Arc<Self>, Receiver<(u128, Request)>) {
|
|
|
|
|
|
+impl<T: Storage + Send + Sync + 'static> Drop for Relayer<T> {
|
|
|
|
+ fn drop(&mut self) {
|
|
|
|
+ if let Some((_, handle)) = self.client_pool.take() {
|
|
|
|
+ let _ = handle.abort();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+impl<T: Storage + Send + Sync + 'static> Relayer<T> {
|
|
|
|
+ /// Creates a new relayer instance
|
|
|
|
+ ///
|
|
|
|
+ /// If the storage is given, it will be used to persist events, as well to
|
|
|
|
+ /// server past events when a new subscription is added.
|
|
|
|
+ ///
|
|
|
|
+ /// If the client_pool is given it will be used to connect to those relayers
|
|
|
|
+ /// and create a network of relayers, reposting events to them and
|
|
|
|
+ /// subscribing to their events.`gqq`
|
|
|
|
+ pub fn new(storage: Option<T>, client_pool: Option<Pool>) -> Result<Self, Error> {
|
|
let (sender, receiver) = channel(100_000);
|
|
let (sender, receiver) = channel(100_000);
|
|
- (
|
|
|
|
- Arc::new(Self {
|
|
|
|
- storage,
|
|
|
|
- subscriptions: RwLock::new(HashMap::new()),
|
|
|
|
- subscriptions_ids_index: RwLock::new(HashMap::new()),
|
|
|
|
- clients: RwLock::new(HashMap::new()),
|
|
|
|
- sender,
|
|
|
|
- }),
|
|
|
|
- receiver,
|
|
|
|
- )
|
|
|
|
|
|
+ Ok(Self {
|
|
|
|
+ storage,
|
|
|
|
+ subscriptions: Default::default(),
|
|
|
|
+ send_to_relayer: sender.clone(),
|
|
|
|
+ relayer_receiver: Some(receiver),
|
|
|
|
+ clients: Default::default(),
|
|
|
|
+ client_pool: if let Some(client_pool) = client_pool {
|
|
|
|
+ Some(Self::handle_client_pool(client_pool, sender)?)
|
|
|
|
+ } else {
|
|
|
|
+ None
|
|
|
|
+ },
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Total number of subscribers requests that actively listening for new events
|
|
|
|
+ pub fn total_subscribers(&self) -> usize {
|
|
|
|
+ self.subscriptions.total_subscribers()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Splits the relayer object and extract their receiver.
|
|
|
|
+ pub fn split(mut self) -> Result<(Self, Receiver<(ConnectionId, Request)>), Error> {
|
|
|
|
+ let receiver = self.relayer_receiver.take().ok_or(Error::AlreadySplitted)?;
|
|
|
|
+ Ok((self, receiver))
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Runs the relayer main loop in a tokio task and returns it.
|
|
|
|
+ ///
|
|
|
|
+ /// This function consumes the object and takes the ownership. The returned
|
|
|
|
+ /// JoinHandle() can be used to stop the main loop
|
|
|
|
+ pub fn main(self, server: TcpListener) -> Result<JoinHandle<()>, Error> {
|
|
|
|
+ let (this, mut receiver) = self.split()?;
|
|
|
|
+ Ok(tokio::spawn(async move {
|
|
|
|
+ loop {
|
|
|
|
+ tokio::select! {
|
|
|
|
+ Ok((stream, _)) = server.accept() => {
|
|
|
|
+ // accept new external connections
|
|
|
|
+ let _ = this.add_connection(None, stream).await;
|
|
|
|
+ },
|
|
|
|
+ Some((conn_id, request)) = receiver.recv() => {
|
|
|
|
+ // receive messages from the connection pool
|
|
|
|
+ if conn_id.is_empty() {
|
|
|
|
+ // connection pool
|
|
|
|
+ if let Request::Event(event) = request {
|
|
|
|
+ if let Some(storage) = this.storage.as_ref() {
|
|
|
|
+ let _ = storage.store_local_event(&event).await;
|
|
|
|
+ }
|
|
|
|
+ this.broadcast(&event.deref()).await;
|
|
|
|
+ }
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let connections = this.clients.read().await;
|
|
|
|
+ let connection = if let Some(connection) = connections.get(&conn_id) {
|
|
|
|
+ connection
|
|
|
|
+ } else {
|
|
|
|
+ continue;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // receive messages from clients
|
|
|
|
+ let _ = this.process_request_from_client(connection, request).await;
|
|
|
|
+ drop(connections);
|
|
|
|
+ }
|
|
|
|
+ else => {
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }))
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ fn handle_client_pool(
|
|
|
|
+ client_pool: Pool,
|
|
|
|
+ sender: Sender<(ConnectionId, Request)>,
|
|
|
|
+ ) -> Result<(Pool, JoinHandle<()>), ClientError> {
|
|
|
|
+ let (mut receiver, client_pool) = client_pool.split()?;
|
|
|
|
+
|
|
|
|
+ let handle = tokio::spawn(async move {
|
|
|
|
+ loop {
|
|
|
|
+ if let Some((response, _)) = receiver.recv().await {
|
|
|
|
+ match response {
|
|
|
|
+ Response::Event(event) => {
|
|
|
|
+ let _ = sender
|
|
|
|
+ .send((
|
|
|
|
+ ConnectionId::new_empty(),
|
|
|
|
+ Request::Event(event.event.into()),
|
|
|
|
+ ))
|
|
|
|
+ .await;
|
|
|
|
+ }
|
|
|
|
+ x => {
|
|
|
|
+ println!("x => {:?}", x);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ Ok((client_pool, handle))
|
|
}
|
|
}
|
|
|
|
|
|
/// Returns a reference to the internal database
|
|
/// Returns a reference to the internal database
|
|
@@ -63,62 +156,50 @@ impl<T: Storage> Relayer<T> {
|
|
&self.storage
|
|
&self.storage
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /// Adds a new TpStream and adds it to the list of active connections.
|
|
|
|
+ ///
|
|
|
|
+ /// This function will spawn the client's loop to receive incoming messages and send those messages
|
|
pub async fn add_connection(
|
|
pub async fn add_connection(
|
|
&self,
|
|
&self,
|
|
- disconnection_notify: Option<mpsc::Sender<u128>>,
|
|
|
|
|
|
+ disconnection_notify: Option<mpsc::Sender<ConnectionId>>,
|
|
stream: TcpStream,
|
|
stream: TcpStream,
|
|
- ) -> Result<u128, Error> {
|
|
|
|
- let client = Connection::new(self.sender.clone(), disconnection_notify, stream).await?;
|
|
|
|
- let id = client.conn_id;
|
|
|
|
- let mut clients = self.clients.write();
|
|
|
|
- clients.insert(id, client);
|
|
|
|
|
|
+ ) -> Result<ConnectionId, Error> {
|
|
|
|
+ let client =
|
|
|
|
+ Connection::new_connection(self.send_to_relayer.clone(), disconnection_notify, stream)
|
|
|
|
+ .await?;
|
|
|
|
+ let id = client.get_conn_id();
|
|
|
|
+ self.clients.write().await.insert(id, client);
|
|
|
|
|
|
Ok(id)
|
|
Ok(id)
|
|
}
|
|
}
|
|
|
|
|
|
- async fn recv_request_from_client(
|
|
|
|
|
|
+ /// Process a request from a connected client
|
|
|
|
+ async fn process_request_from_client(
|
|
&self,
|
|
&self,
|
|
connection: &Connection,
|
|
connection: &Connection,
|
|
request: Request,
|
|
request: Request,
|
|
) -> Result<Option<Request>, Error> {
|
|
) -> Result<Option<Request>, Error> {
|
|
match &request {
|
|
match &request {
|
|
Request::Event(event) => {
|
|
Request::Event(event) => {
|
|
- self.store_and_broadcast_local_event(event.deref()).await;
|
|
|
|
|
|
+ if let Some(storage) = self.storage.as_ref() {
|
|
|
|
+ let _ = storage.store(event).await;
|
|
|
|
+ let _ = storage.store_local_event(event).await;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ self.broadcast(event).await;
|
|
|
|
+
|
|
|
|
+ if let Some((client_pool, _)) = self.client_pool.as_ref() {
|
|
|
|
+ // pass the event to the pool of clients, so this relayer can relay
|
|
|
|
+ // their local events to the clients in the network of relayers
|
|
|
|
+ let _ = client_pool.post(event.clone().into()).await;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
Request::Request(request) => {
|
|
Request::Request(request) => {
|
|
- // Create subscription
|
|
|
|
- let (sub_id, receiver) =
|
|
|
|
- connection.create_subscription(request.subscription_id.deref().to_owned());
|
|
|
|
- let mut sub_index = self.subscriptions_ids_index.write();
|
|
|
|
- let mut subscriptions = self.subscriptions.write();
|
|
|
|
- if let Some(prev_subs) = sub_index.remove(&sub_id) {
|
|
|
|
- // remove any previous subscriptions
|
|
|
|
- prev_subs.iter().for_each(|index| {
|
|
|
|
- if let Some(subscriptions) = subscriptions.get_mut(index) {
|
|
|
|
- subscriptions.write().remove(&sub_id);
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
|
|
+ if let Some((client_pool, _)) = self.client_pool.as_ref() {
|
|
|
|
+ // pass the subscription request to the pool of clients, so this relayer
|
|
|
|
+ // can relay any unknown event to the clients through their subscriptions
|
|
|
|
+ let _ = client_pool.subscribe(request.filters.clone().into()).await;
|
|
}
|
|
}
|
|
- sub_index.insert(
|
|
|
|
- sub_id,
|
|
|
|
- Subscription::from_filters(&request.filters)
|
|
|
|
- .into_iter()
|
|
|
|
- .map(|index| {
|
|
|
|
- subscriptions
|
|
|
|
- .entry(index.clone())
|
|
|
|
- .or_insert_with(|| RwLock::new(HashMap::new()))
|
|
|
|
- .write()
|
|
|
|
- .insert(
|
|
|
|
- sub_id,
|
|
|
|
- (request.subscription_id.clone(), receiver.clone()),
|
|
|
|
- );
|
|
|
|
- index
|
|
|
|
- })
|
|
|
|
- .collect::<Vec<_>>(),
|
|
|
|
- );
|
|
|
|
-
|
|
|
|
- drop(subscriptions);
|
|
|
|
- drop(sub_index);
|
|
|
|
|
|
|
|
if let Some(storage) = self.storage.as_ref() {
|
|
if let Some(storage) = self.storage.as_ref() {
|
|
// Sent all events that match the filter that are stored in our database
|
|
// Sent all events that match the filter that are stored in our database
|
|
@@ -139,102 +220,73 @@ impl<T: Storage> Relayer<T> {
|
|
|
|
|
|
let _ = connection
|
|
let _ = connection
|
|
.send(relayer::EndOfStoredEvents(request.subscription_id.clone()).into());
|
|
.send(relayer::EndOfStoredEvents(request.subscription_id.clone()).into());
|
|
|
|
+
|
|
|
|
+ connection
|
|
|
|
+ .keep_track_subscription(
|
|
|
|
+ request.subscription_id.clone(),
|
|
|
|
+ self.subscriptions
|
|
|
|
+ .subscribe(
|
|
|
|
+ connection.get_conn_id(),
|
|
|
|
+ connection.get_sender(),
|
|
|
|
+ request.clone(),
|
|
|
|
+ )
|
|
|
|
+ .await,
|
|
|
|
+ )
|
|
|
|
+ .await;
|
|
}
|
|
}
|
|
- Request::Close(close) => {
|
|
|
|
- if let Some(id) = connection.get_subscription_id(&close.0) {
|
|
|
|
- let mut subscriptions = self.subscriptions_ids_index.write();
|
|
|
|
- if let Some(indexes) = subscriptions.remove(&id) {
|
|
|
|
- let mut subscriptions = self.subscriptions.write();
|
|
|
|
- indexes.iter().for_each(|index| {
|
|
|
|
- if let Some(subscriptions) = subscriptions.get_mut(index) {
|
|
|
|
- subscriptions.write().remove(&id);
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ Request::Close(_close) => {
|
|
|
|
+ todo!()
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
Ok(Some(request))
|
|
Ok(Some(request))
|
|
}
|
|
}
|
|
|
|
|
|
- pub async fn recv(
|
|
|
|
- &self,
|
|
|
|
- receiver: &mut Receiver<(u128, Request)>,
|
|
|
|
- ) -> Result<Option<Request>, Error> {
|
|
|
|
- let (conn_id, request) = if let Some(request) = receiver.recv().await {
|
|
|
|
- request
|
|
|
|
- } else {
|
|
|
|
- return Ok(None);
|
|
|
|
- };
|
|
|
|
- let connections = self.clients.read();
|
|
|
|
- let connection = connections
|
|
|
|
- .get(&conn_id)
|
|
|
|
- .ok_or(Error::UnknownConnection(conn_id))?;
|
|
|
|
-
|
|
|
|
- self.recv_request_from_client(connection, request).await
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- pub fn send_to_conn(&self, conn_id: u128, response: Response) -> Result<(), Error> {
|
|
|
|
- let connections = self.clients.read();
|
|
|
|
- let connection = connections
|
|
|
|
- .get(&conn_id)
|
|
|
|
- .ok_or(Error::UnknownConnection(conn_id))?;
|
|
|
|
-
|
|
|
|
- connection.send(response)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- #[inline]
|
|
|
|
- fn broadcast_to_subscribers(subscriptions: RwLockReadGuard<Subscriptions>, event: &Event) {
|
|
|
|
- for (_, receiver) in subscriptions.iter() {
|
|
|
|
- let _ = receiver.1.try_send(
|
|
|
|
- relayer::Event {
|
|
|
|
- subscription_id: receiver.0.clone(),
|
|
|
|
- event: event.clone(),
|
|
|
|
- }
|
|
|
|
- .into(),
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- #[inline]
|
|
|
|
- pub async fn store_and_broadcast_local_event(&self, event: &Event) {
|
|
|
|
- if let Some(storage) = self.storage.as_ref() {
|
|
|
|
- let _ = storage.store_local_event(event).await;
|
|
|
|
- }
|
|
|
|
- let subscriptions = self.subscriptions.read();
|
|
|
|
-
|
|
|
|
- for subscription_type in Subscription::from_event(event) {
|
|
|
|
- if let Some(subscribers) = subscriptions.get(&subscription_type) {
|
|
|
|
- Self::broadcast_to_subscribers(subscribers.read(), event);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
#[inline]
|
|
#[inline]
|
|
- pub fn store_and_broadcast(&self, event: &Event) {
|
|
|
|
|
|
+ /// Broadcast a given event to all local subscribers
|
|
|
|
+ pub async fn broadcast(&self, event: &Event) {
|
|
if let Some(storage) = self.storage.as_ref() {
|
|
if let Some(storage) = self.storage.as_ref() {
|
|
- let _ = storage.store(event);
|
|
|
|
|
|
+ let _ = storage.store(event).await;
|
|
}
|
|
}
|
|
- let subscriptions = self.subscriptions.read();
|
|
|
|
|
|
|
|
- for subscription_type in Subscription::from_event(event) {
|
|
|
|
- if let Some(subscribers) = subscriptions.get(&subscription_type) {
|
|
|
|
- Self::broadcast_to_subscribers(subscribers.read(), event);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ self.subscriptions.broadcast(event.clone());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#[cfg(test)]
|
|
#[cfg(test)]
|
|
mod test {
|
|
mod test {
|
|
|
|
+ use std::time::Duration;
|
|
|
|
+
|
|
use super::*;
|
|
use super::*;
|
|
- use crate::get_id;
|
|
|
|
- use nostr_rs_rocksdb::RocksDb;
|
|
|
|
|
|
+ use futures::future::join_all;
|
|
|
|
+ use nostr_rs_memory::Memory;
|
|
use nostr_rs_types::Request;
|
|
use nostr_rs_types::Request;
|
|
|
|
+ use serde_json::json;
|
|
|
|
+ use tokio::time::sleep;
|
|
|
|
+
|
|
|
|
+ fn get_note() -> Request {
|
|
|
|
+ serde_json::from_value(json!(
|
|
|
|
+ [
|
|
|
|
+ "EVENT",
|
|
|
|
+ {
|
|
|
|
+ "kind":1,
|
|
|
|
+ "content":"Pong",
|
|
|
|
+ "tags":[
|
|
|
|
+ ["e","9508850d7ddc8ef58c8b392236c49d472dc23fa11f4e73eb5475dfb099ddff42","","root"],
|
|
|
|
+ ["e","2e72250d80e9b3fd30230b3db3ed7d22f15d266ed345c36700b01ec153c9e28a","","reply"],
|
|
|
|
+ ["p","39a7d06e824c0c2523bedb93f0cef84245e4401fee03b6257a1c6dfd18b57efb"],
|
|
|
|
+ ["p","ee7202ad91459e013bfef263c59e47deb0163a5e7651b026673765488bfaf102"]
|
|
|
|
+ ],
|
|
|
|
+ "created_at":1681938616,
|
|
|
|
+ "pubkey":"a42007e33cfa25673b26f46f39df039aa6003258a68dc88f1f1e0447607aedb3",
|
|
|
|
+ "id":"e862fe23daf52ab09b36a37fa91ca3743e0c323e630e8627891212ca147c2da9",
|
|
|
|
+ "sig":"9036150a6c8a32933cffcc42aec4d2109a22e9f10d1c3860c0435a925e6386babb7df5c95fcf68c8ed6a9726a1f07225af663d0b068eb555014130aad21674fc",
|
|
|
|
+ }
|
|
|
|
+ ])).expect("value")
|
|
|
|
+ }
|
|
|
|
|
|
- async fn get_db(prefill: bool) -> RocksDb {
|
|
|
|
- let db = RocksDb::new(format!("/tmp/db/{}", get_id())).expect("db");
|
|
|
|
|
|
+ async fn get_db(prefill: bool) -> Memory {
|
|
|
|
+ let db = Memory::default();
|
|
if prefill {
|
|
if prefill {
|
|
let events = include_str!("../tests/events.json")
|
|
let events = include_str!("../tests/events.json")
|
|
.lines()
|
|
.lines()
|
|
@@ -244,23 +296,73 @@ mod test {
|
|
for event in events {
|
|
for event in events {
|
|
assert!(db.store(&event).await.expect("valid"));
|
|
assert!(db.store(&event).await.expect("valid"));
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ while db.is_flushing() {
|
|
|
|
+ tokio::time::sleep(tokio::time::Duration::from_millis(1)).await;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
db
|
|
db
|
|
}
|
|
}
|
|
|
|
|
|
#[tokio::test]
|
|
#[tokio::test]
|
|
async fn serve_listener_from_local_db() {
|
|
async fn serve_listener_from_local_db() {
|
|
- let request: Request = serde_json::from_str("[
|
|
|
|
- \"REQ\",\"1298169700973717\",
|
|
|
|
- {\"authors\":[\"39a7d06e824c0c2523bedb93f0cef84245e4401fee03b6257a1c6dfd18b57efb\"],\"since\":1681928304},
|
|
|
|
- {\"#p\":[\"39a7d06e824c0c2523bedb93f0cef84245e4401fee03b6257a1c6dfd18b57efb\"],\"kinds\":[1,3,6,7,9735],\"since\":1681928304},
|
|
|
|
- {\"#p\":[\"39a7d06e824c0c2523bedb93f0cef84245e4401fee03b6257a1c6dfd18b57efb\"],\"kinds\":[4]},
|
|
|
|
- {\"authors\":[\"39a7d06e824c0c2523bedb93f0cef84245e4401fee03b6257a1c6dfd18b57efb\"],\"kinds\":[4]},
|
|
|
|
- {\"#e\":[\"2e72250d80e9b3fd30230b3db3ed7d22f15d266ed345c36700b01ec153c9e28a\",\"a5e3369c43daf2675ecbce18831e5f4e07db0d4dde0ef4f5698e645e4c46eed1\"],\"kinds\":[1,6,7,9735]}
|
|
|
|
- ]").expect("valid object");
|
|
|
|
- let (relayer, _) = Relayer::new(Some(get_db(true).await));
|
|
|
|
|
|
+ let request = serde_json::from_value(json!([
|
|
|
|
+ "REQ",
|
|
|
|
+ "1298169700973717",
|
|
|
|
+ {
|
|
|
|
+ "authors": [
|
|
|
|
+ "39a7d06e824c0c2523bedb93f0cef84245e4401fee03b6257a1c6dfd18b57efb"
|
|
|
|
+ ],
|
|
|
|
+ "since": 1681928304
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ "#p": [
|
|
|
|
+ "39a7d06e824c0c2523bedb93f0cef84245e4401fee03b6257a1c6dfd18b57efb"
|
|
|
|
+ ],
|
|
|
|
+ "kinds": [
|
|
|
|
+ 1,
|
|
|
|
+ 3,
|
|
|
|
+ 6,
|
|
|
|
+ 7,
|
|
|
|
+ 9735
|
|
|
|
+ ],
|
|
|
|
+ "since": 1681928304
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ "#p": [
|
|
|
|
+ "39a7d06e824c0c2523bedb93f0cef84245e4401fee03b6257a1c6dfd18b57efb"
|
|
|
|
+ ],
|
|
|
|
+ "kinds": [
|
|
|
|
+ 4
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ "authors": [
|
|
|
|
+ "39a7d06e824c0c2523bedb93f0cef84245e4401fee03b6257a1c6dfd18b57efb"
|
|
|
|
+ ],
|
|
|
|
+ "kinds": [
|
|
|
|
+ 4
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ "#e": [
|
|
|
|
+ "2e72250d80e9b3fd30230b3db3ed7d22f15d266ed345c36700b01ec153c9e28a",
|
|
|
|
+ "a5e3369c43daf2675ecbce18831e5f4e07db0d4dde0ef4f5698e645e4c46eed1"
|
|
|
|
+ ],
|
|
|
|
+ "kinds": [
|
|
|
|
+ 1,
|
|
|
|
+ 6,
|
|
|
|
+ 7,
|
|
|
|
+ 9735
|
|
|
|
+ ]
|
|
|
|
+ }
|
|
|
|
+ ]))
|
|
|
|
+ .expect("valid object");
|
|
|
|
+ let relayer = Relayer::new(Some(get_db(true).await), None).expect("valid relayer");
|
|
let (connection, mut recv) = Connection::new_for_test();
|
|
let (connection, mut recv) = Connection::new_for_test();
|
|
- let _ = relayer.recv_request_from_client(&connection, request).await;
|
|
|
|
|
|
+ let _ = relayer
|
|
|
|
+ .process_request_from_client(&connection, request)
|
|
|
|
+ .await;
|
|
// ev1
|
|
// ev1
|
|
assert_eq!(
|
|
assert_eq!(
|
|
"9508850d7ddc8ef58c8b392236c49d472dc23fa11f4e73eb5475dfb099ddff42",
|
|
"9508850d7ddc8ef58c8b392236c49d472dc23fa11f4e73eb5475dfb099ddff42",
|
|
@@ -294,6 +396,30 @@ mod test {
|
|
.id
|
|
.id
|
|
.to_string()
|
|
.to_string()
|
|
);
|
|
);
|
|
|
|
+
|
|
|
|
+ // ev3 (again)
|
|
|
|
+ assert_eq!(
|
|
|
|
+ "e862fe23daf52ab09b36a37fa91ca3743e0c323e630e8627891212ca147c2da9",
|
|
|
|
+ recv.try_recv()
|
|
|
|
+ .expect("valid")
|
|
|
|
+ .as_event()
|
|
|
|
+ .expect("event")
|
|
|
|
+ .event
|
|
|
|
+ .id
|
|
|
|
+ .to_string()
|
|
|
|
+ );
|
|
|
|
+ // ev2 (again)
|
|
|
|
+ assert_eq!(
|
|
|
|
+ "2e72250d80e9b3fd30230b3db3ed7d22f15d266ed345c36700b01ec153c9e28a",
|
|
|
|
+ recv.try_recv()
|
|
|
|
+ .expect("valid")
|
|
|
|
+ .as_event()
|
|
|
|
+ .expect("event")
|
|
|
|
+ .event
|
|
|
|
+ .id
|
|
|
|
+ .to_string()
|
|
|
|
+ );
|
|
|
|
+
|
|
// eod
|
|
// eod
|
|
assert!(recv
|
|
assert!(recv
|
|
.try_recv()
|
|
.try_recv()
|
|
@@ -306,10 +432,46 @@ mod test {
|
|
|
|
|
|
#[tokio::test]
|
|
#[tokio::test]
|
|
async fn server_listener_real_time() {
|
|
async fn server_listener_real_time() {
|
|
- let request: Request = serde_json::from_str("[\"REQ\",\"1298169700973717\",{\"authors\":[\"39a7d06e824c0c2523bedb93f0cef84245e4401fee03b6257a1c6dfd18b57efb\"],\"since\":1681939304},{\"#p\":[\"39a7d06e824c0c2523bedb93f0cef84245e4401fee03b6257a1c6dfd18b57efb\"],\"kinds\":[1,3,6,7,9735],\"since\":1681939304},{\"#p\":[\"39a7d06e824c0c2523bedb93f0cef84245e4401fee03b6257a1c6dfd18b57efb\"],\"kinds\":[4]},{\"authors\":[\"39a7d06e824c0c2523bedb93f0cef84245e4401fee03b6257a1c6dfd18b57efb\"],\"kinds\":[4]},{\"#e\":[\"2e72250d80e9b3fd30230b3db3ed7d22f15d266ed345c36700b01ec153c9e28a\",\"a5e3369c43daf2675ecbce18831e5f4e07db0d4dde0ef4f5698e645e4c46eed1\"],\"kinds\":[1,6,7,9735]}]").expect("valid object");
|
|
|
|
- let (relayer, _) = Relayer::new(Some(get_db(false).await));
|
|
|
|
|
|
+ let request: Request = serde_json::from_value(json!(
|
|
|
|
+ [
|
|
|
|
+ "REQ",
|
|
|
|
+ "1298169700973717",
|
|
|
|
+ {
|
|
|
|
+ "authors": ["39a7d06e824c0c2523bedb93f0cef84245e4401fee03b6257a1c6dfd18b57efb"],
|
|
|
|
+ "since":1681939304
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ "#p":["39a7d06e824c0c2523bedb93f0cef84245e4401fee03b6257a1c6dfd18b57efb"],
|
|
|
|
+ "kinds":[1,3,6,7,9735],
|
|
|
|
+ "since":1681939304
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ "#p":["39a7d06e824c0c2523bedb93f0cef84245e4401fee03b6257a1c6dfd18b57efb"],
|
|
|
|
+ "kinds":[4]
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ "authors":["39a7d06e824c0c2523bedb93f0cef84245e4401fee03b6257a1c6dfd18b57efb"],
|
|
|
|
+ "kinds":[4]
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ "#e":[
|
|
|
|
+ "2e72250d80e9b3fd30230b3db3ed7d22f15d266ed345c36700b01ec153c9e28a",
|
|
|
|
+ "a5e3369c43daf2675ecbce18831e5f4e07db0d4dde0ef4f5698e645e4c46eed1"
|
|
|
|
+ ],
|
|
|
|
+ "kinds":[1,6,7,9735]
|
|
|
|
+ }
|
|
|
|
+ ]))
|
|
|
|
+ .expect("valid object");
|
|
|
|
+ let relayer = Relayer::new(Some(get_db(false).await), None).expect("valid relayer");
|
|
let (connection, mut recv) = Connection::new_for_test();
|
|
let (connection, mut recv) = Connection::new_for_test();
|
|
- let _ = relayer.recv_request_from_client(&connection, request).await;
|
|
|
|
|
|
+
|
|
|
|
+ assert_eq!(relayer.total_subscribers(), 0);
|
|
|
|
+ let _ = relayer
|
|
|
|
+ .process_request_from_client(&connection, request)
|
|
|
|
+ .await;
|
|
|
|
+
|
|
|
|
+ assert_eq!(relayer.total_subscribers(), 5);
|
|
|
|
+
|
|
// eod
|
|
// eod
|
|
assert!(recv
|
|
assert!(recv
|
|
.try_recv()
|
|
.try_recv()
|
|
@@ -320,14 +482,254 @@ mod test {
|
|
// It is empty
|
|
// It is empty
|
|
assert!(recv.try_recv().is_err());
|
|
assert!(recv.try_recv().is_err());
|
|
|
|
|
|
- let new_event: Request = serde_json::from_str(r#"["EVENT", {"kind":1,"content":"Pong","tags":[["e","9508850d7ddc8ef58c8b392236c49d472dc23fa11f4e73eb5475dfb099ddff42","","root"],["e","2e72250d80e9b3fd30230b3db3ed7d22f15d266ed345c36700b01ec153c9e28a","","reply"],["p","39a7d06e824c0c2523bedb93f0cef84245e4401fee03b6257a1c6dfd18b57efb"],["p","ee7202ad91459e013bfef263c59e47deb0163a5e7651b026673765488bfaf102"]],"created_at":1681938616,"pubkey":"a42007e33cfa25673b26f46f39df039aa6003258a68dc88f1f1e0447607aedb3","id":"e862fe23daf52ab09b36a37fa91ca3743e0c323e630e8627891212ca147c2da9","sig":"9036150a6c8a32933cffcc42aec4d2109a22e9f10d1c3860c0435a925e6386babb7df5c95fcf68c8ed6a9726a1f07225af663d0b068eb555014130aad21674fc","meta":{"revision":0,"created":1681939266488,"version":0},"$loki":108}]"#).expect("value");
|
|
|
|
|
|
+ relayer
|
|
|
|
+ .process_request_from_client(&connection, get_note())
|
|
|
|
+ .await
|
|
|
|
+ .expect("process event");
|
|
|
|
+
|
|
|
|
+ sleep(Duration::from_millis(100)).await;
|
|
|
|
+
|
|
|
|
+ // It is not empty
|
|
|
|
+ let msg = recv.try_recv();
|
|
|
|
+ assert!(msg.is_ok());
|
|
|
|
+ assert_eq!(
|
|
|
|
+ msg.expect("is ok")
|
|
|
|
+ .as_event()
|
|
|
|
+ .expect("valid")
|
|
|
|
+ .subscription_id
|
|
|
|
+ .to_string(),
|
|
|
|
+ "1298169700973717".to_owned()
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ // it must be deliverd at most once
|
|
|
|
+ assert!(recv.try_recv().is_err());
|
|
|
|
+ assert_eq!(relayer.total_subscribers(), 5);
|
|
|
|
+
|
|
|
|
+ // when client is dropped, the subscription is removed
|
|
|
|
+ // automatically
|
|
|
|
+ drop(connection);
|
|
|
|
+
|
|
|
|
+ sleep(Duration::from_millis(10)).await;
|
|
|
|
+
|
|
|
|
+ assert_eq!(relayer.total_subscribers(), 0);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ #[tokio::test]
|
|
|
|
+ async fn subscribe_partial_key() {
|
|
|
|
+ let request: Request = serde_json::from_value(json!([
|
|
|
|
+ "REQ",
|
|
|
|
+ "1298169700973717",
|
|
|
|
+ {
|
|
|
|
+ "authors":["a42007e33c"],
|
|
|
|
+ "since":1681939304
|
|
|
|
+ }
|
|
|
|
+ ]))
|
|
|
|
+ .expect("valid object");
|
|
|
|
+
|
|
|
|
+ let relayer = Relayer::new(Some(get_db(false).await), None).expect("valid relayer");
|
|
|
|
+ let (connection, mut recv) = Connection::new_for_test();
|
|
|
|
+
|
|
|
|
+ assert_eq!(relayer.total_subscribers(), 0);
|
|
|
|
+ let _ = relayer
|
|
|
|
+ .process_request_from_client(&connection, request)
|
|
|
|
+ .await;
|
|
|
|
+
|
|
|
|
+ assert_eq!(relayer.total_subscribers(), 1);
|
|
|
|
+
|
|
|
|
+ // eod
|
|
|
|
+ assert!(recv
|
|
|
|
+ .try_recv()
|
|
|
|
+ .expect("valid")
|
|
|
|
+ .as_end_of_stored_events()
|
|
|
|
+ .is_some());
|
|
|
|
+
|
|
|
|
+ // It is empty
|
|
|
|
+ assert!(recv.try_recv().is_err());
|
|
|
|
|
|
relayer
|
|
relayer
|
|
- .recv_request_from_client(&connection, new_event)
|
|
|
|
|
|
+ .process_request_from_client(&connection, get_note())
|
|
.await
|
|
.await
|
|
.expect("process event");
|
|
.expect("process event");
|
|
|
|
|
|
|
|
+ sleep(Duration::from_millis(100)).await;
|
|
|
|
+
|
|
// It is not empty
|
|
// It is not empty
|
|
- assert!(recv.try_recv().is_ok());
|
|
|
|
|
|
+ let msg = recv.try_recv();
|
|
|
|
+ assert!(msg.is_ok());
|
|
|
|
+ assert_eq!(
|
|
|
|
+ msg.expect("is ok")
|
|
|
|
+ .as_event()
|
|
|
|
+ .expect("valid")
|
|
|
|
+ .subscription_id
|
|
|
|
+ .to_string(),
|
|
|
|
+ "1298169700973717".to_owned()
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ // it must be deliverd at most once
|
|
|
|
+ assert!(recv.try_recv().is_err());
|
|
|
|
+ assert_eq!(relayer.total_subscribers(), 1);
|
|
|
|
+
|
|
|
|
+ // when client is dropped, the subscription is removed
|
|
|
|
+ // automatically
|
|
|
|
+ drop(connection);
|
|
|
|
+
|
|
|
|
+ sleep(Duration::from_millis(10)).await;
|
|
|
|
+
|
|
|
|
+ assert_eq!(relayer.total_subscribers(), 0);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ #[tokio::test]
|
|
|
|
+ async fn multiple_subcribers() {
|
|
|
|
+ let req1: Request = serde_json::from_value(json!(["REQ", "1298169700973717", {
|
|
|
|
+ "authors":["c42007e33c"],
|
|
|
|
+ }]))
|
|
|
|
+ .expect("valid object");
|
|
|
|
+ let req2: Request = serde_json::from_value(json!(["REQ", "1298169700973717", {
|
|
|
|
+ "authors":["a42007e33c"]
|
|
|
|
+ }]))
|
|
|
|
+ .expect("valid object");
|
|
|
|
+
|
|
|
|
+ let relayer = Relayer::new(Some(get_db(false).await), None).expect("valid relayer");
|
|
|
|
+ let (publisher, _) = Connection::new_for_test();
|
|
|
|
+
|
|
|
|
+ let mut set1 = (0..1000)
|
|
|
|
+ .map(|_| Connection::new_for_test())
|
|
|
|
+ .collect::<Vec<_>>();
|
|
|
|
+
|
|
|
|
+ let mut set2 = (0..100)
|
|
|
|
+ .map(|_| Connection::new_for_test())
|
|
|
|
+ .collect::<Vec<_>>();
|
|
|
|
+
|
|
|
|
+ let subscribe1 = set1
|
|
|
|
+ .iter()
|
|
|
|
+ .map(|(connection, _)| relayer.process_request_from_client(connection, req1.clone()))
|
|
|
|
+ .collect::<Vec<_>>();
|
|
|
|
+
|
|
|
|
+ let subscribe2 = set2
|
|
|
|
+ .iter()
|
|
|
|
+ .map(|(connection, _)| relayer.process_request_from_client(connection, req2.clone()))
|
|
|
|
+ .collect::<Vec<_>>();
|
|
|
|
+
|
|
|
|
+ assert_eq!(relayer.total_subscribers(), 0);
|
|
|
|
+
|
|
|
|
+ join_all(subscribe1)
|
|
|
|
+ .await
|
|
|
|
+ .into_iter()
|
|
|
|
+ .collect::<Result<Vec<_>, _>>()
|
|
|
|
+ .expect("valid calls");
|
|
|
|
+ join_all(subscribe2)
|
|
|
|
+ .await
|
|
|
|
+ .into_iter()
|
|
|
|
+ .collect::<Result<Vec<_>, _>>()
|
|
|
|
+ .expect("valid calls");
|
|
|
|
+
|
|
|
|
+ for (_, recv) in set1.iter_mut() {
|
|
|
|
+ assert!(recv
|
|
|
|
+ .try_recv()
|
|
|
|
+ .expect("end of stored events")
|
|
|
|
+ .as_end_of_stored_events()
|
|
|
|
+ .is_some());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for (_, recv) in set2.iter_mut() {
|
|
|
|
+ assert!(recv
|
|
|
|
+ .try_recv()
|
|
|
|
+ .expect("end of stored events")
|
|
|
|
+ .as_end_of_stored_events()
|
|
|
|
+ .is_some());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ assert_eq!(relayer.total_subscribers(), 1100);
|
|
|
|
+
|
|
|
|
+ relayer
|
|
|
|
+ .process_request_from_client(&publisher, get_note())
|
|
|
|
+ .await
|
|
|
|
+ .expect("process event");
|
|
|
|
+
|
|
|
|
+ sleep(Duration::from_millis(10)).await;
|
|
|
|
+
|
|
|
|
+ for (_, recv) in set1.iter_mut() {
|
|
|
|
+ assert!(recv.try_recv().is_err());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for (_, recv) in set2.iter_mut() {
|
|
|
|
+ let msg = recv.try_recv();
|
|
|
|
+ assert!(msg.is_ok());
|
|
|
|
+ let msg = msg.expect("msg");
|
|
|
|
+
|
|
|
|
+ assert_eq!(
|
|
|
|
+ msg.as_event().expect("valid").subscription_id.to_string(),
|
|
|
|
+ "1298169700973717".to_owned()
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ assert!(recv.try_recv().is_err());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ drop(set1);
|
|
|
|
+ sleep(Duration::from_millis(10)).await;
|
|
|
|
+ assert_eq!(relayer.total_subscribers(), 100);
|
|
|
|
+
|
|
|
|
+ drop(set2);
|
|
|
|
+ sleep(Duration::from_millis(10)).await;
|
|
|
|
+ assert_eq!(relayer.total_subscribers(), 0);
|
|
|
|
+
|
|
|
|
+ drop(relayer);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ #[tokio::test]
|
|
|
|
+ async fn subscribe_to_all() {
|
|
|
|
+ let request: Request =
|
|
|
|
+ serde_json::from_value(json!(["REQ", "1298169700973717", {}])).expect("valid object");
|
|
|
|
+
|
|
|
|
+ let relayer = Relayer::new(Some(get_db(false).await), None).expect("valid relayer");
|
|
|
|
+ let (connection, mut recv) = Connection::new_for_test();
|
|
|
|
+
|
|
|
|
+ assert_eq!(relayer.total_subscribers(), 0);
|
|
|
|
+ let _ = relayer
|
|
|
|
+ .process_request_from_client(&connection, request)
|
|
|
|
+ .await;
|
|
|
|
+
|
|
|
|
+ assert_eq!(relayer.total_subscribers(), 1);
|
|
|
|
+
|
|
|
|
+ // eod
|
|
|
|
+ assert!(recv
|
|
|
|
+ .try_recv()
|
|
|
|
+ .expect("valid")
|
|
|
|
+ .as_end_of_stored_events()
|
|
|
|
+ .is_some());
|
|
|
|
+
|
|
|
|
+ // It is empty
|
|
|
|
+ assert!(recv.try_recv().is_err());
|
|
|
|
+
|
|
|
|
+ relayer
|
|
|
|
+ .process_request_from_client(&connection, get_note())
|
|
|
|
+ .await
|
|
|
|
+ .expect("process event");
|
|
|
|
+
|
|
|
|
+ sleep(Duration::from_millis(10)).await;
|
|
|
|
+
|
|
|
|
+ // It is not empty
|
|
|
|
+ let msg = recv.try_recv();
|
|
|
|
+ assert!(msg.is_ok());
|
|
|
|
+ assert_eq!(
|
|
|
|
+ msg.expect("is ok")
|
|
|
|
+ .as_event()
|
|
|
|
+ .expect("valid")
|
|
|
|
+ .subscription_id
|
|
|
|
+ .to_string(),
|
|
|
|
+ "1298169700973717".to_owned()
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ // it must be deliverd at most once
|
|
|
|
+ assert!(recv.try_recv().is_err());
|
|
|
|
+ assert_eq!(relayer.total_subscribers(), 1);
|
|
|
|
+
|
|
|
|
+ // when client is dropped, the subscription is removed
|
|
|
|
+ // automatically
|
|
|
|
+ drop(connection);
|
|
|
|
+
|
|
|
|
+ sleep(Duration::from_millis(10)).await;
|
|
|
|
+
|
|
|
|
+ assert_eq!(relayer.total_subscribers(), 0);
|
|
}
|
|
}
|
|
}
|
|
}
|