uhttp 0.22.0

Simple http server for humans
Documentation
use std::time::Duration;

use serde::Serialize;
use tokio::io::AsyncWriteExt;
use tokio::sync::mpsc::UnboundedSender;
use tokio::sync::mpsc::unbounded_channel;
use tokio::sync::oneshot;

use super::JsonFrame;
use crate::ResponseBody;

pub struct ServerSentEventsOptions {
  pub max_buffer_size: usize,
  pub heartbeat_duration: Duration,
}

pub struct ServerSentEvents {
  tx: UnboundedSender<ServerSentEventsInternalEvent>,
}

impl ServerSentEvents {
  pub fn new(options: ServerSentEventsOptions) -> (ResponseBody, Self) {
    let ServerSentEventsOptions {
      max_buffer_size,
      heartbeat_duration,
    } = options;

    let (body, mut stream) = ResponseBody::chunked(max_buffer_size);

    let (tx, mut rx) = unbounded_channel::<ServerSentEventsInternalEvent>();

    tokio::spawn({
      let tx = tx.clone();
      async move {
        loop {
          tokio::time::sleep(heartbeat_duration).await;
          if tx.send(ServerSentEventsInternalEvent::HeartBeat).is_err() {
            break;
          }
        }
      }
    });

    tokio::spawn(async move {
      while let Some(event) = rx.recv().await {
        match event {
          ServerSentEventsInternalEvent::HeartBeat => {
            let Ok(frame) = JsonFrame::builder().event("heartbeat").as_frame() else {
              break;
            };
            if stream.write_all(&frame).await.is_err() {
              break;
            }
          }
          ServerSentEventsInternalEvent::Frame {
            value,
            tx_done: done,
          } => {
            let Ok(frame) = JsonFrame::builder().event("event").data(value).as_frame() else {
              let _ = done.send(Err(anyhow::anyhow!("Unable to create frame")));
              continue;
            };
            if stream.write_all(&frame).await.is_err() {
              let _ = done.send(Err(anyhow::anyhow!("Unable to write to socket")));
              continue;
            }
            let _ = done.send(Ok(()));
          }
        }
      }
    });

    (body, ServerSentEvents { tx })
  }

  pub async fn send<S: Serialize>(
    &self,
    message: S,
  ) -> anyhow::Result<()> {
    let value = serde_json::to_value(&message)?;
    let (tx_done, rx_done) = oneshot::channel();

    self
      .tx
      .send(ServerSentEventsInternalEvent::Frame { value, tx_done })?;

    rx_done.await??;

    Ok(())
  }
}

pub struct SseSendError<T>(pub T);

enum ServerSentEventsInternalEvent {
  HeartBeat,
  Frame {
    value: serde_json::Value,
    tx_done: oneshot::Sender<anyhow::Result<()>>,
  },
}