1use 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
14pub trait HttpServerAlg {
16 type Program;
18 type Open;
20 type Error;
22
23 fn open(&mut self, setup: HttpServerSetup<Self::Program>) -> impl Future<Output = Result<Self::Open, Self::Error>>;
28
29 fn close(&mut self, open: &mut Self::Open) -> impl Future<Output = Result<(), Self::Error>>;
39
40 fn end(&mut self, open: &mut Self::Open) -> impl Future<Output = Result<(), Self::Error>>;
51}
52
53#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
55pub struct HttpBind(SocketAddr);
56
57impl HttpBind {
58 pub const fn new(address: SocketAddr) -> Self {
60 Self(address)
61 }
62
63 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#[derive(Debug)]
77pub struct HttpServerSetup<Surface> {
78 bind: HttpBind,
79 surface: Surface,
80}
81
82impl<Surface> HttpServerSetup<Surface> {
83 pub const fn new(bind: HttpBind, surface: Surface) -> Self {
85 Self { bind, surface }
86 }
87
88 pub const fn bind(&self) -> HttpBind {
90 self.bind
91 }
92
93 pub fn into_parts(self) -> (HttpBind, Surface) {
95 (self.bind, self.surface)
96 }
97}
98
99#[derive(Debug)]
101pub enum HttpServerCommand<Surface> {
102 Open(HttpServerSetup<Surface>),
104 Close,
106}
107
108#[derive(Debug, PartialEq)]
110pub enum HttpServerEvent<Error> {
111 Opened(HttpBind),
113 Replaced {
115 closed: HttpBind,
117 opened: HttpBind,
119 },
120 Closed(HttpBind),
122 Failed {
124 bind: HttpBind,
126 error: Error,
128 },
129}
130
131enum Opening<Open, Error, Program> {
133 Made(Result<Open, Error>),
135 Cancelled(HttpServerCommand<Program>),
137}
138
139#[ext(name = HttpServerExt)]
141pub impl<This> This
142where
143 This: HttpServerAlg,
144{
145 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 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 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 struct SlowServer {
321 binding: Option<Receiver<()>>,
323 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 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 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}