Skip to main content

alux_http/
server.rs

1//! States HTTP server lifecycle independently of the framework that serves a surface.
2//!
3//! [`HttpServerAlg`] is the tagless-final specification: an interpreter chooses its executable
4//! surface, open-server handle, and error types, then gives meaning to opening, closing and ending.
5//! [`HttpServerExt::lifecycle`] derives streaming access from those primitives.
6
7use alux_ext::ext;
8use core::future::Future;
9use core::pin::pin;
10use futures::future::{Either, select};
11use futures::{Stream, StreamExt, stream};
12use std::net::SocketAddr;
13
14/// Interprets the lifecycle of one bound HTTP surface.
15pub trait HttpServerAlg {
16    /// Carries the executable HTTP surface this server serves.
17    type Program;
18    /// Carries a concrete open server.
19    type Open;
20    /// States a failure while opening or closing a server.
21    type Error;
22
23    /// Opens the address a setup names and resolves only once it is ready to serve.
24    ///
25    /// Must leave no address bound if dropped before it resolves, so a call still binding can be
26    /// cancelled.
27    fn open(&mut self, setup: HttpServerSetup<Self::Program>) -> impl Future<Output = Result<Self::Open, Self::Error>>;
28
29    /// Closes an open server and resolves only once its address is released.
30    ///
31    /// Nothing already being served is waited for. Those requests are answered, or ended when this
32    /// interpretation's drain runs out, which may happen after this has resolved. So closing is
33    /// what a caller wants to take the address back, and [`Self::end`] is what it wants to know
34    /// the server is done.
35    ///
36    /// Leaves `open` available when closing fails, so a manager can retain the state it still owns
37    /// rather than pretending the server disappeared.
38    fn close(&mut self, open: &mut Self::Open) -> impl Future<Output = Result<(), Self::Error>>;
39
40    /// Closes an open server and resolves once nothing it accepted is still being served either.
41    ///
42    /// A request already being served is answered, and one still being served when this
43    /// interpretation's drain runs out is ended. So ending neither abandons whoever is mid-request
44    /// nor waits on them without a bound.
45    ///
46    /// An interpretation that cannot tell the two apart resolves this where [`Self::close`]
47    /// resolves, which satisfies both: the address is released and the drain is over.
48    ///
49    /// Leaves `open` available when ending fails, for the reason [`Self::close`] does.
50    fn end(&mut self, open: &mut Self::Open) -> impl Future<Output = Result<(), Self::Error>>;
51}
52
53/// Names the socket address at which an HTTP surface is bound.
54#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
55pub struct HttpBind(SocketAddr);
56
57impl HttpBind {
58    /// Names one socket address to bind.
59    pub const fn new(address: SocketAddr) -> Self {
60        Self(address)
61    }
62
63    /// Returns the socket address named.
64    pub const fn address(self) -> SocketAddr {
65        self.0
66    }
67}
68
69impl From<SocketAddr> for HttpBind {
70    fn from(address: SocketAddr) -> Self {
71        Self::new(address)
72    }
73}
74
75/// States one executable HTTP surface together with the address at which it is served.
76#[derive(Debug)]
77pub struct HttpServerSetup<Surface> {
78    bind: HttpBind,
79    surface: Surface,
80}
81
82impl<Surface> HttpServerSetup<Surface> {
83    /// States that `surface` is served at `bind`.
84    pub const fn new(bind: HttpBind, surface: Surface) -> Self {
85        Self { bind, surface }
86    }
87
88    /// Returns the address at which the surface is served.
89    pub const fn bind(&self) -> HttpBind {
90        self.bind
91    }
92
93    /// Separates the bound address from the surface it serves.
94    pub fn into_parts(self) -> (HttpBind, Surface) {
95        (self.bind, self.surface)
96    }
97}
98
99/// Requests the desired lifecycle state of one bound HTTP server.
100#[derive(Debug)]
101pub enum HttpServerCommand<Surface> {
102    /// Requests that this setup be the server currently open.
103    Open(HttpServerSetup<Surface>),
104    /// Requests that no server remain open.
105    Close,
106}
107
108/// Records the lifecycle transition an HTTP server interpreter made.
109#[derive(Debug, PartialEq)]
110pub enum HttpServerEvent<Error> {
111    /// Records that a surface began serving at an address.
112    Opened(HttpBind),
113    /// Records that an open server released one address before another was bound.
114    Replaced {
115        /// Names the address the previous server closed.
116        closed: HttpBind,
117        /// Names the address the replacement server opened.
118        opened: HttpBind,
119    },
120    /// Records that an open server released its address.
121    Closed(HttpBind),
122    /// Records that the requested transition could not be completed.
123    Failed {
124        /// Names the address of the transition that failed.
125        bind: HttpBind,
126        /// Carries the interpreter's failure.
127        error: Error,
128    },
129}
130
131/// How one open ended: with the server it made, or with the command that cancelled it.
132enum Opening<Open, Error, Program> {
133    /// What the server answered, open or failed.
134    Made(Result<Open, Error>),
135    /// The command that arrived while the open was still binding.
136    Cancelled(HttpServerCommand<Program>),
137}
138
139/// Derives streaming access to a concrete HTTP server's lifecycle.
140#[ext(name = HttpServerExt)]
141pub impl<This> This
142where
143    This: HttpServerAlg,
144{
145    /// Interprets lifecycle commands as the ordered transitions this server makes.
146    fn lifecycle<Commands>(self, commands: Commands) -> impl Stream<Item = HttpServerEvent<Self::Error>>
147    where
148        Commands: Stream<Item = HttpServerCommand<Self::Program>> + Unpin,
149    {
150        stream::unfold((self, commands, None, None), |(mut server, mut commands, mut open, mut pending)| async move {
151            loop {
152                let command = match pending.take() {
153                    Some(command) => command,
154                    None => commands.next().await?,
155                };
156
157                match command {
158                    HttpServerCommand::Open(setup) => {
159                        let bind = setup.bind();
160                        let closed = match open.take() {
161                            Some((closed, mut active)) => match server.close(&mut active).await {
162                                Ok(()) => Some(closed),
163                                Err(error) => {
164                                    return Some((
165                                        HttpServerEvent::Failed { bind: closed, error },
166                                        (server, commands, Some((closed, active)), pending),
167                                    ));
168                                }
169                            },
170                            None => None,
171                        };
172
173                        // The next command cancels an open that has not bound yet, so a close
174                        // does not wait for the address to be taken first.
175                        let opening = {
176                            let opens = pin!(server.open(setup));
177                            match select(opens, commands.next()).await {
178                                Either::Left((made, _)) => Opening::Made(made),
179                                Either::Right((Some(next), _)) => Opening::Cancelled(next),
180                                Either::Right((None, opens)) => Opening::Made(opens.await),
181                            }
182                        };
183
184                        let made = match opening {
185                            Opening::Made(made) => made,
186                            Opening::Cancelled(next) => {
187                                pending = Some(next);
188                                // The previous address was released before this open was
189                                // cancelled, so report that much.
190                                let Some(closed) = closed else { continue };
191
192                                return Some((HttpServerEvent::Closed(closed), (server, commands, None, pending)));
193                            }
194                        };
195
196                        let (event, active) = match made {
197                            Ok(active) => {
198                                let event = match closed {
199                                    Some(closed) => HttpServerEvent::Replaced { closed, opened: bind },
200                                    None => HttpServerEvent::Opened(bind),
201                                };
202                                (event, Some((bind, active)))
203                            }
204                            Err(error) => (HttpServerEvent::Failed { bind, error }, None),
205                        };
206
207                        return Some((event, (server, commands, active, pending)));
208                    }
209                    HttpServerCommand::Close => {
210                        let Some((bind, mut active)) = open.take() else {
211                            continue;
212                        };
213
214                        let (event, active) = match server.close(&mut active).await {
215                            Ok(()) => (HttpServerEvent::Closed(bind), None),
216                            Err(error) => (HttpServerEvent::Failed { bind, error }, Some((bind, active))),
217                        };
218
219                        return Some((event, (server, commands, active, pending)));
220                    }
221                }
222            }
223        })
224    }
225}
226
227#[cfg(test)]
228mod tests {
229    use super::*;
230    use core::cell::Cell;
231    use futures::channel::oneshot::{self, Receiver, Sender};
232    use futures::{StreamExt, executor::block_on, stream};
233    use std::rc::Rc;
234
235    #[derive(Debug, PartialEq)]
236    struct TestError;
237
238    struct TestServer;
239
240    struct TestOpen;
241
242    impl HttpServerAlg for TestServer {
243        type Program = &'static str;
244        type Open = TestOpen;
245        type Error = TestError;
246
247        async fn open(&mut self, _setup: HttpServerSetup<Self::Program>) -> Result<Self::Open, Self::Error> {
248            Ok(TestOpen)
249        }
250
251        async fn close(&mut self, _open: &mut Self::Open) -> Result<(), Self::Error> {
252            Ok(())
253        }
254
255        async fn end(&mut self, _open: &mut Self::Open) -> Result<(), Self::Error> {
256            Ok(())
257        }
258    }
259
260    struct FailingServer {
261        fails_once: bool,
262    }
263
264    impl HttpServerAlg for FailingServer {
265        type Program = &'static str;
266        type Open = TestOpen;
267        type Error = TestError;
268
269        async fn open(&mut self, _setup: HttpServerSetup<Self::Program>) -> Result<Self::Open, Self::Error> {
270            Ok(TestOpen)
271        }
272
273        async fn close(&mut self, _open: &mut Self::Open) -> Result<(), Self::Error> {
274            if self.fails_once {
275                self.fails_once = false;
276                Err(TestError)
277            } else {
278                Ok(())
279            }
280        }
281
282        async fn end(&mut self, open: &mut Self::Open) -> Result<(), Self::Error> {
283            self.close(open).await
284        }
285    }
286
287    fn bind(port: u16) -> HttpBind {
288        HttpBind::new(SocketAddr::from(([127, 0, 0, 1], port)))
289    }
290
291    #[test]
292    fn streams_ordered_lifecycle_transitions() {
293        let commands = stream::iter([
294            HttpServerCommand::Open(HttpServerSetup::new(bind(3000), "first")),
295            HttpServerCommand::Open(HttpServerSetup::new(bind(3001), "second")),
296            HttpServerCommand::Close,
297        ]);
298
299        let events = block_on(TestServer.lifecycle(commands).collect::<Vec<_>>());
300
301        assert_eq!(
302            events,
303            [
304                HttpServerEvent::Opened(bind(3000)),
305                HttpServerEvent::Replaced { closed: bind(3000), opened: bind(3001) },
306                HttpServerEvent::Closed(bind(3001)),
307            ]
308        );
309    }
310
311    #[test]
312    fn ignores_a_close_when_nothing_is_open() {
313        let commands = stream::iter([HttpServerCommand::<&'static str>::Close]);
314        let events = block_on(TestServer.lifecycle(commands).collect::<Vec<_>>());
315
316        assert!(events.is_empty());
317    }
318
319    /// Binds only when told to, which is what lets a test ask for something while an open waits.
320    struct SlowServer {
321        /// Resolves when the open named by `blocks` may bind; never, where the sender is held.
322        binding: Option<Receiver<()>>,
323        /// Names which open waits, counted from the first.
324        blocks: usize,
325        opened: usize,
326        bound: Rc<Cell<usize>>,
327    }
328
329    impl SlowServer {
330        fn new(blocks: usize, bound: &Rc<Cell<usize>>) -> (Self, Sender<()>) {
331            let (binds, binding) = oneshot::channel();
332            (Self { binding: Some(binding), blocks, opened: 0, bound: bound.clone() }, binds)
333        }
334    }
335
336    impl HttpServerAlg for SlowServer {
337        type Program = &'static str;
338        type Open = TestOpen;
339        type Error = TestError;
340
341        async fn open(&mut self, _setup: HttpServerSetup<Self::Program>) -> Result<Self::Open, Self::Error> {
342            if self.opened == self.blocks
343                && let Some(binding) = self.binding.take()
344            {
345                let _ = binding.await;
346            }
347            self.opened += 1;
348            self.bound.set(self.bound.get() + 1);
349
350            Ok(TestOpen)
351        }
352
353        async fn close(&mut self, _open: &mut Self::Open) -> Result<(), Self::Error> {
354            Ok(())
355        }
356
357        async fn end(&mut self, _open: &mut Self::Open) -> Result<(), Self::Error> {
358            Ok(())
359        }
360    }
361
362    #[test]
363    fn cancels_an_open_that_has_not_bound_when_something_else_is_asked() {
364        let bound = Rc::new(Cell::new(0));
365        let (server, _binds) = SlowServer::new(0, &bound);
366        let commands = stream::iter([
367            HttpServerCommand::Open(HttpServerSetup::new(bind(3000), "never binds")),
368            HttpServerCommand::<&'static str>::Close,
369        ]);
370
371        let events = block_on(server.lifecycle(commands).collect::<Vec<_>>());
372
373        // The close was answered while the address was being taken, so it never was taken.
374        assert!(events.is_empty());
375        assert_eq!(bound.get(), 0, "the cancelled open bound an address");
376    }
377
378    #[test]
379    fn states_the_release_when_a_replacement_open_is_cancelled() {
380        let bound = Rc::new(Cell::new(0));
381        let (server, _binds) = SlowServer::new(1, &bound);
382        let commands = stream::iter([
383            HttpServerCommand::Open(HttpServerSetup::new(bind(3000), "binds")),
384            HttpServerCommand::Open(HttpServerSetup::new(bind(3001), "never binds")),
385            HttpServerCommand::Close,
386        ]);
387
388        let events = block_on(server.lifecycle(commands).collect::<Vec<_>>());
389
390        // The first address was released before the second open was cancelled, and releasing it is
391        // a transition whoever reads them is told about.
392        assert_eq!(events, [HttpServerEvent::Opened(bind(3000)), HttpServerEvent::Closed(bind(3000))]);
393        assert_eq!(bound.get(), 1, "the cancelled open bound an address");
394    }
395
396    #[test]
397    fn retains_a_server_after_its_close_fails() {
398        let commands = stream::iter([
399            HttpServerCommand::Open(HttpServerSetup::new(bind(3000), "first")),
400            HttpServerCommand::Close,
401            HttpServerCommand::Open(HttpServerSetup::new(bind(3001), "second")),
402        ]);
403        let server = FailingServer { fails_once: true };
404        let events = block_on(server.lifecycle(commands).collect::<Vec<_>>());
405
406        assert_eq!(
407            events,
408            [
409                HttpServerEvent::Opened(bind(3000)),
410                HttpServerEvent::Failed { bind: bind(3000), error: TestError },
411                HttpServerEvent::Replaced { closed: bind(3000), opened: bind(3001) },
412            ]
413        );
414    }
415}