feat(protocol): interests, tickers, global room and cant-connect
Adds the server messages the client had no way to speak: user and room search (42/120), room tickers (113-116), the global room feed (150-152), interests and the recommendation queries built on them (51/52/54/56/57/ 110/111/112/117/118), a keepalive ping (32), gifted privileges (123), a password change (142) and multi-user private messages (149). CantConnectToPeer (1001) closes a real gap in both directions: a dial the server asked us to make now answers the waiting peer instead of falling back to brokering the same unreachable link the other way, and an incoming one ends the wait on downloads queued for that peer rather than letting them sit until a timeout.
This commit is contained in:
@@ -729,8 +729,20 @@ impl PeerActor {
|
||||
// unreachable (likely firewalled): signal a connect failure so the
|
||||
// client can fall back to server-brokered connect. Anything else is a
|
||||
// normal disconnect.
|
||||
// A dial the server asked for carries the token the far peer quoted;
|
||||
// it is what a CantConnectToPeer must echo back, so the peer stops
|
||||
// waiting for a connection that is never coming.
|
||||
let brokered_token = self
|
||||
.peer
|
||||
.read_safe()
|
||||
.ok()
|
||||
.and_then(|p| if p.brokered { p.token } else { None });
|
||||
let op = if self.outbound && !self.established {
|
||||
ClientOperation::PeerConnectFailed(self.id, username)
|
||||
ClientOperation::PeerConnectFailed(
|
||||
self.id,
|
||||
username,
|
||||
brokered_token,
|
||||
)
|
||||
} else {
|
||||
ClientOperation::PeerDisconnected(
|
||||
self.id,
|
||||
|
||||
@@ -50,7 +50,7 @@ fn a_timed_out_connect_parks_the_actor_in_disconnected() {
|
||||
"a timed-out connect must leave Connecting"
|
||||
);
|
||||
match rx.try_recv() {
|
||||
Ok(ClientOperation::PeerConnectFailed(7, username)) => {
|
||||
Ok(ClientOperation::PeerConnectFailed(7, username, _)) => {
|
||||
assert_eq!(username, "bob");
|
||||
}
|
||||
other => panic!("expected PeerConnectFailed, got {other:?}"),
|
||||
|
||||
@@ -427,7 +427,7 @@ mod tests {
|
||||
|
||||
// Play the client ops loop: take the terminal outcome, evict by id.
|
||||
match rx.recv_timeout(std::time::Duration::from_secs(10)) {
|
||||
Ok(ClientOperation::PeerConnectFailed(id, username)) => {
|
||||
Ok(ClientOperation::PeerConnectFailed(id, username, _)) => {
|
||||
assert_eq!(username, "ghost");
|
||||
if let Some(handle) = registry.remove_peer_if(&username, id) {
|
||||
let _ = handle.stop();
|
||||
|
||||
@@ -2,12 +2,14 @@ use crate::actor::{Actor, ActorHandle, ConnectionState};
|
||||
use crate::client::ClientOperation;
|
||||
use crate::dispatcher::MessageDispatcher;
|
||||
use crate::message::server::AdminMessageHandler;
|
||||
use crate::message::server::CantConnectToPeerHandler;
|
||||
use crate::message::server::CheckPrivilegesHandler;
|
||||
use crate::message::server::ConnectToPeerHandler;
|
||||
use crate::message::server::EmbeddedMessageHandler;
|
||||
use crate::message::server::ExcludedSearchPhrasesHandler;
|
||||
use crate::message::server::FileSearchHandler;
|
||||
use crate::message::server::GetPeerAddressHandler;
|
||||
use crate::message::server::GlobalRoomMessageHandler;
|
||||
use crate::message::server::JoinRoomHandler;
|
||||
use crate::message::server::LeaveRoomHandler;
|
||||
use crate::message::server::LoginHandler;
|
||||
@@ -27,13 +29,21 @@ use crate::message::server::WishListIntervalHandler;
|
||||
use crate::message::server::{
|
||||
GetUserStatsHandler, GetUserStatusHandler, RoomListHandler,
|
||||
};
|
||||
use crate::message::server::{
|
||||
GlobalRecommendationsHandler, ItemRecommendationsHandler,
|
||||
ItemSimilarUsersHandler, RecommendationsHandler, SimilarUsersHandler,
|
||||
UserInterestsHandler,
|
||||
};
|
||||
use crate::message::server::{
|
||||
RoomTickerAddedHandler, RoomTickerRemovedHandler, RoomTickersHandler,
|
||||
};
|
||||
use crate::message::{Handlers, MessageType};
|
||||
use crate::message::{Message, MessageReader};
|
||||
use crate::peer::ConnectionType;
|
||||
use crate::peer::Peer;
|
||||
use crate::types::{
|
||||
ClientVersion, RoomEvent, RoomInfo, RoomUserStats, SessionLoss,
|
||||
SessionWatch,
|
||||
ClientVersion, Recommendation, RoomEvent, RoomInfo, RoomUserStats,
|
||||
SessionLoss, SessionWatch, SimilarUser, UserInterests,
|
||||
};
|
||||
use crate::utils::lock::RwLockExt;
|
||||
|
||||
@@ -164,6 +174,59 @@ pub enum ServerMessage {
|
||||
PossibleParents(Vec<(String, String, u16)>),
|
||||
/// The server asks us to drop our parent and start over.
|
||||
ResetDistributed,
|
||||
/// The ticker board of a room we just joined (code 113).
|
||||
RoomTickers {
|
||||
room: String,
|
||||
tickers: Vec<crate::types::RoomTicker>,
|
||||
},
|
||||
/// One member set their ticker (code 114).
|
||||
RoomTickerAdded {
|
||||
room: String,
|
||||
username: String,
|
||||
ticker: String,
|
||||
},
|
||||
/// One member cleared their ticker (code 115).
|
||||
RoomTickerRemoved {
|
||||
room: String,
|
||||
username: String,
|
||||
},
|
||||
/// A message from the global room feed (code 152).
|
||||
GlobalRoomMessageReceived {
|
||||
room: String,
|
||||
username: String,
|
||||
message: String,
|
||||
},
|
||||
/// Recommendations from our own interests (code 54).
|
||||
RecommendationsReceived {
|
||||
recommended: Vec<Recommendation>,
|
||||
unrecommended: Vec<Recommendation>,
|
||||
},
|
||||
/// Server-wide recommendations (code 56).
|
||||
GlobalRecommendationsReceived {
|
||||
recommended: Vec<Recommendation>,
|
||||
unrecommended: Vec<Recommendation>,
|
||||
},
|
||||
/// Recommendations for one item (code 111).
|
||||
ItemRecommendationsReceived {
|
||||
item: String,
|
||||
recommendations: Vec<Recommendation>,
|
||||
},
|
||||
/// Users similar to us (code 110).
|
||||
SimilarUsersReceived {
|
||||
users: Vec<SimilarUser>,
|
||||
},
|
||||
/// Users who like one item (code 112).
|
||||
ItemSimilarUsersReceived {
|
||||
item: String,
|
||||
usernames: Vec<String>,
|
||||
},
|
||||
/// What another user likes and hates (code 57).
|
||||
UserInterestsReceived(UserInterests),
|
||||
/// A peer could not connect to us after we asked the server to broker
|
||||
/// (code 1001); the token is the one we quoted.
|
||||
CantConnectToPeer {
|
||||
token: u32,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct ServerActor {
|
||||
@@ -320,6 +383,17 @@ impl ServerActor {
|
||||
handlers.register_handler(FileSearchHandler);
|
||||
handlers.register_handler(GetPeerAddressHandler);
|
||||
handlers.register_handler(ConnectToPeerHandler);
|
||||
handlers.register_handler(CantConnectToPeerHandler);
|
||||
handlers.register_handler(RoomTickersHandler);
|
||||
handlers.register_handler(RoomTickerAddedHandler);
|
||||
handlers.register_handler(RoomTickerRemovedHandler);
|
||||
handlers.register_handler(GlobalRoomMessageHandler);
|
||||
handlers.register_handler(RecommendationsHandler);
|
||||
handlers.register_handler(GlobalRecommendationsHandler);
|
||||
handlers.register_handler(ItemRecommendationsHandler);
|
||||
handlers.register_handler(SimilarUsersHandler);
|
||||
handlers.register_handler(ItemSimilarUsersHandler);
|
||||
handlers.register_handler(UserInterestsHandler);
|
||||
|
||||
self.dispatcher = Some(MessageDispatcher::new(
|
||||
"server".into(),
|
||||
@@ -501,6 +575,37 @@ impl ServerActor {
|
||||
ServerMessage::RoomUserLeft { room, username } => {
|
||||
self.forward_room_event(RoomEvent::UserLeft { room, username });
|
||||
}
|
||||
ServerMessage::RoomTickers { room, tickers } => {
|
||||
self.forward_room_event(RoomEvent::Tickers { room, tickers });
|
||||
}
|
||||
ServerMessage::RoomTickerAdded {
|
||||
room,
|
||||
username,
|
||||
ticker,
|
||||
} => {
|
||||
self.forward_room_event(RoomEvent::TickerAdded {
|
||||
room,
|
||||
username,
|
||||
ticker,
|
||||
});
|
||||
}
|
||||
ServerMessage::RoomTickerRemoved { room, username } => {
|
||||
self.forward_room_event(RoomEvent::TickerRemoved {
|
||||
room,
|
||||
username,
|
||||
});
|
||||
}
|
||||
ServerMessage::GlobalRoomMessageReceived {
|
||||
room,
|
||||
username,
|
||||
message,
|
||||
} => {
|
||||
self.forward_room_event(RoomEvent::GlobalMessage {
|
||||
room,
|
||||
username,
|
||||
message,
|
||||
});
|
||||
}
|
||||
other => self.handle_standing_message(other),
|
||||
}
|
||||
}
|
||||
@@ -539,6 +644,54 @@ impl ServerActor {
|
||||
ServerMessage::CheckPrivileges => {
|
||||
self.queue_message(MessageFactory::build_check_privileges());
|
||||
}
|
||||
ServerMessage::RecommendationsReceived {
|
||||
recommended,
|
||||
unrecommended,
|
||||
} => {
|
||||
self.forward_to_client(ClientOperation::Recommendations {
|
||||
global: false,
|
||||
recommended,
|
||||
unrecommended,
|
||||
});
|
||||
}
|
||||
ServerMessage::GlobalRecommendationsReceived {
|
||||
recommended,
|
||||
unrecommended,
|
||||
} => {
|
||||
self.forward_to_client(ClientOperation::Recommendations {
|
||||
global: true,
|
||||
recommended,
|
||||
unrecommended,
|
||||
});
|
||||
}
|
||||
ServerMessage::ItemRecommendationsReceived {
|
||||
item,
|
||||
recommendations,
|
||||
} => {
|
||||
self.forward_to_client(ClientOperation::ItemRecommendations {
|
||||
item,
|
||||
recommendations,
|
||||
});
|
||||
}
|
||||
ServerMessage::SimilarUsersReceived { users } => {
|
||||
self.forward_to_client(ClientOperation::SimilarUsers(users));
|
||||
}
|
||||
ServerMessage::ItemSimilarUsersReceived { item, usernames } => {
|
||||
self.forward_to_client(ClientOperation::ItemSimilarUsers {
|
||||
item,
|
||||
usernames,
|
||||
});
|
||||
}
|
||||
ServerMessage::UserInterestsReceived(interests) => {
|
||||
self.forward_to_client(ClientOperation::UserInterests(
|
||||
interests,
|
||||
));
|
||||
}
|
||||
ServerMessage::CantConnectToPeer { token } => {
|
||||
self.forward_to_client(ClientOperation::CantConnectToPeer {
|
||||
token,
|
||||
});
|
||||
}
|
||||
other => {
|
||||
error!("[server] unroutable message: {:?}", other);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user