#![cfg(feature = "alloc")]
use bytes::Bytes;
use proxima_core::ProximaError;
use crate::pipe::primitives::Pipe;
use crate::pipe::request::{Request, Response};
use crate::pipe::{SendPipe, alloc_tier};
pub trait Handler:
SendPipe<In = Request<Bytes>, Out = Response<Bytes>, Err = ProximaError>
{
}
impl<Implementor> Handler for Implementor where
Implementor: SendPipe<In = Request<Bytes>, Out = Response<Bytes>, Err = ProximaError>
{
}
pub type PipeHandle = alloc_tier::PipeHandle<Request<Bytes>, Response<Bytes>>;
pub fn into_handle<Implementor>(pipe: Implementor) -> PipeHandle
where
Implementor: SendPipe<In = Request<Bytes>, Out = Response<Bytes>, Err = ProximaError> + 'static,
{
alloc_tier::into_handle(pipe)
}
pub trait ThreadLocalHandler:
Pipe<In = Request<Bytes>, Out = Response<Bytes>, Err = ProximaError>
{
}
impl<Implementor> ThreadLocalHandler for Implementor where
Implementor: Pipe<In = Request<Bytes>, Out = Response<Bytes>, Err = ProximaError>
{
}
pub type ThreadLocalPipeHandle = alloc_tier::LocalPipeHandle<Request<Bytes>, Response<Bytes>>;
pub fn into_thread_local_handle<Implementor>(pipe: Implementor) -> ThreadLocalPipeHandle
where
Implementor: Pipe<In = Request<Bytes>, Out = Response<Bytes>, Err = ProximaError> + 'static,
{
alloc_tier::into_local_handle(pipe)
}
#[cfg(all(test, not(loom)))]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
use core::future::Future;
struct EchoPipe {
label: alloc::string::String,
}
impl SendPipe for EchoPipe {
type In = Request<Bytes>;
type Out = Response<Bytes>;
type Err = ProximaError;
fn call(
&self,
request: Request<Bytes>,
) -> impl Future<Output = Result<Response<Bytes>, ProximaError>> + Send {
let label = self.label.clone();
async move {
let (_, bytes) = request.body_bytes().await?;
Ok(Response::ok(bytes).with_header("x-pipe", label))
}
}
}
impl Pipe for EchoPipe {
type In = Request<Bytes>;
type Out = Response<Bytes>;
type Err = ProximaError;
fn call(
&self,
request: Request<Bytes>,
) -> impl Future<Output = Result<Response<Bytes>, ProximaError>> {
SendPipe::call(self, request)
}
}
#[proxima::test]
async fn dyn_handle_dispatches_to_inner() {
let inner: PipeHandle = into_handle(EchoPipe {
label: "echo".into(),
});
let request = Request::builder()
.method("POST")
.path("/")
.body("ping")
.build()
.expect("builder");
let response = SendPipe::call(&inner, request).await.expect("call");
let body = response.payload;
assert_eq!(&body[..], b"ping");
}
#[proxima::test(flavor = "multi_thread", worker_threads = 2)]
async fn pipe_call_future_is_send_for_tokio_spawn() {
let handle: PipeHandle = into_handle(EchoPipe {
label: "spawnable".into(),
});
let request = Request::builder()
.method("POST")
.path("/")
.body("ok")
.build()
.expect("builder");
let join = tokio::spawn(async move { SendPipe::call(&handle, request).await });
let response = join.await.expect("join").expect("call");
let body = response.payload;
assert_eq!(&body[..], b"ok");
}
use alloc::rc::Rc as TestRc;
use core::cell::RefCell;
struct CounterPipe {
count: TestRc<RefCell<usize>>,
}
impl Pipe for CounterPipe {
type In = Request<Bytes>;
type Out = Response<Bytes>;
type Err = ProximaError;
fn call(
&self,
_request: Request<Bytes>,
) -> impl Future<Output = Result<Response<Bytes>, ProximaError>> {
let count = self.count.clone();
async move {
*count.borrow_mut() += 1;
Ok(Response::ok("counted"))
}
}
}
#[test]
fn thread_local_pipe_runs_with_rc_state() {
futures::executor::block_on(async {
let count: TestRc<RefCell<usize>> = TestRc::new(RefCell::new(0));
let pipe = CounterPipe {
count: count.clone(),
};
let handle: ThreadLocalPipeHandle = into_thread_local_handle(pipe);
for _ in 0..3 {
let request = Request::builder()
.method("GET")
.path("/")
.build()
.expect("builder");
let _ = Pipe::call(&handle, request).await.expect("call");
}
assert_eq!(*count.borrow(), 3);
});
}
#[proxima::test]
async fn blanket_lifts_a_send_pipe_into_thread_local() {
let pipe = EchoPipe {
label: "blanket".into(),
};
let request = Request::builder()
.method("POST")
.path("/")
.body("via-blanket")
.build()
.expect("builder");
let response = SendPipe::call(&pipe, request).await.expect("call");
let body = response.payload;
assert_eq!(&body[..], b"via-blanket");
}
}