use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use bytes::Bytes;
use futures::stream::{Stream, StreamExt};
use foundation_netio::shared::http::SimpleHeaders;
use crate::shared::codec::ProcedureCodecs;
use crate::shared::context::Ctx;
use crate::shared::error::{ConnectError, ConnectResult};
use crate::shared::interceptor::{StreamCall, StreamingHandlerFunc, UnaryCall, UnaryFunc, UnaryReply};
use crate::shared::message::{Request, Response};
use crate::shared::transport::{BoxFuture, HandlerConn, MessageSink, MessageSource};
pub type RequestStream<Req> = Pin<Box<dyn Stream<Item = ConnectResult<Req>> + Send>>;
pub(crate) trait ProcedureMeta: Send + Sync {
fn has_codec(&self, name: &str) -> bool;
fn codec_names(&self) -> Vec<String>;
#[allow(dead_code)]
fn is_binary(&self, name: &str) -> Option<bool>;
}
pub(crate) struct CodecMeta<Req, Res> {
codecs: Arc<ProcedureCodecs<Req, Res>>,
}
impl<Req: 'static, Res: 'static> ProcedureMeta for CodecMeta<Req, Res> {
fn has_codec(&self, name: &str) -> bool {
self.codecs.contains(name)
}
fn codec_names(&self) -> Vec<String> {
self.codecs.names().map(str::to_string).collect()
}
fn is_binary(&self, name: &str) -> Option<bool> {
self.codecs.is_binary(name)
}
}
pub(crate) trait ErasedUnaryHandler: Send + Sync {
fn handle(
&self,
ctx: Ctx,
codec_name: &str,
body: Bytes,
) -> BoxFuture<'static, ConnectResult<(Bytes, SimpleHeaders, SimpleHeaders)>>;
}
pub(crate) trait ErasedStreamHandler: Send + Sync {
fn handle(
&self,
ctx: Ctx,
codec_name: &str,
conn: Box<dyn HandlerConn>,
) -> BoxFuture<'static, ConnectResult<()>>;
}
pub(crate) struct UnaryHandlerImpl {
func: UnaryFunc,
}
impl ErasedUnaryHandler for UnaryHandlerImpl {
fn handle(
&self,
ctx: Ctx,
codec_name: &str,
body: Bytes,
) -> BoxFuture<'static, ConnectResult<(Bytes, SimpleHeaders, SimpleHeaders)>> {
let call = UnaryCall {
headers: (*ctx.request.headers).clone(),
codec_name: codec_name.to_string(),
frame: body,
};
let fut = (self.func)(ctx, call);
Box::pin(async move {
let reply = fut.await?;
Ok((reply.frame, reply.headers, reply.trailers))
})
}
}
impl UnaryHandlerImpl {
pub(crate) fn new(func: UnaryFunc) -> Self {
Self { func }
}
}
pub(crate) struct StreamHandlerImpl {
func: StreamingHandlerFunc,
}
impl ErasedStreamHandler for StreamHandlerImpl {
fn handle(
&self,
ctx: Ctx,
codec_name: &str,
conn: Box<dyn HandlerConn>,
) -> BoxFuture<'static, ConnectResult<()>> {
(self.func)(
ctx,
StreamCall {
codec_name: codec_name.to_string(),
conn,
},
)
}
}
impl StreamHandlerImpl {
pub(crate) fn new(func: StreamingHandlerFunc) -> Self {
Self { func }
}
}
pub(crate) fn unary_handler<Req, Res, F, Fut>(
codecs: Arc<ProcedureCodecs<Req, Res>>,
handler: F,
) -> UnaryFunc
where
Req: Send + 'static,
Res: Send + 'static,
F: Fn(Ctx, Request<Req>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = ConnectResult<Response<Res>>> + Send + 'static,
{
let handler = Arc::new(handler);
Arc::new(move |ctx: Ctx, call: UnaryCall| {
let codecs = codecs.clone();
let handler = handler.clone();
Box::pin(async move {
let req_codec = codecs.for_request(&call.codec_name)?;
let res_codec = codecs.for_response(&call.codec_name)?;
let msg = req_codec.unmarshal(call.frame)?;
let request =
Request::with_parts(msg, ctx.spec().clone(), ctx.peer().clone(), call.headers);
let (out, headers, trailers) = handler(ctx.clone(), request).await?.into_parts();
let frame = res_codec.marshal(&out)?;
Ok(UnaryReply {
headers,
trailers,
frame,
})
})
})
}
pub(crate) fn server_stream_handler<Req, Res, S, F, Fut>(
codecs: Arc<ProcedureCodecs<Req, Res>>,
handler: F,
) -> StreamingHandlerFunc
where
Req: Send + 'static,
Res: Send + 'static,
S: Stream<Item = ConnectResult<Res>> + Send + 'static,
F: Fn(Ctx, Request<Req>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = ConnectResult<S>> + Send + 'static,
{
let handler = Arc::new(handler);
Arc::new(move |ctx: Ctx, call: StreamCall| {
let codecs = codecs.clone();
let handler = handler.clone();
Box::pin(async move {
let req_codec = codecs.for_request(&call.codec_name)?;
let res_codec = codecs.for_response(&call.codec_name)?;
let headers = call.conn.request_headers().clone();
let (receiver, sender) = call.conn.split();
let mut source = MessageSource::new(receiver, req_codec, headers.clone());
let req = source.receive().await?.ok_or_else(|| {
ConnectError::invalid_argument(
"server-stream requires exactly one request message",
)
})?;
let request =
Request::with_parts(req, ctx.spec().clone(), ctx.peer().clone(), headers);
let stream = handler(ctx.clone(), request).await?;
let mut sink = MessageSink::new(sender, res_codec);
let mut stream = Box::pin(stream);
while let Some(item) = stream.next().await {
match item {
Ok(msg) => sink.send(msg).await?,
Err(e) => return sink.close_with_error(e, SimpleHeaders::new()).await,
}
}
sink.close(SimpleHeaders::new()).await
})
})
}
pub(crate) fn client_stream_handler<Req, Res, F, Fut>(
codecs: Arc<ProcedureCodecs<Req, Res>>,
handler: F,
) -> StreamingHandlerFunc
where
Req: Send + 'static,
Res: Send + 'static,
F: Fn(Ctx, RequestStream<Req>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = ConnectResult<Response<Res>>> + Send + 'static,
{
let handler = Arc::new(handler);
Arc::new(move |ctx: Ctx, call: StreamCall| {
let codecs = codecs.clone();
let handler = handler.clone();
Box::pin(async move {
let req_codec = codecs.for_request(&call.codec_name)?;
let res_codec = codecs.for_response(&call.codec_name)?;
let headers = call.conn.request_headers().clone();
let (receiver, sender) = call.conn.split();
let source = MessageSource::new(receiver, req_codec, headers);
let req_stream = source_into_stream(source);
let (out, resp_headers, trailers) =
handler(ctx.clone(), req_stream).await?.into_parts();
let mut sink = MessageSink::new(sender, res_codec);
sink.set_headers(resp_headers).await?;
sink.send(out).await?;
sink.close(trailers).await
})
})
}
pub(crate) fn bidi_stream_handler<Req, Res, S, F, Fut>(
codecs: Arc<ProcedureCodecs<Req, Res>>,
handler: F,
) -> StreamingHandlerFunc
where
Req: Send + 'static,
Res: Send + 'static,
S: Stream<Item = ConnectResult<Res>> + Send + 'static,
F: Fn(Ctx, RequestStream<Req>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = ConnectResult<S>> + Send + 'static,
{
let handler = Arc::new(handler);
Arc::new(move |ctx: Ctx, call: StreamCall| {
let codecs = codecs.clone();
let handler = handler.clone();
Box::pin(async move {
let req_codec = codecs.for_request(&call.codec_name)?;
let res_codec = codecs.for_response(&call.codec_name)?;
let headers = call.conn.request_headers().clone();
let (receiver, sender) = call.conn.split();
let source = MessageSource::new(receiver, req_codec, headers);
let req_stream = source_into_stream(source);
let out_stream = handler(ctx.clone(), req_stream).await?;
let mut sink = MessageSink::new(sender, res_codec);
let mut out_stream = Box::pin(out_stream);
while let Some(item) = out_stream.next().await {
match item {
Ok(msg) => sink.send(msg).await?,
Err(e) => return sink.close_with_error(e, SimpleHeaders::new()).await,
}
}
sink.close(SimpleHeaders::new()).await
})
})
}
fn source_into_stream<Req: Send + 'static>(source: MessageSource<Req>) -> RequestStream<Req> {
Box::pin(futures::stream::unfold(
(source, false),
|(mut source, err_done)| async move {
if err_done {
return None;
}
match source.receive().await {
Ok(Some(msg)) => Some((Ok(msg), (source, false))),
Ok(None) => None,
Err(e) => Some((Err(e), (source, true))),
}
},
))
}
pub(crate) fn codec_meta<Req: 'static, Res: 'static>(
codecs: Arc<ProcedureCodecs<Req, Res>>,
) -> Arc<dyn ProcedureMeta> {
Arc::new(CodecMeta { codecs })
}