use std::pin::Pin;
use std::sync::Arc;
use buffa::Message;
use buffa::view::MessageView;
use buffa::view::OwnedView;
use bytes::Bytes;
use futures::Stream;
use crate::codec::CodecFormat;
use crate::codec::decode_json;
use crate::codec::{JsonDeserialize, JsonSerialize};
use crate::error::ConnectError;
use crate::response::{
Encodable, EncodedResponse, RequestContext, Response, ServiceResult, ServiceStream,
};
fn decode_request_error(e: &buffa::DecodeError) -> ConnectError {
match e {
buffa::DecodeError::ElementMemoryLimitExceeded => ConnectError::invalid_argument(format!(
"failed to decode proto request: {e}; if this peer is trusted, \
raise the limit with Limits::with_element_memory_limit"
)),
_ => ConnectError::invalid_argument(format!("failed to decode proto request: {e}")),
}
}
pub(crate) fn decode_request<Req>(
request: &Bytes,
format: CodecFormat,
options: &buffa::DecodeOptions,
) -> Result<Req, ConnectError>
where
Req: Message + JsonDeserialize,
{
match format {
CodecFormat::Proto => options
.decode_from_slice(&request[..])
.map_err(|e| decode_request_error(&e)),
CodecFormat::Json => decode_json(&request[..]),
}
}
pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
pub type BoxStream<T> = Pin<Box<dyn Stream<Item = T> + Send>>;
fn encode_body_stream<Res, B, S>(stream: S, format: CodecFormat) -> crate::EncodedStream
where
Res: Message + Send + 'static,
B: Encodable<Res> + Send + 'static,
S: Stream<Item = Result<B, ConnectError>> + Send + 'static,
{
crate::dispatcher::codegen::encode_response_stream::<Res, B, S>(stream, format)
}
pub(crate) trait ErasedHandler: Send + Sync {
fn call_erased(
&self,
ctx: RequestContext,
request: crate::Payload,
format: CodecFormat,
) -> BoxFuture<'static, Result<EncodedResponse, ConnectError>>;
#[allow(dead_code)]
fn is_streaming(&self) -> bool;
}
pub(crate) type StreamingHandlerResult =
BoxFuture<'static, Result<Response<crate::EncodedStream>, ConnectError>>;
pub(crate) trait ErasedStreamingHandler: Send + Sync {
fn call_erased(
&self,
ctx: RequestContext,
request: Bytes,
format: CodecFormat,
) -> StreamingHandlerResult;
}
pub(crate) trait ErasedClientStreamingHandler: Send + Sync {
fn call_erased(
&self,
ctx: RequestContext,
requests: BoxStream<Result<Bytes, ConnectError>>,
format: CodecFormat,
) -> BoxFuture<'static, Result<EncodedResponse, ConnectError>>;
}
pub(crate) trait ErasedBidiStreamingHandler: Send + Sync {
fn call_erased(
&self,
ctx: RequestContext,
requests: BoxStream<Result<Bytes, ConnectError>>,
format: CodecFormat,
) -> StreamingHandlerResult;
}
pub trait Handler<Req, Res>: Send + Sync + 'static
where
Req: Message + Send + 'static,
Res: Message + Send + 'static,
{
type Body: Encodable<Res> + Send + 'static;
fn call(
&self,
ctx: RequestContext,
request: Req,
) -> BoxFuture<'static, ServiceResult<Self::Body>>;
}
pub struct FnHandler<F> {
f: Arc<F>,
}
impl<F> FnHandler<F> {
pub fn new(f: F) -> Self {
Self { f: Arc::new(f) }
}
}
impl<F, Fut, Req, Res, B> Handler<Req, Res> for FnHandler<F>
where
F: Fn(RequestContext, Req) -> Fut + Send + Sync + 'static,
Fut: Future<Output = ServiceResult<B>> + Send + 'static,
Req: Message + Send + 'static,
Res: Message + Send + 'static,
B: Encodable<Res> + Send + 'static,
{
type Body = B;
fn call(&self, ctx: RequestContext, request: Req) -> BoxFuture<'static, ServiceResult<B>> {
let f = Arc::clone(&self.f);
Box::pin(async move { f(ctx, request).await })
}
}
pub fn handler_fn<F, Fut, Req, Res, B>(f: F) -> FnHandler<F>
where
F: Fn(RequestContext, Req) -> Fut + Send + Sync + 'static,
Fut: Future<Output = ServiceResult<B>> + Send + 'static,
Req: Message + Send + 'static,
Res: Message + Send + 'static,
B: Encodable<Res> + Send + 'static,
{
FnHandler::new(f)
}
pub(crate) struct UnaryHandlerWrapper<H, Req, Res>
where
H: Handler<Req, Res>,
Req: Message + JsonDeserialize + Send + 'static,
Res: Message + JsonSerialize + Send + 'static,
{
handler: Arc<H>,
_phantom: std::marker::PhantomData<fn(Req) -> Res>,
}
impl<H, Req, Res> UnaryHandlerWrapper<H, Req, Res>
where
H: Handler<Req, Res>,
Req: Message + JsonDeserialize + Send + 'static,
Res: Message + JsonSerialize + Send + 'static,
{
pub fn new(handler: H) -> Self {
Self {
handler: Arc::new(handler),
_phantom: std::marker::PhantomData,
}
}
}
impl<H, Req, Res> ErasedHandler for UnaryHandlerWrapper<H, Req, Res>
where
H: Handler<Req, Res>,
Req: Message + JsonDeserialize + Send + 'static,
Res: Message + JsonSerialize + Send + 'static,
{
fn call_erased(
&self,
ctx: RequestContext,
request: crate::Payload,
format: CodecFormat,
) -> BoxFuture<'static, Result<EncodedResponse, ConnectError>> {
let handler = Arc::clone(&self.handler);
Box::pin(async move {
let req: Req = request.take_message()?;
handler.call(ctx, req).await?.encode::<Res>(format)
})
}
fn is_streaming(&self) -> bool {
false
}
}
pub trait StreamingHandler<Req, Res>: Send + Sync + 'static
where
Req: Message + Send + 'static,
Res: Message + Send + 'static,
{
type Item: Encodable<Res> + Send + 'static;
fn call(
&self,
ctx: RequestContext,
request: Req,
) -> BoxFuture<'static, ServiceResult<ServiceStream<Self::Item>>>;
}
pub struct FnStreamingHandler<F> {
f: Arc<F>,
}
impl<F> FnStreamingHandler<F> {
pub fn new(f: F) -> Self {
Self { f: Arc::new(f) }
}
}
impl<F, Fut, Req, Res, B> StreamingHandler<Req, Res> for FnStreamingHandler<F>
where
F: Fn(RequestContext, Req) -> Fut + Send + Sync + 'static,
Fut: Future<Output = ServiceResult<ServiceStream<B>>> + Send + 'static,
Req: Message + Send + 'static,
Res: Message + Send + 'static,
B: Encodable<Res> + Send + 'static,
{
type Item = B;
fn call(
&self,
ctx: RequestContext,
request: Req,
) -> BoxFuture<'static, ServiceResult<ServiceStream<B>>> {
let f = Arc::clone(&self.f);
Box::pin(async move { f(ctx, request).await })
}
}
pub fn streaming_handler_fn<F, Fut, Req, Res, B>(f: F) -> FnStreamingHandler<F>
where
F: Fn(RequestContext, Req) -> Fut + Send + Sync + 'static,
Fut: Future<Output = ServiceResult<ServiceStream<B>>> + Send + 'static,
Req: Message + Send + 'static,
Res: Message + Send + 'static,
B: Encodable<Res> + Send + 'static,
{
FnStreamingHandler::new(f)
}
pub(crate) struct ServerStreamingHandlerWrapper<H, Req, Res>
where
H: StreamingHandler<Req, Res>,
Req: Message + JsonDeserialize + Send + 'static,
Res: Message + Send + 'static,
{
handler: Arc<H>,
_phantom: std::marker::PhantomData<fn(Req) -> Res>,
}
impl<H, Req, Res> ServerStreamingHandlerWrapper<H, Req, Res>
where
H: StreamingHandler<Req, Res>,
Req: Message + JsonDeserialize + Send + 'static,
Res: Message + Send + 'static,
{
pub fn new(handler: H) -> Self {
Self {
handler: Arc::new(handler),
_phantom: std::marker::PhantomData,
}
}
}
impl<H, Req, Res> ErasedStreamingHandler for ServerStreamingHandlerWrapper<H, Req, Res>
where
H: StreamingHandler<Req, Res>,
Req: Message + JsonDeserialize + Send + 'static,
Res: Message + Send + 'static,
{
fn call_erased(
&self,
ctx: RequestContext,
request: Bytes,
format: CodecFormat,
) -> StreamingHandlerResult {
let handler = Arc::clone(&self.handler);
Box::pin(async move {
let req: Req = decode_request(&request, format, ctx.decode_options())?;
let resp = handler.call(ctx, req).await?;
Ok(resp.map_body(|s| encode_body_stream(s, format)))
})
}
}
pub trait ClientStreamingHandler<Req, Res>: Send + Sync + 'static
where
Req: Message + Send + 'static,
Res: Message + Send + 'static,
{
type Body: Encodable<Res> + Send + 'static;
fn call(
&self,
ctx: RequestContext,
requests: ServiceStream<Req>,
) -> BoxFuture<'static, ServiceResult<Self::Body>>;
}
pub struct FnClientStreamingHandler<F> {
f: Arc<F>,
}
impl<F> FnClientStreamingHandler<F> {
pub fn new(f: F) -> Self {
Self { f: Arc::new(f) }
}
}
impl<F, Fut, Req, Res, B> ClientStreamingHandler<Req, Res> for FnClientStreamingHandler<F>
where
F: Fn(RequestContext, ServiceStream<Req>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = ServiceResult<B>> + Send + 'static,
Req: Message + Send + 'static,
Res: Message + Send + 'static,
B: Encodable<Res> + Send + 'static,
{
type Body = B;
fn call(
&self,
ctx: RequestContext,
requests: ServiceStream<Req>,
) -> BoxFuture<'static, ServiceResult<B>> {
let f = Arc::clone(&self.f);
Box::pin(async move { f(ctx, requests).await })
}
}
pub fn client_streaming_handler_fn<F, Fut, Req, Res, B>(f: F) -> FnClientStreamingHandler<F>
where
F: Fn(RequestContext, ServiceStream<Req>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = ServiceResult<B>> + Send + 'static,
Req: Message + Send + 'static,
Res: Message + Send + 'static,
B: Encodable<Res> + Send + 'static,
{
FnClientStreamingHandler::new(f)
}
pub(crate) struct ClientStreamingHandlerWrapper<H, Req, Res>
where
H: ClientStreamingHandler<Req, Res>,
Req: Message + JsonDeserialize + Send + 'static,
Res: Message + JsonSerialize + Send + 'static,
{
handler: Arc<H>,
_phantom: std::marker::PhantomData<fn(Req) -> Res>,
}
impl<H, Req, Res> ClientStreamingHandlerWrapper<H, Req, Res>
where
H: ClientStreamingHandler<Req, Res>,
Req: Message + JsonDeserialize + Send + 'static,
Res: Message + JsonSerialize + Send + 'static,
{
pub fn new(handler: H) -> Self {
Self {
handler: Arc::new(handler),
_phantom: std::marker::PhantomData,
}
}
}
impl<H, Req, Res> ErasedClientStreamingHandler for ClientStreamingHandlerWrapper<H, Req, Res>
where
H: ClientStreamingHandler<Req, Res>,
Req: Message + JsonDeserialize + Send + 'static,
Res: Message + JsonSerialize + Send + 'static,
{
fn call_erased(
&self,
ctx: RequestContext,
requests: BoxStream<Result<Bytes, ConnectError>>,
format: CodecFormat,
) -> BoxFuture<'static, Result<EncodedResponse, ConnectError>> {
use futures::StreamExt as _;
let handler = Arc::clone(&self.handler);
Box::pin(async move {
let options = ctx.decode_options().clone();
let request_stream: ServiceStream<Req> =
Box::pin(requests.map(move |result| {
result.and_then(|raw| decode_request(&raw, format, &options))
}));
handler
.call(ctx, request_stream)
.await?
.encode::<Res>(format)
})
}
}
pub trait BidiStreamingHandler<Req, Res>: Send + Sync + 'static
where
Req: Message + Send + 'static,
Res: Message + Send + 'static,
{
type Item: Encodable<Res> + Send + 'static;
fn call(
&self,
ctx: RequestContext,
requests: ServiceStream<Req>,
) -> BoxFuture<'static, ServiceResult<ServiceStream<Self::Item>>>;
}
pub struct FnBidiStreamingHandler<F> {
f: Arc<F>,
}
impl<F> FnBidiStreamingHandler<F> {
pub fn new(f: F) -> Self {
Self { f: Arc::new(f) }
}
}
impl<F, Fut, Req, Res, B> BidiStreamingHandler<Req, Res> for FnBidiStreamingHandler<F>
where
F: Fn(RequestContext, ServiceStream<Req>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = ServiceResult<ServiceStream<B>>> + Send + 'static,
Req: Message + Send + 'static,
Res: Message + Send + 'static,
B: Encodable<Res> + Send + 'static,
{
type Item = B;
fn call(
&self,
ctx: RequestContext,
requests: ServiceStream<Req>,
) -> BoxFuture<'static, ServiceResult<ServiceStream<B>>> {
let f = Arc::clone(&self.f);
Box::pin(async move { f(ctx, requests).await })
}
}
pub fn bidi_streaming_handler_fn<F, Fut, Req, Res, B>(f: F) -> FnBidiStreamingHandler<F>
where
F: Fn(RequestContext, ServiceStream<Req>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = ServiceResult<ServiceStream<B>>> + Send + 'static,
Req: Message + Send + 'static,
Res: Message + Send + 'static,
B: Encodable<Res> + Send + 'static,
{
FnBidiStreamingHandler::new(f)
}
pub(crate) struct BidiStreamingHandlerWrapper<H, Req, Res>
where
H: BidiStreamingHandler<Req, Res>,
Req: Message + JsonDeserialize + Send + 'static,
Res: Message + Send + 'static,
{
handler: Arc<H>,
_phantom: std::marker::PhantomData<fn(Req) -> Res>,
}
impl<H, Req, Res> BidiStreamingHandlerWrapper<H, Req, Res>
where
H: BidiStreamingHandler<Req, Res>,
Req: Message + JsonDeserialize + Send + 'static,
Res: Message + Send + 'static,
{
pub fn new(handler: H) -> Self {
Self {
handler: Arc::new(handler),
_phantom: std::marker::PhantomData,
}
}
}
impl<H, Req, Res> ErasedBidiStreamingHandler for BidiStreamingHandlerWrapper<H, Req, Res>
where
H: BidiStreamingHandler<Req, Res>,
Req: Message + JsonDeserialize + Send + 'static,
Res: Message + Send + 'static,
{
fn call_erased(
&self,
ctx: RequestContext,
requests: BoxStream<Result<Bytes, ConnectError>>,
format: CodecFormat,
) -> StreamingHandlerResult {
use futures::StreamExt as _;
let handler = Arc::clone(&self.handler);
Box::pin(async move {
let options = ctx.decode_options().clone();
let request_stream: ServiceStream<Req> =
Box::pin(requests.map(move |result| {
result.and_then(|raw| decode_request(&raw, format, &options))
}));
let resp = handler.call(ctx, request_stream).await?;
Ok(resp.map_body(|s| encode_body_stream(s, format)))
})
}
}
pub(crate) fn decode_request_view<ReqView>(
request: Bytes,
format: CodecFormat,
options: &buffa::DecodeOptions,
) -> Result<OwnedView<ReqView>, ConnectError>
where
ReqView: MessageView<'static> + Send,
ReqView::Owned: Message + JsonDeserialize,
{
let body = request_proto_bytes::<ReqView::Owned>(request, format)?;
OwnedView::<ReqView>::decode_with_options(body, options).map_err(|e| decode_request_error(&e))
}
#[doc(hidden)] pub fn request_proto_bytes<Req>(request: Bytes, format: CodecFormat) -> Result<Bytes, ConnectError>
where
Req: Message + JsonDeserialize,
{
match format {
CodecFormat::Proto => Ok(request),
CodecFormat::Json => {
let owned: Req = decode_json(&request[..])?;
Ok(Bytes::from(owned.encode_to_vec()))
}
}
}
#[doc(hidden)] pub fn decode_borrowed_request_view<'a, ReqView>(
body: &'a [u8],
options: &buffa::DecodeOptions,
) -> Result<ReqView, ConnectError>
where
ReqView: MessageView<'a>,
{
options
.decode_view(body)
.map_err(|e| decode_request_error(&e))
}
pub trait ViewHandler<ReqView>: Send + Sync + 'static
where
ReqView: MessageView<'static> + Send + Sync + 'static,
{
fn call(
&self,
ctx: RequestContext,
request: OwnedView<ReqView>,
format: CodecFormat,
) -> BoxFuture<'static, Result<EncodedResponse, ConnectError>>;
}
pub struct FnViewHandler<F> {
f: Arc<F>,
}
impl<F> FnViewHandler<F> {
pub fn new(f: F) -> Self {
Self { f: Arc::new(f) }
}
}
impl<F, Fut, ReqView> ViewHandler<ReqView> for FnViewHandler<F>
where
F: Fn(RequestContext, OwnedView<ReqView>, CodecFormat) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<EncodedResponse, ConnectError>> + Send + 'static,
ReqView: MessageView<'static> + Send + Sync + 'static,
{
fn call(
&self,
ctx: RequestContext,
request: OwnedView<ReqView>,
format: CodecFormat,
) -> BoxFuture<'static, Result<EncodedResponse, ConnectError>> {
let f = Arc::clone(&self.f);
Box::pin(async move { f(ctx, request, format).await })
}
}
pub fn view_handler_fn<F, Fut, ReqView>(f: F) -> FnViewHandler<F>
where
F: Fn(RequestContext, OwnedView<ReqView>, CodecFormat) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<EncodedResponse, ConnectError>> + Send + 'static,
ReqView: MessageView<'static> + Send + Sync + 'static,
{
FnViewHandler::new(f)
}
pub(crate) struct UnaryViewHandlerWrapper<H, ReqView>
where
H: ViewHandler<ReqView>,
ReqView: MessageView<'static> + Send + Sync + 'static,
ReqView::Owned: Message + JsonDeserialize,
{
handler: Arc<H>,
_phantom: std::marker::PhantomData<fn(ReqView)>,
}
impl<H, ReqView> UnaryViewHandlerWrapper<H, ReqView>
where
H: ViewHandler<ReqView>,
ReqView: MessageView<'static> + Send + Sync + 'static,
ReqView::Owned: Message + JsonDeserialize,
{
pub fn new(handler: H) -> Self {
Self {
handler: Arc::new(handler),
_phantom: std::marker::PhantomData,
}
}
}
impl<H, ReqView> ErasedHandler for UnaryViewHandlerWrapper<H, ReqView>
where
H: ViewHandler<ReqView>,
ReqView: MessageView<'static> + Send + Sync + 'static,
ReqView::Owned: Message + JsonDeserialize,
{
fn call_erased(
&self,
ctx: RequestContext,
request: crate::Payload,
format: CodecFormat,
) -> BoxFuture<'static, Result<EncodedResponse, ConnectError>> {
let handler = Arc::clone(&self.handler);
Box::pin(async move {
let req =
decode_request_view::<ReqView>(request.encoded()?, format, ctx.decode_options())?;
handler.call(ctx, req, format).await
})
}
fn is_streaming(&self) -> bool {
false
}
}
pub trait ViewStreamingHandler<ReqView, Res>: Send + Sync + 'static
where
ReqView: MessageView<'static> + Send + Sync + 'static,
Res: Message + Send + 'static,
{
type Item: Encodable<Res> + Send + 'static;
fn call(
&self,
ctx: RequestContext,
request: OwnedView<ReqView>,
) -> BoxFuture<'static, ServiceResult<ServiceStream<Self::Item>>>;
}
pub struct FnViewStreamingHandler<F> {
f: Arc<F>,
}
impl<F> FnViewStreamingHandler<F> {
pub fn new(f: F) -> Self {
Self { f: Arc::new(f) }
}
}
impl<F, Fut, ReqView, Res, B> ViewStreamingHandler<ReqView, Res> for FnViewStreamingHandler<F>
where
F: Fn(RequestContext, OwnedView<ReqView>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = ServiceResult<ServiceStream<B>>> + Send + 'static,
ReqView: MessageView<'static> + Send + Sync + 'static,
Res: Message + Send + 'static,
B: Encodable<Res> + Send + 'static,
{
type Item = B;
fn call(
&self,
ctx: RequestContext,
request: OwnedView<ReqView>,
) -> BoxFuture<'static, ServiceResult<ServiceStream<B>>> {
let f = Arc::clone(&self.f);
Box::pin(async move { f(ctx, request).await })
}
}
pub fn view_streaming_handler_fn<F, Fut, ReqView, Res, B>(f: F) -> FnViewStreamingHandler<F>
where
F: Fn(RequestContext, OwnedView<ReqView>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = ServiceResult<ServiceStream<B>>> + Send + 'static,
ReqView: MessageView<'static> + Send + Sync + 'static,
Res: Message + Send + 'static,
B: Encodable<Res> + Send + 'static,
{
FnViewStreamingHandler::new(f)
}
pub(crate) struct ServerStreamingViewHandlerWrapper<H, ReqView, Res>
where
H: ViewStreamingHandler<ReqView, Res>,
ReqView: MessageView<'static> + Send + Sync + 'static,
ReqView::Owned: Message + JsonDeserialize,
Res: Message + Send + 'static,
{
handler: Arc<H>,
_phantom: std::marker::PhantomData<fn(ReqView) -> Res>,
}
impl<H, ReqView, Res> ServerStreamingViewHandlerWrapper<H, ReqView, Res>
where
H: ViewStreamingHandler<ReqView, Res>,
ReqView: MessageView<'static> + Send + Sync + 'static,
ReqView::Owned: Message + JsonDeserialize,
Res: Message + Send + 'static,
{
pub fn new(handler: H) -> Self {
Self {
handler: Arc::new(handler),
_phantom: std::marker::PhantomData,
}
}
}
impl<H, ReqView, Res> ErasedStreamingHandler for ServerStreamingViewHandlerWrapper<H, ReqView, Res>
where
H: ViewStreamingHandler<ReqView, Res>,
ReqView: MessageView<'static> + Send + Sync + 'static,
ReqView::Owned: Message + JsonDeserialize,
Res: Message + Send + 'static,
{
fn call_erased(
&self,
ctx: RequestContext,
request: Bytes,
format: CodecFormat,
) -> StreamingHandlerResult {
let handler = Arc::clone(&self.handler);
Box::pin(async move {
let req = decode_request_view::<ReqView>(request, format, ctx.decode_options())?;
let resp = handler.call(ctx, req).await?;
Ok(resp.map_body(|s| encode_body_stream(s, format)))
})
}
}
pub trait ViewClientStreamingHandler<ReqView>: Send + Sync + 'static
where
ReqView: MessageView<'static> + Send + Sync + 'static,
{
fn call(
&self,
ctx: RequestContext,
requests: ServiceStream<OwnedView<ReqView>>,
format: CodecFormat,
) -> BoxFuture<'static, Result<EncodedResponse, ConnectError>>;
}
pub struct FnViewClientStreamingHandler<F> {
f: Arc<F>,
}
impl<F> FnViewClientStreamingHandler<F> {
pub fn new(f: F) -> Self {
Self { f: Arc::new(f) }
}
}
impl<F, Fut, ReqView> ViewClientStreamingHandler<ReqView> for FnViewClientStreamingHandler<F>
where
F: Fn(RequestContext, ServiceStream<OwnedView<ReqView>>, CodecFormat) -> Fut
+ Send
+ Sync
+ 'static,
Fut: Future<Output = Result<EncodedResponse, ConnectError>> + Send + 'static,
ReqView: MessageView<'static> + Send + Sync + 'static,
{
fn call(
&self,
ctx: RequestContext,
requests: ServiceStream<OwnedView<ReqView>>,
format: CodecFormat,
) -> BoxFuture<'static, Result<EncodedResponse, ConnectError>> {
let f = Arc::clone(&self.f);
Box::pin(async move { f(ctx, requests, format).await })
}
}
pub fn view_client_streaming_handler_fn<F, Fut, ReqView>(f: F) -> FnViewClientStreamingHandler<F>
where
F: Fn(RequestContext, ServiceStream<OwnedView<ReqView>>, CodecFormat) -> Fut
+ Send
+ Sync
+ 'static,
Fut: Future<Output = Result<EncodedResponse, ConnectError>> + Send + 'static,
ReqView: MessageView<'static> + Send + Sync + 'static,
{
FnViewClientStreamingHandler::new(f)
}
pub(crate) struct ClientStreamingViewHandlerWrapper<H, ReqView>
where
H: ViewClientStreamingHandler<ReqView>,
ReqView: MessageView<'static> + Send + Sync + 'static,
ReqView::Owned: Message + JsonDeserialize,
{
handler: Arc<H>,
_phantom: std::marker::PhantomData<fn(ReqView)>,
}
impl<H, ReqView> ClientStreamingViewHandlerWrapper<H, ReqView>
where
H: ViewClientStreamingHandler<ReqView>,
ReqView: MessageView<'static> + Send + Sync + 'static,
ReqView::Owned: Message + JsonDeserialize,
{
pub fn new(handler: H) -> Self {
Self {
handler: Arc::new(handler),
_phantom: std::marker::PhantomData,
}
}
}
impl<H, ReqView> ErasedClientStreamingHandler for ClientStreamingViewHandlerWrapper<H, ReqView>
where
H: ViewClientStreamingHandler<ReqView>,
ReqView: MessageView<'static> + Send + Sync + 'static,
ReqView::Owned: Message + JsonDeserialize,
{
fn call_erased(
&self,
ctx: RequestContext,
requests: BoxStream<Result<Bytes, ConnectError>>,
format: CodecFormat,
) -> BoxFuture<'static, Result<EncodedResponse, ConnectError>> {
use futures::StreamExt as _;
let handler = Arc::clone(&self.handler);
Box::pin(async move {
let options = ctx.decode_options().clone();
let request_stream: ServiceStream<OwnedView<ReqView>> =
Box::pin(requests.map(move |result| {
result.and_then(|raw| decode_request_view::<ReqView>(raw, format, &options))
}));
handler.call(ctx, request_stream, format).await
})
}
}
pub trait ViewBidiStreamingHandler<ReqView, Res>: Send + Sync + 'static
where
ReqView: MessageView<'static> + Send + Sync + 'static,
Res: Message + Send + 'static,
{
type Item: Encodable<Res> + Send + 'static;
fn call(
&self,
ctx: RequestContext,
requests: ServiceStream<OwnedView<ReqView>>,
) -> BoxFuture<'static, ServiceResult<ServiceStream<Self::Item>>>;
}
pub struct FnViewBidiStreamingHandler<F> {
f: Arc<F>,
}
impl<F> FnViewBidiStreamingHandler<F> {
pub fn new(f: F) -> Self {
Self { f: Arc::new(f) }
}
}
impl<F, Fut, ReqView, Res, B> ViewBidiStreamingHandler<ReqView, Res>
for FnViewBidiStreamingHandler<F>
where
F: Fn(RequestContext, ServiceStream<OwnedView<ReqView>>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = ServiceResult<ServiceStream<B>>> + Send + 'static,
ReqView: MessageView<'static> + Send + Sync + 'static,
Res: Message + Send + 'static,
B: Encodable<Res> + Send + 'static,
{
type Item = B;
fn call(
&self,
ctx: RequestContext,
requests: ServiceStream<OwnedView<ReqView>>,
) -> BoxFuture<'static, ServiceResult<ServiceStream<B>>> {
let f = Arc::clone(&self.f);
Box::pin(async move { f(ctx, requests).await })
}
}
pub fn view_bidi_streaming_handler_fn<F, Fut, ReqView, Res, B>(
f: F,
) -> FnViewBidiStreamingHandler<F>
where
F: Fn(RequestContext, ServiceStream<OwnedView<ReqView>>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = ServiceResult<ServiceStream<B>>> + Send + 'static,
ReqView: MessageView<'static> + Send + Sync + 'static,
Res: Message + Send + 'static,
B: Encodable<Res> + Send + 'static,
{
FnViewBidiStreamingHandler::new(f)
}
pub(crate) struct BidiStreamingViewHandlerWrapper<H, ReqView, Res>
where
H: ViewBidiStreamingHandler<ReqView, Res>,
ReqView: MessageView<'static> + Send + Sync + 'static,
ReqView::Owned: Message + JsonDeserialize,
Res: Message + Send + 'static,
{
handler: Arc<H>,
_phantom: std::marker::PhantomData<fn(ReqView) -> Res>,
}
impl<H, ReqView, Res> BidiStreamingViewHandlerWrapper<H, ReqView, Res>
where
H: ViewBidiStreamingHandler<ReqView, Res>,
ReqView: MessageView<'static> + Send + Sync + 'static,
ReqView::Owned: Message + JsonDeserialize,
Res: Message + Send + 'static,
{
pub fn new(handler: H) -> Self {
Self {
handler: Arc::new(handler),
_phantom: std::marker::PhantomData,
}
}
}
impl<H, ReqView, Res> ErasedBidiStreamingHandler
for BidiStreamingViewHandlerWrapper<H, ReqView, Res>
where
H: ViewBidiStreamingHandler<ReqView, Res>,
ReqView: MessageView<'static> + Send + Sync + 'static,
ReqView::Owned: Message + JsonDeserialize,
Res: Message + Send + 'static,
{
fn call_erased(
&self,
ctx: RequestContext,
requests: BoxStream<Result<Bytes, ConnectError>>,
format: CodecFormat,
) -> StreamingHandlerResult {
use futures::StreamExt as _;
let handler = Arc::clone(&self.handler);
Box::pin(async move {
let options = ctx.decode_options().clone();
let request_stream: ServiceStream<OwnedView<ReqView>> =
Box::pin(requests.map(move |result| {
result.and_then(|raw| decode_request_view::<ReqView>(raw, format, &options))
}));
let resp = handler.call(ctx, request_stream).await?;
Ok(resp.map_body(|s| encode_body_stream(s, format)))
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_budget::elements_over_default_budget;
use buffa_types::google::protobuf::__buffa::view::StringValueView;
use buffa_types::google::protobuf::StringValue;
#[test]
fn test_decode_request_proto() {
let msg = StringValue::from("hello");
let encoded = Bytes::from(msg.encode_to_vec());
let decoded: StringValue =
decode_request(&encoded, CodecFormat::Proto, &buffa::DecodeOptions::new()).unwrap();
assert_eq!(decoded.value, "hello");
}
#[cfg(feature = "json")]
#[test]
fn test_decode_request_json() {
let encoded = Bytes::from_static(b"\"world\"");
let decoded: StringValue =
decode_request(&encoded, CodecFormat::Json, &buffa::DecodeOptions::new()).unwrap();
assert_eq!(decoded.value, "world");
}
#[test]
fn test_decode_request_proto_invalid() {
let garbage = Bytes::from_static(&[0xFF, 0xFF, 0xFF]);
let err = decode_request::<StringValue>(
&garbage,
CodecFormat::Proto,
&buffa::DecodeOptions::new(),
)
.unwrap_err();
assert_eq!(err.code, crate::error::ErrorCode::InvalidArgument);
}
#[cfg(feature = "json")]
#[test]
fn test_decode_request_json_invalid() {
let garbage = Bytes::from_static(b"not json");
let err = decode_request::<StringValue>(
&garbage,
CodecFormat::Json,
&buffa::DecodeOptions::new(),
)
.unwrap_err();
assert_eq!(err.code, crate::error::ErrorCode::InvalidArgument);
}
#[test]
fn overflow_payload_is_invalid_argument_at_decode_boundary() {
let body = crate::request::tests::unknown_field_overflow_body();
let err = decode_request_view::<StringValueView<'static>>(
body,
CodecFormat::Proto,
&buffa::DecodeOptions::new(),
)
.unwrap_err();
assert_eq!(err.code, crate::error::ErrorCode::InvalidArgument);
}
#[test]
fn test_decode_request_view_proto() {
let msg = StringValue::from("view-test");
let encoded = Bytes::from(msg.encode_to_vec());
let view = decode_request_view::<StringValueView>(
encoded,
CodecFormat::Proto,
&buffa::DecodeOptions::new(),
)
.unwrap();
assert_eq!(view.reborrow().value, "view-test");
}
#[cfg(feature = "json")]
#[test]
fn test_decode_request_view_json() {
let encoded = Bytes::from_static(b"\"json-view\"");
let view = decode_request_view::<StringValueView>(
encoded,
CodecFormat::Json,
&buffa::DecodeOptions::new(),
)
.unwrap();
assert_eq!(view.reborrow().value, "json-view");
}
#[cfg(not(feature = "json"))]
#[test]
fn decode_request_json_is_unimplemented_without_feature() {
let body = Bytes::from_static(b"\"world\"");
let err =
decode_request::<StringValue>(&body, CodecFormat::Json, &buffa::DecodeOptions::new())
.unwrap_err();
assert_eq!(err.code, crate::error::ErrorCode::Unimplemented);
}
#[cfg(not(feature = "json"))]
#[test]
fn decode_request_view_json_is_unimplemented_without_feature() {
let body = Bytes::from_static(b"\"world\"");
let err = decode_request_view::<StringValueView>(
body,
CodecFormat::Json,
&buffa::DecodeOptions::new(),
)
.unwrap_err();
assert_eq!(err.code, crate::error::ErrorCode::Unimplemented);
}
#[test]
fn test_decode_request_view_proto_invalid() {
let garbage = Bytes::from_static(&[0xFF, 0xFF, 0xFF]);
let err = decode_request_view::<StringValueView>(
garbage,
CodecFormat::Proto,
&buffa::DecodeOptions::new(),
)
.unwrap_err();
assert_eq!(err.code, crate::error::ErrorCode::InvalidArgument);
}
#[tokio::test]
async fn encode_body_stream_owned_items() {
use futures::StreamExt as _;
let s = futures::stream::iter([
Ok(StringValue::from("a")),
Ok(StringValue::from("b")),
Err(ConnectError::internal("boom")),
]);
let mut out = encode_body_stream::<StringValue, _, _>(s, CodecFormat::Proto);
let a = out.next().await.unwrap().unwrap().into_contiguous();
let b = out.next().await.unwrap().unwrap().into_contiguous();
assert_eq!(StringValue::decode_from_slice(&a).unwrap().value, "a");
assert_eq!(StringValue::decode_from_slice(&b).unwrap().value, "b");
assert!(out.next().await.unwrap().is_err());
assert!(out.next().await.is_none());
}
#[tokio::test]
async fn encode_body_stream_forwards_segments() {
use crate::EncodedBody;
use futures::StreamExt as _;
struct Split(Bytes, Bytes);
impl Encodable<StringValue> for Split {
fn encode(&self, _: CodecFormat) -> Result<Bytes, ConnectError> {
unreachable!("the streaming path takes encode_segments")
}
fn encode_segments(&self, _: CodecFormat) -> Result<EncodedBody, ConnectError> {
Ok(EncodedBody::Segmented(vec![self.0.clone(), self.1.clone()]))
}
}
let (a, b) = (Bytes::from_static(b"\x0a\x03"), Bytes::from_static(b"abc"));
let s = futures::stream::iter([Ok(Split(a.clone(), b.clone()))]);
let mut out = encode_body_stream::<StringValue, _, _>(s, CodecFormat::Proto);
let item = out.next().await.unwrap().unwrap();
let [s0, s1] = item.segments() else {
panic!("expected two segments");
};
assert!(std::ptr::eq(s0.as_ptr(), a.as_ptr()));
assert!(std::ptr::eq(s1.as_ptr(), b.as_ptr()));
assert!(out.next().await.is_none());
}
#[tokio::test]
async fn encode_body_stream_pre_encoded_items() {
use crate::PreEncoded;
use futures::StreamExt as _;
let bytes_a = StringValue::from("a").encode_to_bytes();
let bytes_b = StringValue::from("b").encode_to_bytes();
let s = futures::stream::iter([
Ok(PreEncoded::<StringValue>::from_bytes_unchecked(
bytes_a.clone(),
)),
Ok(PreEncoded::<StringValue>::from_bytes_unchecked(
bytes_b.clone(),
)),
]);
let mut out =
encode_body_stream::<StringValue, PreEncoded<StringValue>, _>(s, CodecFormat::Proto);
assert_eq!(
out.next().await.unwrap().unwrap().into_contiguous(),
bytes_a
);
assert_eq!(
out.next().await.unwrap().unwrap().into_contiguous(),
bytes_b
);
assert!(out.next().await.is_none());
}
#[cfg(feature = "json")]
#[tokio::test]
async fn encode_body_stream_pre_encoded_json_decodes_per_item() {
use crate::PreEncoded;
use futures::StreamExt as _;
let m_a = StringValue::from("a");
let m_b = StringValue::from("b");
let s = futures::stream::iter([
Ok(PreEncoded::<StringValue>::from_bytes_unchecked(
m_a.encode_to_bytes(),
)),
Ok(PreEncoded::<StringValue>::from_bytes_unchecked(
m_b.encode_to_bytes(),
)),
]);
let mut out =
encode_body_stream::<StringValue, PreEncoded<StringValue>, _>(s, CodecFormat::Json);
assert_eq!(
out.next().await.unwrap().unwrap().into_contiguous(),
Bytes::from(serde_json::to_vec(&m_a).unwrap())
);
assert_eq!(
out.next().await.unwrap().unwrap().into_contiguous(),
Bytes::from(serde_json::to_vec(&m_b).unwrap())
);
assert!(out.next().await.is_none());
}
#[test]
fn streaming_handler_item_is_inferred_from_closure() {
use crate::PreEncoded;
fn assert_handler<H, Req, Res, B>(_: &H)
where
H: StreamingHandler<Req, Res, Item = B>,
Req: Message + Send + 'static,
Res: Message + Send + 'static,
B: Encodable<Res> + Send + 'static,
{
}
let owned = streaming_handler_fn(|_ctx: RequestContext, _req: StringValue| async move {
Response::stream_ok(futures::stream::iter([Ok(StringValue::from("x"))]))
});
assert_handler::<_, StringValue, StringValue, StringValue>(&owned);
let pre = streaming_handler_fn(|_ctx: RequestContext, _req: StringValue| async move {
Response::stream_ok(futures::stream::iter([Ok(
PreEncoded::<StringValue>::from_bytes_unchecked(
StringValue::from("x").encode_to_bytes(),
),
)]))
});
assert_handler::<_, StringValue, StringValue, PreEncoded<StringValue>>(&pre);
}
#[test]
fn owned_message_decoding_honours_the_configured_limit() {
use buffa_types::google::protobuf::{ListValue, Value};
let n = elements_over_default_budget::<Value>();
let list = ListValue {
values: (0..n).map(|_| Value::default()).collect(),
..Default::default()
};
let encoded = Bytes::from(buffa::Message::encode_to_vec(&list));
let raised = crate::Limits::default().with_element_memory_limit(usize::MAX);
assert!(
decode_request::<ListValue>(
&encoded,
CodecFormat::Proto,
&crate::Limits::default().decode_options()
)
.is_err(),
"the default budget must still reject"
);
let decoded: ListValue =
decode_request(&encoded, CodecFormat::Proto, &raised.decode_options())
.expect("raised budget must admit");
assert_eq!(decoded.values.len(), n);
let payload = crate::Payload::new(encoded.clone(), CodecFormat::Proto);
assert!(
payload.take_message::<ListValue>().is_err(),
"a payload with no limits attached decodes under buffa defaults"
);
let payload = crate::Payload::new(encoded, CodecFormat::Proto)
.with_decode_options(raised.decode_options());
let decoded: ListValue = payload
.take_message()
.expect("a payload carrying raised limits must admit");
assert_eq!(decoded.values.len(), n);
}
#[test]
fn an_over_budget_decode_says_which_limit_to_raise() {
use buffa_types::google::protobuf::__buffa::view::{ListValueView, ValueView};
use buffa_types::google::protobuf::{ListValue, Value};
let list = ListValue {
values: (0..elements_over_default_budget::<ValueView<'_>>())
.map(|_| Value::default())
.collect(),
..Default::default()
};
let encoded = Bytes::from(buffa::Message::encode_to_vec(&list));
let err = decode_borrowed_request_view::<ListValueView<'_>>(
&encoded,
&crate::Limits::default().decode_options(),
)
.expect_err("over budget");
let message = err.message.unwrap_or_default();
assert!(
message.contains("element_memory_limit"),
"the budget rejection must name the knob, got {message:?}"
);
let garbage = Bytes::from_static(&[0xFF, 0xFF, 0xFF]);
let err = decode_borrowed_request_view::<ListValueView<'_>>(
&garbage,
&crate::Limits::default().decode_options(),
)
.expect_err("malformed");
let message = err.message.unwrap_or_default();
assert!(
!message.contains("element_memory_limit"),
"a malformed request must not point at a limit, got {message:?}"
);
}
#[test]
fn element_memory_limit_is_taken_from_the_configured_limits() {
use buffa_types::google::protobuf::__buffa::view::{ListValueView, ValueView};
use buffa_types::google::protobuf::{ListValue, Value};
let n = elements_over_default_budget::<ValueView<'_>>();
let list = ListValue {
values: (0..n).map(|_| Value::default()).collect(),
..Default::default()
};
let encoded = Bytes::from(buffa::Message::encode_to_vec(&list));
let defaults = crate::Limits::default();
let err =
decode_borrowed_request_view::<ListValueView<'_>>(&encoded, &defaults.decode_options())
.expect_err("the fixture must exceed the default element-memory budget");
assert_eq!(err.code, crate::error::ErrorCode::InvalidArgument);
let raised = crate::Limits::default().with_element_memory_limit(usize::MAX);
let view =
decode_borrowed_request_view::<ListValueView<'_>>(&encoded, &raised.decode_options())
.expect("raising the limit must admit the same bytes");
assert_eq!(view.values.len(), n);
}
#[test]
fn unlimited_limits_lift_the_element_budget() {
assert_eq!(
crate::Limits::unlimited().element_memory_limit(),
usize::MAX
);
}
#[test]
fn a_bare_request_context_decodes_under_buffa_defaults() {
let ctx = RequestContext::new(http::HeaderMap::new());
let listing = format!("{:?}", ctx.decode_options());
assert!(
listing.contains(&buffa::DEFAULT_ELEMENT_MEMORY_LIMIT.to_string()),
"expected buffa's default element-memory budget, got {listing}"
);
}
}