retty/
lib.rs

1//! ### What is Retty?
2//! Retty is an asynchronous Rust networking framework that makes it easy to build protocols, application clients/servers.
3//!
4//! It's like [Netty](https://netty.io) or [Wangle](https://github.com/facebook/wangle), but in Rust.
5//!
6//! ### What is a Pipeline?
7//! The fundamental abstraction of Retty is the [Pipeline](crate::channel::Pipeline).
8//! It offers immense flexibility to customize how requests and responses are handled by your service.
9//! Once you have fully understood this abstraction,
10//! you will be able to write all sorts of sophisticated protocols, application clients/servers.
11//!  
12//! A [Pipeline](crate::channel::Pipeline) is a chain of request/response [handlers](crate::channel::Handler) that handle inbound request and
13//! outbound response. Once you chain handlers together, it provides an agile way to convert
14//! a raw data stream into the desired message type and the inverse -- desired message type to raw data stream.
15//! Pipeline implements an advanced form of the Intercepting Filter pattern to give a user full control
16//! over how an event is handled and how the handlers in a pipeline interact with each other.
17//!
18//! A [Handler](crate::channel::Handler) should do one and only one function - just like the UNIX philosophy. If you have a handler that
19//! is doing more than one function than you should split it into individual handlers. This is really important for
20//! maintainability and flexibility as its common to change your protocol for one reason or the other.
21//!
22//! ### How does an event flow in a Pipeline?
23//! ```text
24//!                                                       | write()
25//!   +---------------------------------------------------+---------------+
26//!   |                             Pipeline              |               |
27//!   |                                                  \|/              |
28//!   |    +----------+----------+------------+-----------+----------+    |
29//!   |    |                       Handler  N                        |    |
30//!   |    +----------+----------+------------+-----------+----------+    |
31//!   |              /|\                                  |               |
32//!   |               |                                   |               |
33//!   |               |                                   |               |
34//!   |               |                                  \|/              |
35//!   |    +----------+----------+------------+-----------+----------+    |
36//!   |    |                       Handler N-1                       |    |
37//!   |    +----------+----------+------------+-----------+----------+    |
38//!   |              /|\                                  |               |
39//!   |               |                                   |               |
40//!   |               |                         Context.fire_poll_write() |
41//!   |               |                                   |               |
42//!   |               |                                   |               |
43//!   |          Context.fire_read()                      |               |
44//!   |               |                                   |               |
45//!   |               |                                  \|/              |
46//!   |    +----------+----------+------------+-----------+----------+    |
47//!   |    |                       Handler  2                        |    |
48//!   |    +----------+----------+------------+-----------+----------+    |
49//!   |              /|\                                  |               |
50//!   |               |                                   |               |
51//!   |               |                                   |               |
52//!   |               |                                  \|/              |
53//!   |    +----------+----------+------------+-----------+----------+    |
54//!   |    |                       Handler  1                        |    |
55//!   |    +----------+----------+------------+-----------+----------+    |
56//!   |              /|\                                  |               |
57//!   +---------------+-----------------------------------+---------------+
58//!                   | read()                            | poll_write()
59//!                   |                                  \|/
60//!   +---------------+-----------------------------------+---------------+
61//!   |               |                                   |               |
62//!   |        Internal I/O Threads (Transport Implementation)            |
63//!   +-------------------------------------------------------------------+
64//! ```
65//!
66//! ### Echo Server Example
67//! Let's look at how to write an echo server.
68//!
69//! Here's the main piece of code in our echo server; it receives a string from inbound direction in the pipeline,
70//! prints it to stdout and sends it back to outbound direction in the pipeline. It's really important to add the
71//! line delimiter because our pipeline will use a line decoder.
72//! ```ignore
73//! struct EchoServerHandler {
74//!     transmits: VecDeque<TaggedString>,
75//! }
76//!
77//! impl Handler for EchoServerHandler {
78//!     type Rin = TaggedString;
79//!     type Rout = Self::Rin;
80//!     type Win = TaggedString;
81//!     type Wout = Self::Win;
82//!
83//!     fn name(&self) -> &str {
84//!         "EchoServerHandler"
85//!     }
86//!
87//!     fn handle_read(
88//!         &mut self,
89//!         _ctx: &Context<Self::Rin, Self::Rout, Self::Win, Self::Wout>,
90//!         msg: Self::Rin,
91//!     ) {
92//!         println!("handling {}", msg.message);
93//!         self.transmits.push_back(TaggedString {
94//!             now: Instant::now(),
95//!             transport: msg.transport,
96//!             message: format!("{}\r\n", msg.message),
97//!         });
98//!     }
99//!
100//!     fn poll_write(
101//!         &mut self,
102//!         ctx: &Context<Self::Rin, Self::Rout, Self::Win, Self::Wout>,
103//!     ) -> Option<Self::Wout> {
104//!         if let Some(msg) = ctx.fire_poll_write() {
105//!             self.transmits.push_back(msg);
106//!         }
107//!         self.transmits.pop_front()
108//!     }
109//! }
110//! ```
111//!
112//! This needs to be the final handler in the pipeline. Now the definition of the pipeline is needed to handle the requests and responses.
113//! ```ignore
114//! let mut bootstrap = BootstrapServerTcp::new();
115//! bootstrap.pipeline(Box::new(move || {
116//!     let pipeline: Pipeline<TaggedBytesMut, TaggedString> = Pipeline::new();
117//!
118//!     let line_based_frame_decoder_handler = TaggedByteToMessageCodec::new(Box::new(
119//!         LineBasedFrameDecoder::new(8192, true, TerminatorType::BOTH),
120//!     ));
121//!     let string_codec_handler = TaggedStringCodec::new();
122//!     let echo_server_handler = EchoServerHandler::new();
123//!
124//!     pipeline.add_back(line_based_frame_decoder_handler);
125//!     pipeline.add_back(string_codec_handler);
126//!     pipeline.add_back(echo_server_handler);
127//!     pipeline.finalize()
128//! }));
129//! ```
130//!
131//! It is very important to be strict in the order of insertion as they are ordered by insertion. The pipeline has 4 handlers:
132//!
133//! * [TaggedByteToMessageCodec](crate::codec::byte_to_message_decoder::TaggedByteToMessageCodec)
134//!     * Inbound: receives a zero-copy byte buffer and splits on line-endings
135//!     * Outbound: just passes the byte buffer to AsyncTransport
136//! * [TaggedStringCodec](crate::codec::string_codec::TaggedStringCodec)
137//!     * Inbound: receives a byte buffer and decodes it into a std::string and pass up to the EchoHandler.
138//!     * Outbound: receives a std::string and encodes it into a byte buffer and pass down to the TaggedByteToMessageCodec.
139//! * EchoHandler
140//!     * Inbound: receives a std::string and writes it to the pipeline, which will send the message outbound.
141//!     * Outbound: receives a std::string and forwards it to TaggedStringCodec.
142//!
143//! Now that all needs to be done is plug the pipeline factory into a [BootstrapServerTcp](crate::bootstrap::BootstrapTcpServer) and that’s pretty much it.
144//! Bind a local host:port and wait for it to stop.
145//!
146//! ```ignore
147//! bootstrap.bind(format!("{}:{}", host, port)).await?;
148//!
149//! println!("Press ctrl-c to stop");
150//! tokio::select! {
151//!     _ = tokio::signal::ctrl_c() => {
152//!         bootstrap.graceful_stop().await;
153//!     }
154//! };
155//! ```
156//!
157//! ### Echo Client Example
158//! The code for the echo client is very similar to the Echo Server. Here is the main echo handler.
159//!
160//! ```ignore
161//! struct EchoClientHandler;
162//!
163//! impl Handler for EchoClientHandler {
164//!     type Rin = TaggedString;
165//!     type Rout = Self::Rin;
166//!     type Win = TaggedString;
167//!     type Wout = Self::Win;
168//!
169//!     fn name(&self) -> &str {
170//!         "EchoClientHandler"
171//!     }
172//!
173//!     fn handle_read(
174//!         &mut self,
175//!         _ctx: &Context<Self::Rin, Self::Rout, Self::Win, Self::Wout>,
176//!         msg: Self::Rin,
177//!     ) {
178//!         println!("received back: {}", msg.message);
179//!     }
180//!
181//!     fn read_exception(
182//!         &mut self,
183//!         ctx: &Context<Self::Rin, Self::Rout, Self::Win, Self::Wout>,
184//!         err: Box<dyn Error + Send + Sync>,
185//!     ) {
186//!         println!("received exception: {}", err);
187//!         ctx.fire_close();
188//!     }
189//!
190//!     fn read_eof(&mut self, ctx: &Context<Self::Rin, Self::Rout, Self::Win, Self::Wout>) {
191//!         println!("EOF received :(");
192//!         ctx.fire_close();
193//!     }
194//!
195//!     fn poll_write(
196//!         &mut self,
197//!         ctx: &Context<Self::Rin, Self::Rout, Self::Win, Self::Wout>,
198//!     ) -> Option<Self::Wout> {
199//!         ctx.fire_poll_write()
200//!     }
201//! }
202//! ```
203//!
204//! Notice that we override other methods—read_exception and read_eof.
205//! There are few other methods that can be overriden. If you need to handle a particular event,
206//! just override the corresponding method.
207//!
208//! Now onto the client’s pipeline factory. It is identical the server’s pipeline factory, which
209//! handles writing data.
210//! ```ignore
211//! let mut bootstrap = BootstrapClientTcp::new();
212//! bootstrap.pipeline(Box::new( move || {
213//!     let pipeline: Pipeline<TaggedBytesMut, TaggedString> = Pipeline::new();
214//!
215//!     let line_based_frame_decoder_handler = TaggedByteToMessageCodec::new(Box::new(
216//!         LineBasedFrameDecoder::new(8192, true, TerminatorType::BOTH),
217//!     ));
218//!     let string_codec_handler = TaggedStringCodec::new();
219//!     let echo_client_handler = EchoClientHandler::new();
220//!
221//!     pipeline.add_back(line_based_frame_decoder_handler);
222//!     pipeline.add_back(string_codec_handler);
223//!     pipeline.add_back(echo_client_handler);
224//!     pipeline.finalize()
225//! }));
226//! ```
227//!
228//! Now that all needs to be done is plug the pipeline factory into a BootstrapTcpClient and that’s pretty much it.
229//! Connect to the remote peer and then read line from stdin and write it to pipeline.
230//! ```ignore
231//! let pipeline = bootstrap.connect(format!("{}:{}", host, port)).await?;
232//!
233//! println!("Enter bye to stop");
234//! let mut buffer = String::new();
235//! while tokio::io::stdin().read_line(&mut buffer).await.is_ok() {
236//!     match buffer.trim_end() {
237//!         "" => break,
238//!         line => {
239//!             pipeline.write(TaggedString {
240//!                 now: Instant::now(),
241//!                 transport,
242//!                 message: format!("{}\r\n", line),
243//!             });
244//!             if line == "bye" {
245//!                 pipeline.close();
246//!                 break;
247//!             }
248//!         }
249//!     };
250//!     buffer.clear();
251//! }
252//!
253//! bootstrap.graceful_stop().await;
254//! ```
255#![doc(html_logo_url = "https://raw.githubusercontent.com/retty-io/retty/master/docs/retty.io.jpg")]
256#![warn(rust_2018_idioms)]
257#![allow(dead_code)]
258#![warn(missing_docs)]
259
260pub mod bootstrap;
261pub mod channel;
262pub mod codec;
263pub mod executor;
264pub mod transport;