use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
use hyper::body::{Body, Incoming};
use hyper::server::conn::http2;
use hyper::service::Service;
use hyper::{Request, Response};
use hyper_util::rt::{TokioExecutor, TokioIo};
use tokio::io::{AsyncRead, AsyncWrite, DuplexStream, ReadBuf};
mod handshake;
mod wslay;
pub use handshake::{
accept, accept_with, accept_with_options, is_upgrade_request, offered_protocols, AcceptOptions,
UpgradedIo, WebSocketError, DEFAULT_SUBPROTOCOL,
};
pub use wslay::{
bridge, bridge_with, control_channel, BridgeConfig, CloseFrame, CloseHook, ControlHook,
ControlReceiver, KeepAlive, WsControl,
};
const DUPLEX_BUF: usize = 64 * 1024;
pub struct WsByteStream {
inner: DuplexStream,
}
impl WsByteStream {
pub fn new<S>(ws_io: S) -> Self
where
S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
{
Self::with_config(ws_io, BridgeConfig::default())
}
pub fn with_config<S>(ws_io: S, config: BridgeConfig) -> Self
where
S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
{
let (app_side, ws_side) = tokio::io::duplex(DUPLEX_BUF);
tokio::spawn(async move {
let _ = bridge_with(ws_io, ws_side, config).await;
});
Self { inner: app_side }
}
}
impl AsyncRead for WsByteStream {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
Pin::new(&mut self.get_mut().inner).poll_read(cx, buf)
}
}
impl AsyncWrite for WsByteStream {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut self.get_mut().inner).poll_write(cx, buf)
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.get_mut().inner).poll_flush(cx)
}
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.get_mut().inner).poll_shutdown(cx)
}
}
pub async fn serve_h2<S, Svc, B>(ws_io: S, service: Svc) -> hyper::Result<()>
where
S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
Svc: Service<Request<Incoming>, Response = Response<B>> + Send + 'static,
Svc::Future: Send + 'static,
Svc::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
B: Body + Send + 'static,
B::Data: Send,
B::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
{
serve_h2_with(ws_io, service, BridgeConfig::default()).await
}
pub async fn serve_h2_with<S, Svc, B>(
ws_io: S,
service: Svc,
config: BridgeConfig,
) -> hyper::Result<()>
where
S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
Svc: Service<Request<Incoming>, Response = Response<B>> + Send + 'static,
Svc::Future: Send + 'static,
Svc::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
B: Body + Send + 'static,
B::Data: Send,
B::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
{
let io = TokioIo::new(WsByteStream::with_config(ws_io, config));
http2::Builder::new(TokioExecutor::new())
.serve_connection(io, service)
.await
}