| @ -0,0 +1,127 @@ | |||||
| use std::net::{SocketAddr, SocketAddrV4}; | |||||
| use anyhow::bail; | |||||
| use solstice_proto::server::PeerAddressResponse; | |||||
| use crate::context::Context; | |||||
| use crate::message_handler::MessageHandler; | |||||
| use crate::peer::PeerState; | |||||
| #[derive(Debug, Default)] | |||||
| pub struct PeerAddressResponseHandler; | |||||
| impl MessageHandler for PeerAddressResponseHandler { | |||||
| type Message = PeerAddressResponse; | |||||
| fn run( | |||||
| self, | |||||
| context: &mut Context, | |||||
| response: &PeerAddressResponse, | |||||
| ) -> anyhow::Result<()> { | |||||
| match context.state.peers.peers.get_mut(&response.user_name) { | |||||
| Some(state @ &mut PeerState::FetchingAddress) => { | |||||
| *state = PeerState::Opening { | |||||
| address: SocketAddr::V4(SocketAddrV4::new( | |||||
| response.ip, | |||||
| response.port, | |||||
| )), | |||||
| }; | |||||
| } | |||||
| entry => bail!("unexpected peer state entry: {:?}", entry), | |||||
| }; | |||||
| // TODO: try opening a connection to the peer. Queue an event that will be | |||||
| // handled later if that fails. | |||||
| // | |||||
| // Question: should dispatcher_tx be added to Context? Otherwise, how would | |||||
| // the asynchronous open task notify the context it is done? If we add it, | |||||
| // then how does the dispatcher notice it should stop running? | |||||
| Ok(()) | |||||
| } | |||||
| fn name() -> String { | |||||
| "PeerAddressResponseHandler".to_string() | |||||
| } | |||||
| } | |||||
| #[cfg(test)] | |||||
| mod tests { | |||||
| use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; | |||||
| use solstice_proto::server::PeerAddressResponse; | |||||
| use crate::context::{ContextBundle, ContextOptions}; | |||||
| use crate::message_handler::MessageHandler; | |||||
| use crate::peer::PeerState; | |||||
| use super::PeerAddressResponseHandler; | |||||
| #[test] | |||||
| fn run_missing_state() { | |||||
| let mut options = ContextOptions::default(); | |||||
| options.initial_state.peers.peers.insert( | |||||
| "aisha".to_string(), | |||||
| PeerState::Opening { | |||||
| address: "1.2.3.4:42".parse().unwrap(), | |||||
| }, | |||||
| ); | |||||
| let mut bundle = ContextBundle::new(options); | |||||
| let response = PeerAddressResponse { | |||||
| user_name: "aisha".to_string(), | |||||
| ip: Ipv4Addr::new(1, 2, 3, 4), | |||||
| port: 42, | |||||
| }; | |||||
| PeerAddressResponseHandler::default() | |||||
| .run(&mut bundle.context, &response) | |||||
| .unwrap_err(); | |||||
| } | |||||
| #[test] | |||||
| fn run_wrong_state() { | |||||
| let mut bundle = ContextBundle::default(); | |||||
| let response = PeerAddressResponse { | |||||
| user_name: "aisha".to_string(), | |||||
| ip: Ipv4Addr::new(1, 2, 3, 4), | |||||
| port: 42, | |||||
| }; | |||||
| PeerAddressResponseHandler::default() | |||||
| .run(&mut bundle.context, &response) | |||||
| .unwrap_err(); | |||||
| } | |||||
| #[test] | |||||
| fn run_success() { | |||||
| let mut options = ContextOptions::default(); | |||||
| options | |||||
| .initial_state | |||||
| .peers | |||||
| .peers | |||||
| .insert("aisha".to_string(), PeerState::FetchingAddress); | |||||
| let mut bundle = ContextBundle::new(options); | |||||
| let response = PeerAddressResponse { | |||||
| user_name: "aisha".to_string(), | |||||
| ip: Ipv4Addr::new(1, 2, 3, 4), | |||||
| port: 42, | |||||
| }; | |||||
| PeerAddressResponseHandler::default() | |||||
| .run(&mut bundle.context, &response) | |||||
| .unwrap(); | |||||
| match bundle.context.state.peers.peers.get("aisha") { | |||||
| Some(PeerState::Opening { address }) => assert_eq!( | |||||
| address, | |||||
| &SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 42)) | |||||
| ), | |||||
| entry => panic!("unexpected peer state entry: {:?}", entry), | |||||
| }; | |||||
| } | |||||
| } | |||||