| @ -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), | |||
| }; | |||
| } | |||
| } | |||