use futures_util::StreamExt;
use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt};
use tokio_util::codec::{Decoder, Framed};
use crate::{proto::Role, upgrade::client_request, Error, WebsocketStream};
const BAD_REQUEST: &[u8] = b"HTTP/1.1 400 Bad Request\r\n\r\n";
pub struct Builder {
fail_fast_on_invalid_utf8: bool,
}
impl Default for Builder {
fn default() -> Self {
Self::new()
}
}
impl Builder {
#[must_use]
pub fn new() -> Self {
Self {
fail_fast_on_invalid_utf8: true,
}
}
#[must_use]
pub fn fail_fast_on_invalid_utf8(mut self, value: bool) -> Self {
self.fail_fast_on_invalid_utf8 = value;
self
}
pub async fn accept<S: AsyncRead + AsyncWrite + Unpin>(
&self,
stream: S,
) -> Result<WebsocketStream<S>, Error> {
let (reply, framed) = client_request::Codec {}.framed(stream).into_future().await;
let mut parts = framed.into_parts();
match reply {
Some(Ok(response)) => {
parts.io.write_all(response.as_bytes()).await?;
let framed = Framed::from_parts(parts);
Ok(WebsocketStream::from_framed(
framed,
Role::Server,
self.fail_fast_on_invalid_utf8,
))
}
Some(Err(e)) => {
parts.io.write_all(BAD_REQUEST).await?;
Err(e)
}
None => Err(Error::NoUpgradeResponse),
}
}
pub fn serve<S: AsyncRead + AsyncWrite + Unpin>(&self, stream: S) -> WebsocketStream<S> {
WebsocketStream::from_raw_stream(stream, Role::Server, self.fail_fast_on_invalid_utf8)
}
}