use std::{
future::Future,
net::SocketAddr,
pin::Pin,
task::{Context, Poll},
};
use futures::{future::BoxFuture, FutureExt};
use tokio_rustls::TlsConnector;
use url::Url;
use super::{Options, WebSocket};
use crate::{stream::MaybeTlsStream, Result};
use tokio::net::TcpStream;
pub type HttpRequest = hyper::http::request::Request<()>;
pub type HttpRequestBuilder = hyper::http::request::Builder;
pub struct WebSocketBuilder {
pub(super) opts: Option<WsBuilderOpts>,
pub(super) future: Option<BoxFuture<'static, Result<WebSocket<MaybeTlsStream<TcpStream>>>>>,
}
pub(super) struct WsBuilderOpts {
pub(super) url: Url,
pub(super) tcp_address: Option<SocketAddr>,
pub(super) connector: Option<TlsConnector>,
pub(super) establish_options: Option<Options>,
pub(super) http_builder: Option<HttpRequestBuilder>,
}
impl WebSocketBuilder {
pub(super) fn new(url: Url) -> Self {
Self {
opts: Some(WsBuilderOpts {
url,
tcp_address: None,
connector: None,
establish_options: None,
http_builder: None,
}),
future: None,
}
}
pub fn with_connector(mut self, connector: TlsConnector) -> Self {
let Some(opts) = &mut self.opts else {
unreachable!()
};
opts.connector = Some(connector);
self
}
pub fn with_tcp_address(mut self, address: SocketAddr) -> Self {
let Some(opts) = &mut self.opts else {
unreachable!()
};
opts.tcp_address = Some(address);
self
}
pub fn with_options(mut self, options: Options) -> Self {
let Some(opts) = &mut self.opts else {
unreachable!()
};
opts.establish_options = Some(options);
self
}
pub fn with_request(mut self, builder: HttpRequestBuilder) -> Self {
let Some(opts) = &mut self.opts else {
unreachable!()
};
opts.http_builder = Some(builder);
self
}
}
impl Future for WebSocketBuilder {
type Output = Result<WebSocket<MaybeTlsStream<TcpStream>>>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
if let Some(opts) = this.opts.take() {
let future = WebSocket::connect_priv(
opts.url,
opts.tcp_address,
opts.connector,
opts.establish_options.unwrap_or_default(),
opts.http_builder.unwrap_or_else(HttpRequest::builder),
);
this.future = Some(Box::pin(future));
}
let Some(pinned) = &mut this.future else {
unreachable!()
};
pinned.poll_unpin(cx)
}
}