use engineioxide::service::{EngineIoService, MakeEngineIoService};
use http::{Request, Response};
use http_body::Body;
use hyper::service::Service as HyperSvc;
use std::{
sync::Arc,
task::{Context, Poll},
};
use tower_service::Service as TowerSvc;
use crate::{
SocketIoConfig,
adapter::{Adapter, LocalAdapter},
client::Client,
};
pub struct SocketIoService<S: Clone, A: Adapter = LocalAdapter> {
engine_svc: EngineIoService<Client<A>, S>,
}
impl<S, ReqBody, ResBody, A> TowerSvc<Request<ReqBody>> for SocketIoService<S, A>
where
ReqBody: Body + Send + Unpin + std::fmt::Debug + 'static,
<ReqBody as Body>::Error: std::fmt::Debug,
<ReqBody as Body>::Data: Send,
ResBody: Body + Send + 'static,
S: TowerSvc<Request<ReqBody>, Response = Response<ResBody>> + Clone,
A: Adapter,
{
type Response = <EngineIoService<Client<A>, S> as TowerSvc<Request<ReqBody>>>::Response;
type Error = <EngineIoService<Client<A>, S> as TowerSvc<Request<ReqBody>>>::Error;
type Future = <EngineIoService<Client<A>, S> as TowerSvc<Request<ReqBody>>>::Future;
#[inline(always)]
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.engine_svc.poll_ready(cx)
}
#[inline(always)]
fn call(&mut self, req: Request<ReqBody>) -> Self::Future {
self.engine_svc.call(req)
}
}
impl<S, ReqBody, ResBody, A> HyperSvc<Request<ReqBody>> for SocketIoService<S, A>
where
ReqBody: Body + Send + Unpin + std::fmt::Debug + 'static,
<ReqBody as Body>::Error: std::fmt::Debug,
<ReqBody as Body>::Data: Send,
ResBody: Body + Send + 'static,
S: HyperSvc<Request<ReqBody>, Response = Response<ResBody>> + Clone,
A: Adapter,
{
type Response = <EngineIoService<Client<A>, S> as HyperSvc<Request<ReqBody>>>::Response;
type Error = <EngineIoService<Client<A>, S> as HyperSvc<Request<ReqBody>>>::Error;
type Future = <EngineIoService<Client<A>, S> as HyperSvc<Request<ReqBody>>>::Future;
#[inline(always)]
fn call(&self, req: Request<ReqBody>) -> Self::Future {
self.engine_svc.call(req)
}
}
impl<A: Adapter, S: Clone> SocketIoService<S, A> {
#[inline(always)]
pub fn into_make_service(self) -> MakeEngineIoService<Client<A>, S> {
self.engine_svc.into_make_service()
}
pub(crate) fn with_config_inner(
inner: S,
config: SocketIoConfig,
adapter_state: A::State,
#[cfg(feature = "state")] state: state::TypeMap![Send + Sync],
) -> (Self, Arc<Client<A>>) {
let engine_config = config.engine_config.clone();
let client = Arc::new(Client::new(
config,
adapter_state,
#[cfg(feature = "state")]
state,
));
let svc = EngineIoService::with_config_inner(inner, client.clone(), engine_config);
(Self { engine_svc: svc }, client)
}
pub(crate) fn with_client(inner: S, client: Arc<Client<A>>) -> Self {
let engine_config = client.config.engine_config.clone();
let svc = EngineIoService::with_config_inner(inner, client, engine_config);
Self { engine_svc: svc }
}
}
impl<A: Adapter, S: Clone> Clone for SocketIoService<S, A> {
fn clone(&self) -> Self {
Self {
engine_svc: self.engine_svc.clone(),
}
}
}
#[cfg(feature = "__test_harness")]
#[doc(hidden)]
impl<Svc, A> SocketIoService<Svc, A>
where
Svc: Clone + 'static,
A: Adapter,
{
pub fn ws_init<S>(
&self,
conn: S,
protocol: crate::ProtocolVersion,
sid: Option<crate::socket::Sid>,
req_data: http::request::Parts,
) -> impl std::future::Future<Output = ()> + 'static
where
S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + 'static,
{
let svc = self.engine_svc.clone();
async move {
if let Err(e) = svc.ws_init(conn, protocol.into(), sid, req_data).await {
eprintln!("Error initializing websocket connection {e}");
}
}
}
}