Crate netty_rs[−][src]
Netty-rs allows exposes a simple-to-use API used to create stateful application level network protocols as both a client or server.
Netty-rs allows requires consumers specify how to handle messages in different circumstances. Whenever specifying this the same API is used by using Connection. This very simple API allows consumers to specify restful protocols of varying complexity. Each message-and-reply chain is in their own channel which does not and is not impacted by messages sent or received in other message-and-reply chains.
The situations where how to handle messages need to be specified are:
- When a consumer sends a message it can choose to wait for a reply and handle it in a custom way.
- When acting as a server consumers need to specify how to handshake with new connections, which allows custom authentication of clients among any other handshake related action.
- When acting as a server consumers need to specify how to handle non-reply messages from connections that have already been authenticated.
The main API is accessed through the Networker struct.
Netty-rs uses the DirectoryService trait in order to allow consumers to either implement
their own directory service for example a DNS or use the SimpleDirectoryService
struct that
implements this trait.
Example
fn generate_challenge() -> Vec<u8> { // Generate the challenge ... } fn verify_challenge(a: &Vec<u8>) -> bool { // Verify challenge answer ... } fn sign(c: &Vec<u8>) -> Vec<u8> { // Sign the challenge } // Enum for the different types of messages we want to send #[derive(Clone, Serialize, Deserialize, Debug, Eq, PartialEq)] enum Content { Init, Challenge(Vec<u8>), Answer(Vec<u8>), Accept, Deny, Request, Response(i32), ProtocolError, } let ds = SimpleDirectoryService::new(); let networker = Networker::new("127.0.0.1:8080".parse().unwrap(), ds, |handshake_msg: NetworkMessage<Content>, mut con: Connection<Content, MyError>| async move { // Perhaps you authenticate by producing a challenge and then // waiting for a response let challenge = generate_challenge(); let message = handshake_msg.reply(Content::Challenge(challenge)); let timeout = Duration::from_secs(2); // On timeout or other errors we just abort this whole process let response = con.send_message_await_reply(message, Some(timeout)).await?; if let Content::Answer(a) = &response.content { if verify_challenge(a) { let accept_msg = response.reply(Content::Accept); con.send_message(accept_msg).await?; } else { let deny_msg = response.reply(Content::Deny); con.send_message(deny_msg).await?; } } else { let deny_msg = response.reply(Content::Deny); con.send_message(deny_msg).await?; } // Return the id of this client let inner = Arc::try_unwrap(handshake_msg.from).unwrap_or_else(|e| (*e).clone()); Ok(inner) }, |message: NetworkMessage<Content>, mut con: Connection<Content, MyError>| async move { if let Content::Request = message.content { // Respond with the magical number for the meaning of life let response = message.reply(Content::Response(42)); con.send_message(response).await?; } else { let response = message.reply(Content::ProtocolError); con.send_message(response).await?; } Ok(()) }).await.map_err(|_| MyError)?; networker.listen(true).await.map_err(|_| MyError)?; // Send a message to ourselves let first_message = NetworkMessage::new( Arc::new("127.0.0.1:8080".to_string()), Arc::new("127.0.0.1:8080".to_string()), Content::Init, ); let timeout = Duration::from_secs(2); let action = Action::new( |msg: NetworkMessage<Content>, mut con: Connection<Content, MyError>| { async move { if let Content::Challenge(c) = &msg.content { let answer = sign(c); let resp = msg.reply(Content::Answer(answer)); let timeout = Duration::from_secs(2); let accept = con.send_message_await_reply(resp, Some(timeout)).await?; if let Content::Accept = accept.content { Ok(()) } else { Err(MyError.into()) } } else { Err(MyError.into()) } } .boxed() }, ); networker .send_message(first_message, Some(timeout), Some(action)) .await.map_err(|_| MyError)?; Result::<(), MyError>::Ok(())
Structs
Action | Action contains a closure that handles the communication on a channel |
Connection | A connection serves as the main API to specify how a network conversation should look. It
allows sending messages to the recipiant and awaiting their response using the two methods
|
Error | Error returned by netty-rs |
NetworkMessage | This struct represents the network messages sent and received, it can be created either from
the new |
Networker | This struct is the main API for using netty. It allows the creation of a server and the ability to send messages to clients. |
SimpleDirectoryService | Directory service that translates Strings and socket addresses to socket addresses Strings have to be in the format of “127.0.0.1:8080” |
Traits
DirectoryService | Netty-rs uses a pluggable directory service to translate from an id to an IP address and port number. This is provided via this trait. A SimpleDirectoryService struct is provided which translates from any type that implements ToSocketAddrs. This includes strings which are in the format of for example “127.0.0.1:8080”, which will allow consumers to easily use strings or IP addresses as identifiers. If a more complicated lookup is required then implementing this trait is always avaliable. |
HandlerError | Marker trait for errors that are returned by the handlers |
NetworkContent | Marker trait for the content contained in the NetworkMessage struct |