rotor_stream/
protocol.rs

1use std::io;
2use std::error::Error;
3use rotor::Scope;
4
5use {Transport, Intent, StreamSocket};
6
7quick_error!{
8    /// An exception value that is received in `Protocol::exception` method
9    ///
10    /// This thing is usually used for nice error detection. But sometimes it
11    /// is also useful for valid protocol processing. For example it allows
12    /// to detect end-of-stream-delimited prootols (of those which make of
13    /// use of TCP half close)
14    #[derive(Debug)]
15    pub enum Exception {
16        /// End of stream reached (when reading)
17        ///
18        /// This may be not a broken expectation, we just notify of end of
19        /// stream always (if the state machine is still alive)
20        ///
21        /// Note: the equivalent of end of stream for write system call is
22        /// translated to `WriteError(WriteZero)`
23        EndOfStream {
24            description("end of stream reached")
25        }
26        /// Limit for the number of bytes reached
27        ///
28        /// This is called when there is alredy maximum bytes in the buffer
29        /// (third argument of `Delimiter`) but no delimiter found.
30        LimitReached {
31            description("reached the limit of bytes buffered")
32        }
33        ReadError(err: io::Error) {
34            description("error when reading from stream")
35            display("read error: {}", err)
36        }
37        WriteError(err: io::Error) {
38            description("error when writing to stream")
39            display("write error: {}", err)
40        }
41        ConnectError(err: io::Error) {
42            description("error when connecting to an address")
43            display("connection error: {}", err)
44        }
45    }
46}
47
48
49/// This is an enumeration used to declare what next protocol is expecting
50///
51/// The value is used in `Intent::expect()`.
52///
53/// Most users should use `IntentBuilder`'s (a type which is returned from
54/// `Intent::of(..)`) methods. But for some kinds of control flow being
55/// able to specify expectation as a separate enum is very useful.
56// #[derive(Clone, Clone)]
57// This could be Copy, but I think it could be implemented efficient enough
58// without Copy and Clone. Probably we will enable them for the user code later
59#[derive(Debug)]
60pub enum Expectation {
61    /// Read number of bytes
62    ///
63    /// The buffer that is passed to bytes_read might contain more bytes, but
64    /// `num` parameter of the `bytes_read()` method will contain a number of
65    /// bytes passed into `Bytes` constructor.
66    ///
67    /// Note that bytes passed here is neither limit on bytes actually read
68    /// from the network (we resize buffer as convenient for memory allocator
69    /// and read as much as possible), nor is the preallocated buffer size
70    /// (we don't preallocate the buffer to be less vulnerable to DoS attacks).
71    ///
72    /// Note that real number of bytes that `netbuf::Buf` might contain is less
73    /// than 4Gb. So this value can't be as big as `usize::MAX`
74    Bytes(usize),
75    /// Read until delimiter
76    ///
77    /// Parameters: `offset`, `delimiter`, `max_bytes`
78    ///
79    /// Only static strings are supported for delimiter now.
80    ///
81    /// `bytes_read` action gets passed `num` bytes before the delimeter, or
82    /// in other words, the position of the delimiter in the buffer.
83    /// The delimiter is guaranteed to be in the buffer too. The `max_bytes`
84    /// do include the offset itself.
85    ///
86    Delimiter(usize, &'static [u8], usize),
87    /// Wait until no more than N bytes is in output buffer
88    ///
89    /// This is going to be used for several cases:
90    ///
91    /// 1. `Flush(0)` before closing the connection
92    /// 2. `Flush(0)` to before receiving new request (if needed)
93    /// 3. `Flush(N)` to wait when you can continue producing some data, this
94    ///    allows TCP pushback. To be able not to put everything in output
95    ///    buffer at once. Still probably more efficient than `Flush(0)`
96    Flush(usize),
97    /// Wait until deadline
98    ///
99    /// This useful for two cases:
100    ///
101    /// 1. Just wait before doing anything if required by business logic
102    /// 2. Wait until `wakeup` happens or atimeout whatever comes first
103    Sleep,
104}
105
106pub trait Protocol: Sized {
107    type Context;
108    type Socket: StreamSocket;
109    type Seed;
110    /// Starting the protocol (e.g. accepted a socket)
111    // TODO(tailhook) transport be here instead of sock?
112    fn create(seed: Self::Seed, sock: &mut Self::Socket,
113        scope: &mut Scope<Self::Context>)
114        -> Intent<Self>;
115
116    /// The action WaitBytes or WaitDelimiter is complete
117    ///
118    /// Note you don't have to consume input buffer. The data is in the
119    /// transport, but you are free to ignore it. This may be useful for
120    /// example to yield `Bytes(4)` to read the header size and then yield
121    /// bigger value to read the whole header at once. But be careful, if
122    /// you don't consume bytes you will repeatedly receive them again.
123    fn bytes_read(self, transport: &mut Transport<Self::Socket>,
124                  end: usize, scope: &mut Scope<Self::Context>)
125        -> Intent<Self>;
126
127    /// The action Flush is complete
128    fn bytes_flushed(self, transport: &mut Transport<Self::Socket>,
129                     scope: &mut Scope<Self::Context>)
130        -> Intent<Self>;
131
132    /// Timeout happened, which means either deadline reached in
133    /// Bytes, Delimiter, Flush. Or Sleep has passed.
134    fn timeout(self, transport: &mut Transport<Self::Socket>,
135        scope: &mut Scope<Self::Context>)
136        -> Intent<Self>;
137
138    /// The method is called when too much bytes are read but no delimiter
139    /// is found within the number of bytes specified. Or end of stream reached
140    ///
141    /// The usual case is to just close the connection (because it's probably
142    /// DoS attack is going on or the protocol mismatch), but sometimes you
143    /// want to send error code, like 413 Entity Too Large for HTTP.
144    ///
145    /// Note it's your responsibility to wait for the buffer to be flushed.
146    /// If you write to the buffer and then return Intent::done() immediately,
147    /// your data will be silently discarded.
148    ///
149    /// The `WriteError` and `ConnectError` are never passed here but passed
150    /// into `fatal` handler instead.
151    fn exception(self, _transport: &mut Transport<Self::Socket>,
152        reason: Exception, _scope: &mut Scope<Self::Context>)
153        -> Intent<Self>;
154
155    /// This method is called on fatal errors of the connection
156    ///
157    /// Connection can't proceed after this method is called
158    ///
159    /// Note: we use shared `Exception` type for both exception and fatal
160    /// exceptions. This method receives ``WriteError`` and ``ConnectError``
161    /// options only.
162    fn fatal(self, reason: Exception, scope: &mut Scope<Self::Context>)
163        -> Option<Box<Error>>;
164
165    /// Message received (from the main loop)
166    fn wakeup(self, transport: &mut Transport<Self::Socket>,
167        scope: &mut Scope<Self::Context>)
168        -> Intent<Self>;
169}