Skip to main content

actix_http/ws/
dispatcher.rs

1use std::{
2    future::Future,
3    pin::Pin,
4    task::{Context, Poll},
5};
6
7use actix_codec::{AsyncRead, AsyncWrite, Framed};
8use actix_service::{IntoService, Service};
9use pin_project_lite::pin_project;
10
11use super::{Codec, Frame, Message};
12
13pin_project! {
14    pub struct Dispatcher<S, T>
15    where
16        S: Service<Frame, Response = Message>,
17        S: 'static,
18        T: AsyncRead,
19        T: AsyncWrite,
20    {
21        #[pin]
22        inner: inner::Dispatcher<S, T, Codec, Message>,
23    }
24}
25
26impl<S, T> Dispatcher<S, T>
27where
28    T: AsyncRead + AsyncWrite,
29    S: Service<Frame, Response = Message>,
30    S::Future: 'static,
31    S::Error: 'static,
32{
33    pub fn new<F: IntoService<S, Frame>>(io: T, service: F) -> Self {
34        Dispatcher {
35            inner: inner::Dispatcher::new(Framed::new(io, Codec::new()), service),
36        }
37    }
38
39    pub fn with<F: IntoService<S, Frame>>(framed: Framed<T, Codec>, service: F) -> Self {
40        Dispatcher {
41            inner: inner::Dispatcher::new(framed, service),
42        }
43    }
44}
45
46impl<S, T> Future for Dispatcher<S, T>
47where
48    T: AsyncRead + AsyncWrite,
49    S: Service<Frame, Response = Message>,
50    S::Future: 'static,
51    S::Error: 'static,
52{
53    type Output = Result<(), inner::DispatcherError<S::Error, Codec, Message>>;
54
55    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
56        self.project().inner.poll(cx)
57    }
58}
59
60/// Framed dispatcher service and related utilities.
61mod inner {
62    // allow dead code since this mod was ripped from actix-utils
63    #![allow(dead_code)]
64
65    use core::{
66        fmt,
67        future::Future,
68        mem,
69        pin::Pin,
70        task::{Context, Poll},
71    };
72
73    use actix_codec::Framed;
74    use actix_service::{IntoService, Service};
75    use futures_core::stream::Stream;
76    use local_channel::mpsc;
77    use pin_project_lite::pin_project;
78    use tokio::{
79        io::{AsyncRead, AsyncWrite},
80        task::spawn_local,
81    };
82    use tokio_util::codec::{Decoder, Encoder};
83    use tracing::debug;
84
85    use crate::{body::BoxBody, Response};
86
87    /// Framed transport errors
88    pub enum DispatcherError<E, U, I>
89    where
90        U: Encoder<I> + Decoder,
91    {
92        /// Inner service error.
93        Service(E),
94
95        /// Frame encoding error.
96        Encoder(<U as Encoder<I>>::Error),
97
98        /// Frame decoding error.
99        Decoder(<U as Decoder>::Error),
100    }
101
102    impl<E, U, I> From<E> for DispatcherError<E, U, I>
103    where
104        U: Encoder<I> + Decoder,
105    {
106        fn from(err: E) -> Self {
107            DispatcherError::Service(err)
108        }
109    }
110
111    impl<E, U, I> fmt::Debug for DispatcherError<E, U, I>
112    where
113        E: fmt::Debug,
114        U: Encoder<I> + Decoder,
115        <U as Encoder<I>>::Error: fmt::Debug,
116        <U as Decoder>::Error: fmt::Debug,
117    {
118        fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
119            match *self {
120                DispatcherError::Service(ref err) => {
121                    write!(fmt, "DispatcherError::Service({err:?})")
122                }
123                DispatcherError::Encoder(ref err) => {
124                    write!(fmt, "DispatcherError::Encoder({err:?})")
125                }
126                DispatcherError::Decoder(ref err) => {
127                    write!(fmt, "DispatcherError::Decoder({err:?})")
128                }
129            }
130        }
131    }
132
133    impl<E, U, I> fmt::Display for DispatcherError<E, U, I>
134    where
135        E: fmt::Display,
136        U: Encoder<I> + Decoder,
137        <U as Encoder<I>>::Error: fmt::Debug,
138        <U as Decoder>::Error: fmt::Debug,
139    {
140        fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
141            match *self {
142                DispatcherError::Service(ref err) => write!(fmt, "{err}"),
143                DispatcherError::Encoder(ref err) => write!(fmt, "{err:?}"),
144                DispatcherError::Decoder(ref err) => write!(fmt, "{err:?}"),
145            }
146        }
147    }
148
149    impl<E, U, I> From<DispatcherError<E, U, I>> for Response<BoxBody>
150    where
151        E: fmt::Debug + fmt::Display,
152        U: Encoder<I> + Decoder,
153        <U as Encoder<I>>::Error: fmt::Debug,
154        <U as Decoder>::Error: fmt::Debug,
155    {
156        fn from(err: DispatcherError<E, U, I>) -> Self {
157            Response::internal_server_error().set_body(BoxBody::new(err.to_string()))
158        }
159    }
160
161    /// Message type wrapper for signalling end of message stream.
162    pub enum Message<T> {
163        /// Message item.
164        Item(T),
165
166        /// Signal from service to flush all messages and stop processing.
167        Close,
168    }
169
170    pin_project! {
171        /// A future that reads frames from a [`Framed`] object and passes them to a [`Service`].
172        pub struct Dispatcher<S, T, U, I>
173        where
174            S: Service<<U as Decoder>::Item, Response = I>,
175            S::Error: 'static,
176            S::Future: 'static,
177            T: AsyncRead,
178            T: AsyncWrite,
179            U: Encoder<I>,
180            U: Decoder,
181            I: 'static,
182            <U as Encoder<I>>::Error: fmt::Debug,
183        {
184            service: S,
185            state: State<S, U, I>,
186            #[pin]
187            framed: Framed<T, U>,
188            rx: mpsc::Receiver<Result<Message<I>, S::Error>>,
189            tx: mpsc::Sender<Result<Message<I>, S::Error>>,
190        }
191    }
192
193    enum State<S, U, I>
194    where
195        S: Service<<U as Decoder>::Item>,
196        U: Encoder<I> + Decoder,
197    {
198        Processing,
199        Error(DispatcherError<S::Error, U, I>),
200        FramedError(DispatcherError<S::Error, U, I>),
201        FlushAndStop,
202        Stopping,
203    }
204
205    impl<S, U, I> State<S, U, I>
206    where
207        S: Service<<U as Decoder>::Item>,
208        U: Encoder<I> + Decoder,
209    {
210        fn take_error(&mut self) -> DispatcherError<S::Error, U, I> {
211            match mem::replace(self, State::Processing) {
212                State::Error(err) => err,
213                _ => panic!(),
214            }
215        }
216
217        fn take_framed_error(&mut self) -> DispatcherError<S::Error, U, I> {
218            match mem::replace(self, State::Processing) {
219                State::FramedError(err) => err,
220                _ => panic!(),
221            }
222        }
223    }
224
225    impl<S, T, U, I> Dispatcher<S, T, U, I>
226    where
227        S: Service<<U as Decoder>::Item, Response = I>,
228        S::Error: 'static,
229        S::Future: 'static,
230        T: AsyncRead + AsyncWrite,
231        U: Decoder + Encoder<I>,
232        I: 'static,
233        <U as Decoder>::Error: fmt::Debug,
234        <U as Encoder<I>>::Error: fmt::Debug,
235    {
236        /// Create new `Dispatcher`.
237        pub fn new<F>(framed: Framed<T, U>, service: F) -> Self
238        where
239            F: IntoService<S, <U as Decoder>::Item>,
240        {
241            let (tx, rx) = mpsc::channel();
242            Dispatcher {
243                framed,
244                rx,
245                tx,
246                service: service.into_service(),
247                state: State::Processing,
248            }
249        }
250
251        /// Construct new `Dispatcher` instance with customer `mpsc::Receiver`
252        pub fn with_rx<F>(
253            framed: Framed<T, U>,
254            service: F,
255            rx: mpsc::Receiver<Result<Message<I>, S::Error>>,
256        ) -> Self
257        where
258            F: IntoService<S, <U as Decoder>::Item>,
259        {
260            let tx = rx.sender();
261            Dispatcher {
262                framed,
263                rx,
264                tx,
265                service: service.into_service(),
266                state: State::Processing,
267            }
268        }
269
270        /// Get sender handle.
271        pub fn tx(&self) -> mpsc::Sender<Result<Message<I>, S::Error>> {
272            self.tx.clone()
273        }
274
275        /// Get reference to a service wrapped by `Dispatcher` instance.
276        pub fn service(&self) -> &S {
277            &self.service
278        }
279
280        /// Get mutable reference to a service wrapped by `Dispatcher` instance.
281        pub fn service_mut(&mut self) -> &mut S {
282            &mut self.service
283        }
284
285        /// Get reference to a framed instance wrapped by `Dispatcher` instance.
286        pub fn framed(&self) -> &Framed<T, U> {
287            &self.framed
288        }
289
290        /// Get mutable reference to a framed instance wrapped by `Dispatcher` instance.
291        pub fn framed_mut(&mut self) -> &mut Framed<T, U> {
292            &mut self.framed
293        }
294
295        /// Read from framed object.
296        fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> bool
297        where
298            S: Service<<U as Decoder>::Item, Response = I>,
299            S::Error: 'static,
300            S::Future: 'static,
301            T: AsyncRead + AsyncWrite,
302            U: Decoder + Encoder<I>,
303            I: 'static,
304            <U as Encoder<I>>::Error: fmt::Debug,
305        {
306            loop {
307                let this = self.as_mut().project();
308                match this.service.poll_ready(cx) {
309                    Poll::Ready(Ok(_)) => {
310                        let item = match this.framed.next_item(cx) {
311                            Poll::Ready(Some(Ok(el))) => el,
312                            Poll::Ready(Some(Err(err))) => {
313                                *this.state = State::FramedError(DispatcherError::Decoder(err));
314                                return true;
315                            }
316                            Poll::Pending => return false,
317                            Poll::Ready(None) => {
318                                *this.state = State::Stopping;
319                                return true;
320                            }
321                        };
322
323                        let tx = this.tx.clone();
324                        let fut = this.service.call(item);
325                        spawn_local(async move {
326                            let item = fut.await;
327                            let _ = tx.send(item.map(Message::Item));
328                        });
329                    }
330                    Poll::Pending => return false,
331                    Poll::Ready(Err(err)) => {
332                        *this.state = State::Error(DispatcherError::Service(err));
333                        return true;
334                    }
335                }
336            }
337        }
338
339        /// Write to framed object.
340        fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> bool
341        where
342            S: Service<<U as Decoder>::Item, Response = I>,
343            S::Error: 'static,
344            S::Future: 'static,
345            T: AsyncRead + AsyncWrite,
346            U: Decoder + Encoder<I>,
347            I: 'static,
348            <U as Encoder<I>>::Error: fmt::Debug,
349        {
350            loop {
351                let mut this = self.as_mut().project();
352                while !this.framed.is_write_buf_full() {
353                    match Pin::new(&mut this.rx).poll_next(cx) {
354                        Poll::Ready(Some(Ok(Message::Item(msg)))) => {
355                            if let Err(err) = this.framed.as_mut().write(msg) {
356                                *this.state = State::FramedError(DispatcherError::Encoder(err));
357                                return true;
358                            }
359                        }
360                        Poll::Ready(Some(Ok(Message::Close))) => {
361                            *this.state = State::FlushAndStop;
362                            return true;
363                        }
364                        Poll::Ready(Some(Err(err))) => {
365                            *this.state = State::Error(DispatcherError::Service(err));
366                            return true;
367                        }
368                        Poll::Ready(None) | Poll::Pending => break,
369                    }
370                }
371
372                if !this.framed.is_write_buf_empty() {
373                    match this.framed.flush(cx) {
374                        Poll::Pending => break,
375                        Poll::Ready(Ok(_)) => {}
376                        Poll::Ready(Err(err)) => {
377                            debug!("Error sending data: {:?}", err);
378                            *this.state = State::FramedError(DispatcherError::Encoder(err));
379                            return true;
380                        }
381                    }
382                } else {
383                    break;
384                }
385            }
386
387            false
388        }
389    }
390
391    impl<S, T, U, I> Future for Dispatcher<S, T, U, I>
392    where
393        S: Service<<U as Decoder>::Item, Response = I>,
394        S::Error: 'static,
395        S::Future: 'static,
396        T: AsyncRead + AsyncWrite,
397        U: Decoder + Encoder<I>,
398        I: 'static,
399        <U as Encoder<I>>::Error: fmt::Debug,
400        <U as Decoder>::Error: fmt::Debug,
401    {
402        type Output = Result<(), DispatcherError<S::Error, U, I>>;
403
404        fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
405            loop {
406                let this = self.as_mut().project();
407
408                return match this.state {
409                    State::Processing => {
410                        if self.as_mut().poll_read(cx) || self.as_mut().poll_write(cx) {
411                            continue;
412                        } else {
413                            Poll::Pending
414                        }
415                    }
416                    State::Error(_) => {
417                        // flush write buffer
418                        if !this.framed.is_write_buf_empty() && this.framed.flush(cx).is_pending() {
419                            return Poll::Pending;
420                        }
421                        Poll::Ready(Err(this.state.take_error()))
422                    }
423                    State::FlushAndStop => {
424                        if !this.framed.is_write_buf_empty() {
425                            this.framed.flush(cx).map(|res| {
426                                if let Err(err) = res {
427                                    debug!("Error sending data: {:?}", err);
428                                }
429
430                                Ok(())
431                            })
432                        } else {
433                            Poll::Ready(Ok(()))
434                        }
435                    }
436                    State::FramedError(_) => Poll::Ready(Err(this.state.take_framed_error())),
437                    State::Stopping => Poll::Ready(Ok(())),
438                };
439            }
440        }
441    }
442}