mod dialer;
use std::pin::Pin;
use std::sync::Arc;
use std::task::Context;
use std::task::Poll;
use codex_http_client::BuildCustomCaTransportError;
use codex_http_client::HttpClientFactory;
use codex_http_client::build_rustls_client_config_with_custom_ca;
use futures::Sink;
use futures::Stream;
use rustls::ClientConfig;
use tokio::io::AsyncRead;
use tokio::io::AsyncWrite;
use tokio::net::TcpStream;
use tokio_tungstenite::MaybeTlsStream;
use tokio_tungstenite::WebSocketStream as TungsteniteStream;
use tokio_tungstenite::tungstenite::Error as WebSocketError;
use tokio_tungstenite::tungstenite::Message;
use tokio_tungstenite::tungstenite::handshake::client::Request;
use tokio_tungstenite::tungstenite::handshake::client::Response;
use tokio_tungstenite::tungstenite::protocol::WebSocketConfig;
#[derive(Clone)]
pub struct WebSocketConnector {
http_client_factory: HttpClientFactory,
tls_config: Arc<ClientConfig>,
}
impl WebSocketConnector {
pub fn new(
http_client_factory: &HttpClientFactory,
) -> Result<Self, BuildCustomCaTransportError> {
Ok(Self {
http_client_factory: http_client_factory.clone(),
tls_config: build_rustls_client_config_with_custom_ca()?,
})
}
pub async fn connect(
&self,
request: Request,
config: WebSocketConfig,
) -> Result<(WebSocketConnection, Response), WebSocketError> {
let proxy_route = self
.http_client_factory
.resolve_proxy_route(&request.uri().to_string());
let (inner, response) =
dialer::connect(request, config, Arc::clone(&self.tls_config), proxy_route).await?;
Ok((WebSocketConnection { inner }, response))
}
}
pub struct WebSocketConnection {
inner: ConnectionInner,
}
impl Stream for WebSocketConnection {
type Item = Result<Message, WebSocketError>;
fn poll_next(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Option<Self::Item>> {
match &mut self.get_mut().inner {
ConnectionInner::TransportDefault(stream) => Pin::new(stream).poll_next(context),
ConnectionInner::Routed(stream) => Pin::new(stream).poll_next(context),
}
}
}
impl Sink<Message> for WebSocketConnection {
type Error = WebSocketError;
fn poll_ready(
self: Pin<&mut Self>,
context: &mut Context<'_>,
) -> Poll<Result<(), Self::Error>> {
match &mut self.get_mut().inner {
ConnectionInner::TransportDefault(stream) => Pin::new(stream).poll_ready(context),
ConnectionInner::Routed(stream) => Pin::new(stream).poll_ready(context),
}
}
fn start_send(self: Pin<&mut Self>, message: Message) -> Result<(), Self::Error> {
match &mut self.get_mut().inner {
ConnectionInner::TransportDefault(stream) => Pin::new(stream).start_send(message),
ConnectionInner::Routed(stream) => Pin::new(stream).start_send(message),
}
}
fn poll_flush(
self: Pin<&mut Self>,
context: &mut Context<'_>,
) -> Poll<Result<(), Self::Error>> {
match &mut self.get_mut().inner {
ConnectionInner::TransportDefault(stream) => Pin::new(stream).poll_flush(context),
ConnectionInner::Routed(stream) => Pin::new(stream).poll_flush(context),
}
}
fn poll_close(
self: Pin<&mut Self>,
context: &mut Context<'_>,
) -> Poll<Result<(), Self::Error>> {
match &mut self.get_mut().inner {
ConnectionInner::TransportDefault(stream) => Pin::new(stream).poll_close(context),
ConnectionInner::Routed(stream) => Pin::new(stream).poll_close(context),
}
}
}
pub(crate) enum ConnectionInner {
TransportDefault(TungsteniteStream<MaybeTlsStream<TcpStream>>),
Routed(TungsteniteStream<MaybeTlsStream<Box<dyn AsyncIo>>>),
}
pub(crate) trait AsyncIo: AsyncRead + AsyncWrite + Send + Unpin {}
impl<T> AsyncIo for T where T: AsyncRead + AsyncWrite + Send + Unpin {}