Skip to main content

actix_codec/
framed.rs

1use std::{
2    fmt, io,
3    pin::Pin,
4    task::{Context, Poll},
5};
6
7use bitflags::bitflags;
8use bytes::{Buf, BytesMut};
9use futures_core::{ready, Stream};
10use futures_sink::Sink;
11use pin_project_lite::pin_project;
12
13use crate::{AsyncRead, AsyncWrite, Decoder, Encoder};
14
15/// Low-water mark
16const LW: usize = 1024;
17/// High-water mark
18const HW: usize = 8 * 1024;
19
20bitflags! {
21    #[derive(Debug, Clone, Copy)]
22    struct Flags: u8 {
23        const EOF = 0b0001;
24        const READABLE = 0b0010;
25    }
26}
27
28pin_project! {
29    /// A unified `Stream` and `Sink` interface to an underlying I/O object, using the `Encoder` and
30    /// `Decoder` traits to encode and decode frames.
31    ///
32    /// Raw I/O objects work with byte sequences, but higher-level code usually wants to batch these
33    /// into meaningful chunks, called "frames". This method layers framing on top of an I/O object,
34    /// by using the `Encoder`/`Decoder` traits to handle encoding and decoding of message frames.
35    /// Note that the incoming and outgoing frame types may be distinct.
36    pub struct Framed<T, U> {
37        #[pin]
38        io: T,
39        codec: U,
40        flags: Flags,
41        read_buf: BytesMut,
42        write_buf: BytesMut,
43    }
44}
45
46impl<T, U> Framed<T, U> {
47    /// Creates a framed transport from an I/O object and a codec.
48    ///
49    /// The transport implements `Stream` when the I/O object implements `AsyncRead` and the codec
50    /// implements `Decoder`. It implements `Sink<I>` when the I/O object implements `AsyncWrite`
51    /// and the codec implements `Encoder<I>`. Read and write halves can therefore use separate
52    /// codecs.
53    pub fn new(io: T, codec: U) -> Framed<T, U> {
54        Framed {
55            io,
56            codec,
57            flags: Flags::empty(),
58            read_buf: BytesMut::with_capacity(HW),
59            write_buf: BytesMut::with_capacity(HW),
60        }
61    }
62}
63
64impl<T, U> Framed<T, U> {
65    /// Returns a reference to the underlying codec.
66    pub fn codec_ref(&self) -> &U {
67        &self.codec
68    }
69
70    /// Returns a mutable reference to the underlying codec.
71    pub fn codec_mut(&mut self) -> &mut U {
72        &mut self.codec
73    }
74
75    /// Returns a reference to the underlying I/O stream wrapped by `Frame`.
76    ///
77    /// Note that care should be taken to not tamper with the underlying stream of data coming in as
78    /// it may corrupt the stream of frames otherwise being worked with.
79    pub fn io_ref(&self) -> &T {
80        &self.io
81    }
82
83    /// Returns a mutable reference to the underlying I/O stream.
84    ///
85    /// Note that care should be taken to not tamper with the underlying stream of data coming in as
86    /// it may corrupt the stream of frames otherwise being worked with.
87    pub fn io_mut(&mut self) -> &mut T {
88        &mut self.io
89    }
90
91    /// Returns a `Pin` of a mutable reference to the underlying I/O stream.
92    pub fn io_pin(self: Pin<&mut Self>) -> Pin<&mut T> {
93        self.project().io
94    }
95
96    /// Check if read buffer is empty.
97    pub fn is_read_buf_empty(&self) -> bool {
98        self.read_buf.is_empty()
99    }
100
101    /// Check if write buffer is empty.
102    pub fn is_write_buf_empty(&self) -> bool {
103        self.write_buf.is_empty()
104    }
105
106    /// Check if write buffer is full.
107    pub fn is_write_buf_full(&self) -> bool {
108        self.write_buf.len() >= HW
109    }
110
111    /// Check if framed is able to write more data.
112    ///
113    /// `Framed` object considers ready if there is free space in write buffer.
114    pub fn is_write_ready(&self) -> bool {
115        self.write_buf.len() < HW
116    }
117
118    /// Consume the `Frame`, returning `Frame` with different codec.
119    pub fn replace_codec<U2>(self, codec: U2) -> Framed<T, U2> {
120        Framed {
121            codec,
122            io: self.io,
123            flags: self.flags,
124            read_buf: self.read_buf,
125            write_buf: self.write_buf,
126        }
127    }
128
129    /// Consume the `Frame`, returning `Frame` with different io.
130    pub fn into_map_io<F, T2>(self, f: F) -> Framed<T2, U>
131    where
132        F: Fn(T) -> T2,
133    {
134        Framed {
135            io: f(self.io),
136            codec: self.codec,
137            flags: self.flags,
138            read_buf: self.read_buf,
139            write_buf: self.write_buf,
140        }
141    }
142
143    /// Consume the `Frame`, returning `Frame` with different codec.
144    pub fn into_map_codec<F, U2>(self, f: F) -> Framed<T, U2>
145    where
146        F: Fn(U) -> U2,
147    {
148        Framed {
149            io: self.io,
150            codec: f(self.codec),
151            flags: self.flags,
152            read_buf: self.read_buf,
153            write_buf: self.write_buf,
154        }
155    }
156}
157
158impl<T, U> Framed<T, U> {
159    /// Serialize item and write to the inner buffer
160    pub fn write<I>(mut self: Pin<&mut Self>, item: I) -> Result<(), <U as Encoder<I>>::Error>
161    where
162        T: AsyncWrite,
163        U: Encoder<I>,
164    {
165        let this = self.as_mut().project();
166        let remaining = this.write_buf.capacity() - this.write_buf.len();
167        if remaining < LW {
168            this.write_buf.reserve(HW - remaining);
169        }
170
171        this.codec.encode(item, this.write_buf)?;
172        Ok(())
173    }
174
175    /// Try to read underlying I/O stream and decode item.
176    pub fn next_item(
177        mut self: Pin<&mut Self>,
178        cx: &mut Context<'_>,
179    ) -> Poll<Option<Result<<U as Decoder>::Item, U::Error>>>
180    where
181        T: AsyncRead,
182        U: Decoder,
183    {
184        loop {
185            let this = self.as_mut().project();
186            // Repeatedly call `decode` or `decode_eof` as long as it is "readable". Readable is
187            // defined as not having returned `None`. If the upstream has returned EOF, and the
188            // decoder is no longer readable, it can be assumed that the decoder will never become
189            // readable again, at which point the stream is terminated.
190
191            if this.flags.contains(Flags::READABLE) {
192                if this.flags.contains(Flags::EOF) {
193                    match this.codec.decode_eof(this.read_buf) {
194                        Ok(Some(frame)) => return Poll::Ready(Some(Ok(frame))),
195                        Ok(None) => return Poll::Ready(None),
196                        Err(err) => return Poll::Ready(Some(Err(err))),
197                    }
198                }
199
200                tracing::trace!("attempting to decode a frame");
201
202                match this.codec.decode(this.read_buf) {
203                    Ok(Some(frame)) => {
204                        tracing::trace!("frame decoded from buffer");
205                        return Poll::Ready(Some(Ok(frame)));
206                    }
207                    Err(err) => return Poll::Ready(Some(Err(err))),
208                    _ => (), // Need more data
209                }
210
211                this.flags.remove(Flags::READABLE);
212            }
213
214            debug_assert!(!this.flags.contains(Flags::EOF));
215
216            // Otherwise, try to read more data and try again. Make sure we've got room.
217            let remaining = this.read_buf.capacity() - this.read_buf.len();
218            if remaining < LW {
219                this.read_buf.reserve(HW - remaining)
220            }
221
222            let cnt = match tokio_util::io::poll_read_buf(this.io, cx, this.read_buf) {
223                Poll::Pending => return Poll::Pending,
224                Poll::Ready(Err(err)) => return Poll::Ready(Some(Err(err.into()))),
225                Poll::Ready(Ok(cnt)) => cnt,
226            };
227
228            if cnt == 0 {
229                this.flags.insert(Flags::EOF);
230            }
231            this.flags.insert(Flags::READABLE);
232        }
233    }
234
235    /// Flush write buffer to underlying I/O stream.
236    pub fn flush<I>(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), U::Error>>
237    where
238        T: AsyncWrite,
239        U: Encoder<I>,
240    {
241        let mut this = self.as_mut().project();
242        tracing::trace!("flushing framed transport");
243
244        while !this.write_buf.is_empty() {
245            tracing::trace!("writing; remaining={}", this.write_buf.len());
246
247            let n = ready!(this.io.as_mut().poll_write(cx, this.write_buf))?;
248
249            if n == 0 {
250                return Poll::Ready(Err(io::Error::new(
251                    io::ErrorKind::WriteZero,
252                    "failed to write frame to transport",
253                )
254                .into()));
255            }
256
257            // remove written data
258            this.write_buf.advance(n);
259        }
260
261        // Try flushing the underlying IO
262        ready!(this.io.poll_flush(cx))?;
263
264        tracing::trace!("framed transport flushed");
265        Poll::Ready(Ok(()))
266    }
267
268    /// Flush write buffer and shutdown underlying I/O stream.
269    pub fn close<I>(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), U::Error>>
270    where
271        T: AsyncWrite,
272        U: Encoder<I>,
273    {
274        let mut this = self.as_mut().project();
275        ready!(this.io.as_mut().poll_flush(cx))?;
276        ready!(this.io.as_mut().poll_shutdown(cx))?;
277        Poll::Ready(Ok(()))
278    }
279}
280
281impl<T, U> Stream for Framed<T, U>
282where
283    T: AsyncRead,
284    U: Decoder,
285{
286    type Item = Result<U::Item, U::Error>;
287
288    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
289        self.next_item(cx)
290    }
291}
292
293impl<T, U, I> Sink<I> for Framed<T, U>
294where
295    T: AsyncWrite,
296    U: Encoder<I>,
297    U::Error: From<io::Error>,
298{
299    type Error = U::Error;
300
301    fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
302        if self.is_write_ready() {
303            Poll::Ready(Ok(()))
304        } else {
305            self.flush(cx)
306        }
307    }
308
309    fn start_send(self: Pin<&mut Self>, item: I) -> Result<(), Self::Error> {
310        self.write(item)
311    }
312
313    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
314        self.flush(cx)
315    }
316
317    fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
318        self.close(cx)
319    }
320}
321
322impl<T, U> fmt::Debug for Framed<T, U>
323where
324    T: fmt::Debug,
325    U: fmt::Debug,
326{
327    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
328        f.debug_struct("Framed")
329            .field("io", &self.io)
330            .field("codec", &self.codec)
331            .finish()
332    }
333}
334
335impl<T, U> Framed<T, U> {
336    /// This function returns a *single* object that is both `Stream` and `Sink`; grouping this into
337    /// a single object is often useful for layering things like gzip or TLS, which require both
338    /// read and write access to the underlying object.
339    ///
340    /// These objects take a stream, a read buffer and a write buffer. These fields can be obtained
341    /// from an existing `Framed` with the `into_parts` method.
342    pub fn from_parts(parts: FramedParts<T, U>) -> Framed<T, U> {
343        Framed {
344            io: parts.io,
345            codec: parts.codec,
346            flags: parts.flags,
347            write_buf: parts.write_buf,
348            read_buf: parts.read_buf,
349        }
350    }
351
352    /// Consumes the `Frame`, returning its underlying I/O stream, the buffer with unprocessed data,
353    /// and the codec.
354    ///
355    /// Note that care should be taken to not tamper with the underlying stream of data coming in as
356    /// it may corrupt the stream of frames otherwise being worked with.
357    pub fn into_parts(self) -> FramedParts<T, U> {
358        FramedParts {
359            io: self.io,
360            codec: self.codec,
361            flags: self.flags,
362            read_buf: self.read_buf,
363            write_buf: self.write_buf,
364        }
365    }
366}
367
368/// `FramedParts` contains an export of the data of a Framed transport.
369///
370/// It can be used to construct a new `Framed` with a different codec. It contains all current
371/// buffers and the inner transport.
372#[derive(Debug)]
373pub struct FramedParts<T, U> {
374    /// The inner transport used to read bytes to and write bytes to.
375    pub io: T,
376
377    /// The codec object.
378    pub codec: U,
379
380    /// The buffer with read but unprocessed data.
381    pub read_buf: BytesMut,
382
383    /// A buffer with unprocessed data which are not written yet.
384    pub write_buf: BytesMut,
385
386    flags: Flags,
387}
388
389impl<T, U> FramedParts<T, U> {
390    /// Creates a new default `FramedParts`.
391    pub fn new(io: T, codec: U) -> FramedParts<T, U> {
392        FramedParts {
393            io,
394            codec,
395            flags: Flags::empty(),
396            read_buf: BytesMut::new(),
397            write_buf: BytesMut::new(),
398        }
399    }
400
401    /// Creates a new `FramedParts` with read buffer.
402    pub fn with_read_buf(io: T, codec: U, read_buf: BytesMut) -> FramedParts<T, U> {
403        FramedParts {
404            io,
405            codec,
406            read_buf,
407            flags: Flags::empty(),
408            write_buf: BytesMut::new(),
409        }
410    }
411}