use std::future::Future;
use std::sync::Arc;
use std::task::{Context, Poll, Wake, Waker};
use foundation_core::io::ioutils::SharedByteBufferStream;
use foundation_http::shared::context::ContextBag;
use foundation_http::shared::serve::{ConnectionResult, Serve};
use foundation_netio::netcap::RawStream;
use foundation_netio::shared::http::{
Http11, RenderHttp, SendSafeBody, SimpleHeader, SimpleIncomingRequest, SimpleOutgoingResponse,
};
use crate::shared::router::ConnectRpcHandler;
pub struct ConnectRpcServe {
handler: Arc<ConnectRpcHandler>,
}
impl ConnectRpcServe {
#[must_use]
pub fn new(handler: ConnectRpcHandler) -> Self {
Self {
handler: Arc::new(handler),
}
}
#[must_use]
pub fn from_arc(handler: Arc<ConnectRpcHandler>) -> Self {
Self { handler }
}
}
impl Serve for ConnectRpcServe {
fn serve(
&self,
bag: Arc<ContextBag>,
req: SimpleIncomingRequest,
mut conn: SharedByteBufferStream<RawStream>,
) -> ConnectionResult {
let mut response = block_on(self.handler.dispatch(bag, req));
ensure_content_length(&mut response);
match Http11::response(response).http_render_to_writer(&mut conn) {
Ok(_) => ConnectionResult::Keep,
Err(_) => ConnectionResult::Close(None),
}
}
}
fn ensure_content_length(response: &mut SimpleOutgoingResponse) {
let len = match &response.body {
Some(SendSafeBody::Bytes(bytes)) => Some(bytes.len()),
Some(SendSafeBody::Text(text)) => Some(text.len()),
_ => None,
};
if let Some(len) = len {
if response.headers.get(&SimpleHeader::CONTENT_LENGTH).is_none() {
response
.headers
.insert(SimpleHeader::CONTENT_LENGTH, vec![len.to_string()]);
}
}
}
pub(crate) fn block_on<F: Future>(future: F) -> F::Output {
struct ThreadWaker(std::thread::Thread);
impl Wake for ThreadWaker {
fn wake(self: Arc<Self>) {
self.0.unpark();
}
fn wake_by_ref(self: &Arc<Self>) {
self.0.unpark();
}
}
let waker = Waker::from(Arc::new(ThreadWaker(std::thread::current())));
let mut cx = Context::from_waker(&waker);
let mut future = std::pin::pin!(future);
loop {
match future.as_mut().poll(&mut cx) {
Poll::Ready(value) => return value,
Poll::Pending => std::thread::park(),
}
}
}