From 2f9fce92aef1206636f2eec3015e211893d8abc6 Mon Sep 17 00:00:00 2001 From: Titouan Rigoudy Date: Mon, 11 Oct 2021 17:00:31 +0200 Subject: [PATCH] Introduce simpler RequestHandler trait. --- proto/src/server/testing.rs | 185 +++++++++++++++++++++++------------- 1 file changed, 117 insertions(+), 68 deletions(-) diff --git a/proto/src/server/testing.rs b/proto/src/server/testing.rs index b273b2d..f332edd 100644 --- a/proto/src/server/testing.rs +++ b/proto/src/server/testing.rs @@ -20,23 +20,6 @@ use crate::server::{ UserStatusResponse, }; -#[derive(Debug, Default)] -pub struct UserStatusMap { - map: HashMap, -} - -// IDEA: impl FromIterator for UserStatusMap. - -impl UserStatusMap { - pub fn insert(&mut self, response: UserStatusResponse) { - self.map.insert(response.user_name.clone(), response); - } - - pub fn get(&self, user_name: &str) -> Option { - self.map.get(user_name).map(|response| response.clone()) - } -} - /// A handler for a single client connected to the server. pub trait ClientHandler: Send { /// Runs this handler against the given incoming stream of requests. @@ -56,70 +39,36 @@ pub trait ClientHandlerFactory { fn make(&self) -> Self::Handler; } -/// A handler that can serve responses to user status requests. -/// Responses are sent only for users who appear in `user_status_map`. -/// All other requests are ignored. -#[derive(Clone, Default)] -pub struct UserStatusHandler { - user_status_map: Arc>, +/// A handler for a simple request/response protocol. +pub trait RequestHandler { + /// Returns a response if one should be sent back, `None` otherwise. + fn handle( + &mut self, + request: &ServerRequest, + ) -> anyhow::Result>; } -impl ClientHandler for UserStatusHandler { +/// Blanket implementation of `ClientHandler` for `RequestHandler` types. +/// Simply handles each incoming request in turn and sends responses back. +impl ClientHandler for H { fn run( - self, + mut self, mut request_rx: mpsc::Receiver, response_tx: mpsc::Sender, ) -> anyhow::Result<()> { while let Some(request) = request_rx.blocking_recv() { - match request { - ServerRequest::UserStatusRequest(UserStatusRequest { user_name }) => { - let entry = self.user_status_map.lock().get(&user_name); - if let Some(response) = entry { - response_tx - .blocking_send(ServerResponse::UserStatusResponse(response)) - .context("sending response")?; - } else { - warn!("Received UserStatusRequest for unknown user {}", user_name); - } - } - _ => { - warn!("Unhandled request: {:?}", request); - } + let optional_response = + self.handle(&request).context("handling request")?; + if let Some(response) = optional_response { + response_tx + .blocking_send(response) + .context("sending response")?; } } Ok(()) } } -/// A factory for `UserStatusHandler`. All handlers share `user_status_map`. -#[derive(Default)] -pub struct UserStatusHandlerFactory { - /// The status map from which responses are served. - /// - /// Testing code may wish to retain a copy of this in order to mutate the map - /// concurrently with requests being handled. - pub user_status_map: Arc>, -} - -impl ClientHandlerFactory for UserStatusHandlerFactory { - type Handler = UserStatusHandler; - - fn make(&self) -> Self::Handler { - Self::Handler { - user_status_map: self.user_status_map.clone(), - } - } -} - -impl UserStatusHandlerFactory { - /// Convenience function to create a new factory wrapping the given `map`. - pub fn new(map: UserStatusMap) -> Self { - Self { - user_status_map: Arc::new(Mutex::new(map)), - } - } -} - /// Handles the login exchange, then hands off the connection to `inner`. struct LoginHandler { reader: FrameReader, @@ -400,6 +349,106 @@ impl Server { } } +// LOG HANDLER + +/// A `ClientHandler` that does nothing but log incoming requests. +pub struct LogHandler; + +impl RequestHandler for LogHandler { + fn handle( + &mut self, + request: &ServerRequest, + ) -> anyhow::Result> { + info!("Received request: {:?}", request); + Ok(None) + } +} + +// USER STATUS HANDLER + +/// A map of user names to user status responses to serve. +#[derive(Debug, Default)] +pub struct UserStatusMap { + map: HashMap, +} + +// IDEA: impl FromIterator for UserStatusMap. + +impl UserStatusMap { + /// Inserts the given `response` in this map. + /// Overwrites the previous entry for the same user name, if any. + pub fn insert(&mut self, response: UserStatusResponse) { + self.map.insert(response.user_name.clone(), response); + } + + /// Returns a clone of the response for the given user name. + pub fn get(&self, user_name: &str) -> Option { + self.map.get(user_name).map(|response| response.clone()) + } +} + +/// A handler that can serve responses to user status requests. +/// Responses are sent only for users who appear in `user_status_map`. +/// All other requests are ignored. +#[derive(Clone, Default)] +pub struct UserStatusHandler { + user_status_map: Arc>, +} + +impl RequestHandler for UserStatusHandler { + fn handle( + &mut self, + request: &ServerRequest, + ) -> anyhow::Result> { + let user_name = match request { + ServerRequest::UserStatusRequest(UserStatusRequest { ref user_name }) => { + user_name + } + _ => { + warn!("Unhandled request: {:?}", request); + return Ok(None); + } + }; + + let entry = self.user_status_map.lock().get(user_name); + if let Some(response) = entry { + Ok(Some(ServerResponse::UserStatusResponse(response))) + } else { + warn!("Received UserStatusRequest for unknown user {}", user_name); + Ok(None) + } + } +} + +/// A factory for `UserStatusHandler`. All handlers share `user_status_map`. +#[derive(Default)] +pub struct UserStatusHandlerFactory { + /// The status map from which responses are served. + /// + /// Testing code may wish to retain a copy of this in order to mutate the map + /// concurrently with requests being handled. + pub user_status_map: Arc>, +} + +impl ClientHandlerFactory for UserStatusHandlerFactory { + type Handler = UserStatusHandler; + + fn make(&self) -> Self::Handler { + Self::Handler { + user_status_map: self.user_status_map.clone(), + } + } +} + +impl UserStatusHandlerFactory { + /// Convenience function to create a new factory wrapping the given `map`. + pub fn new(map: UserStatusMap) -> Self { + Self { + user_status_map: Arc::new(Mutex::new(map)), + } + } +} + #[cfg(test)] mod tests { use tokio::net::TcpStream;