use std::future::Future;
use std::io::{self, Write};
use std::pin::Pin;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::task::{Context, Poll};
use std::time::Duration;
use bytes::Bytes;
use foundation_core::io::ioutils::SharedByteBufferStream;
use foundation_http::shared::context::ContextBag;
use foundation_http::shared::serve::{ConnectionResult, Serve};
use foundation_netio::native::readiness::IoInterest;
use foundation_netio::netcap::RawStream;
use foundation_netio::shared::http::{
ensure_chunked_transfer_encoding, Http11, RenderHttp, SendSafeBody, SimpleHeader,
SimpleIncomingRequest, SimpleOutgoingResponse,
};
use super::conn_io::{ConnIo, IoReady};
use crate::shared::context::CancelSignal;
use crate::shared::router::dispatch::{BodyReader, BodyStep};
use crate::shared::router::{CallHooks, ConnectRpcHandler, RequestBody, ResponseSink};
use crate::shared::transport::BoxFuture;
use futures::future::{select, Either};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ServeTimeouts {
pub body_read: Duration,
pub write: Duration,
}
impl Default for ServeTimeouts {
fn default() -> Self {
Self {
body_read: Duration::from_secs(60),
write: Duration::from_secs(60),
}
}
}
impl ServeTimeouts {
#[must_use]
pub fn with_body_read(mut self, timeout: Duration) -> Self {
self.body_read = timeout;
self
}
#[must_use]
pub fn with_write(mut self, timeout: Duration) -> Self {
self.write = timeout;
self
}
}
pub struct ConnectRpcServe {
handler: Arc<ConnectRpcHandler>,
timeouts: ServeTimeouts,
}
impl ConnectRpcServe {
#[must_use]
pub fn new(handler: ConnectRpcHandler) -> Self {
Self::from_arc(Arc::new(handler))
}
#[must_use]
pub fn from_arc(handler: Arc<ConnectRpcHandler>) -> Self {
Self {
handler,
timeouts: ServeTimeouts::default(),
}
}
#[must_use]
pub fn with_timeouts(mut self, timeouts: ServeTimeouts) -> Self {
self.timeouts = timeouts;
self
}
}
impl Serve for ConnectRpcServe {
fn serve(
&self,
bag: Arc<ContextBag>,
mut req: SimpleIncomingRequest,
mut conn: SharedByteBufferStream<RawStream>,
) -> ConnectionResult {
let io = match ConnIo::new(&conn) {
Ok(io) => io,
Err(e) => {
tracing::error!(error = %e, "could not set up the connection driver; closing");
return ConnectionResult::Close(None);
}
};
let body = req.body.take();
let drained = AtomicBool::new(matches!(body, None | Some(SendSafeBody::None)));
let body: RequestBody<'_> = Box::pin(H1Body {
reader: BodyReader::new(body),
io: &io,
timeout: self.timeouts.body_read,
wait: None,
drained: &drained,
});
let mut sink = H1Sink {
conn: &mut conn,
io: &io,
timeout: self.timeouts.write,
};
let cancel = CancelSignal::new();
let hooks = CallHooks::new(cancel.clone()).with_timer(&io);
let dispatch = std::pin::pin!(self.handler.dispatch_to(bag, req, body, &mut sink, hooks));
let hangup = std::pin::pin!(async {
match io.hangup().await {
Ok(()) => {}
Err(e) => {
tracing::debug!(error = %e, "cannot watch the connection for a hangup");
std::future::pending::<()>().await;
}
}
});
let outcome = match io.block_on(select(dispatch, hangup)) {
Either::Left((outcome, _)) => outcome,
Either::Right(((), _dispatch)) => {
tracing::debug!("the client closed the connection; cancelling the call");
cancel.cancel();
return ConnectionResult::Close(None);
}
};
match outcome {
Ok(()) if drained.load(Ordering::Acquire) => ConnectionResult::Keep,
Ok(()) => {
tracing::debug!(
"the request body was not read to its end; closing the connection"
);
ConnectionResult::Close(None)
}
Err(e) => {
tracing::debug!(error = %e, "writing the response failed; closing the connection");
ConnectionResult::Close(None)
}
}
}
}
struct H1Body<'a> {
reader: BodyReader,
io: &'a ConnIo,
timeout: Duration,
wait: Option<IoReady<'a>>,
drained: &'a AtomicBool,
}
impl futures::Stream for H1Body<'_> {
type Item = io::Result<Bytes>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let this = self.get_mut();
loop {
if let Some(wait) = this.wait.as_mut() {
match Pin::new(wait).poll(cx) {
Poll::Pending => return Poll::Pending,
Poll::Ready(result) => {
this.wait = None;
if let Err(e) = result {
return Poll::Ready(Some(Err(e)));
}
}
}
}
match this.reader.step() {
BodyStep::Chunk(bytes) => return Poll::Ready(Some(Ok(bytes))),
BodyStep::End => {
this.drained.store(true, Ordering::Release);
return Poll::Ready(None);
}
BodyStep::Failed(e) => return Poll::Ready(Some(Err(e))),
BodyStep::WouldBlock => {
this.wait = Some(this.io.ready(IoInterest::Readable, this.timeout));
}
}
}
}
}
struct H1Sink<'a> {
conn: &'a mut SharedByteBufferStream<RawStream>,
io: &'a ConnIo,
timeout: Duration,
}
impl H1Sink<'_> {
async fn write_all(&mut self, mut buf: &[u8]) -> io::Result<()> {
while !buf.is_empty() {
match self.conn.write(buf) {
Ok(0) => {
return Err(io::Error::new(
io::ErrorKind::WriteZero,
"the connection accepted no bytes",
));
}
Ok(n) => buf = &buf[n..],
Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
self.io.ready(IoInterest::Writable, self.timeout).await?;
}
Err(e) => return Err(e),
}
}
Ok(())
}
async fn flush(&mut self) -> io::Result<()> {
loop {
match self.conn.flush() {
Ok(()) => return Ok(()),
Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
self.io.ready(IoInterest::Writable, self.timeout).await?;
}
Err(e) => return Err(e),
}
}
}
async fn write_chunk(&mut self, bytes: &[u8]) -> io::Result<()> {
if bytes.is_empty() {
return Ok(());
}
self.write_all(format!("{:x}\r\n", bytes.len()).as_bytes())
.await?;
self.write_all(bytes).await?;
self.write_all(b"\r\n").await
}
async fn write_whole(&mut self, mut response: SimpleOutgoingResponse) -> io::Result<()> {
match response.body.take() {
None | Some(SendSafeBody::None) => {
set_content_length(&mut response, 0);
self.write_all(&render_head(response)?).await?;
}
Some(SendSafeBody::Bytes(bytes)) => {
set_content_length(&mut response, bytes.len());
self.write_all(&render_head(response)?).await?;
self.write_all(&bytes).await?;
}
Some(SendSafeBody::Text(text)) => {
set_content_length(&mut response, text.len());
self.write_all(&render_head(response)?).await?;
self.write_all(text.as_bytes()).await?;
}
Some(streamed) => {
ensure_chunked_transfer_encoding(&mut response.headers);
self.write_all(&render_head(response)?).await?;
let mut reader = BodyReader::new(Some(streamed));
loop {
match reader.step() {
BodyStep::Chunk(bytes) => self.write_chunk(&bytes).await?,
BodyStep::End => break,
BodyStep::Failed(e) => return Err(e),
BodyStep::WouldBlock => {
return Err(io::Error::new(
io::ErrorKind::WouldBlock,
"a response body reported it would block",
));
}
}
}
self.write_all(b"0\r\n\r\n").await?;
}
}
self.flush().await
}
}
impl ResponseSink for H1Sink<'_> {
fn whole(&mut self, response: SimpleOutgoingResponse) -> BoxFuture<'_, io::Result<()>> {
Box::pin(self.write_whole(response))
}
fn open(&mut self, mut head: SimpleOutgoingResponse) -> BoxFuture<'_, io::Result<()>> {
Box::pin(async move {
ensure_chunked_transfer_encoding(&mut head.headers);
head.body = None;
self.write_all(&render_head(head)?).await?;
self.flush().await
})
}
fn chunk(&mut self, bytes: Bytes) -> BoxFuture<'_, io::Result<()>> {
Box::pin(async move {
self.write_chunk(&bytes).await?;
self.flush().await
})
}
fn finish(&mut self) -> BoxFuture<'_, io::Result<()>> {
Box::pin(async move {
self.write_all(b"0\r\n\r\n").await?;
self.flush().await
})
}
}
fn render_head(mut response: SimpleOutgoingResponse) -> io::Result<Vec<u8>> {
response.body = Some(SendSafeBody::None);
let parts = Http11::response(response)
.http_render()
.map_err(|e| io::Error::other(e.to_string()))?;
let mut head = Vec::new();
for part in parts {
head.extend_from_slice(&part.map_err(|e| io::Error::other(e.to_string()))?);
}
Ok(head)
}
fn set_content_length(response: &mut SimpleOutgoingResponse, len: usize) {
response
.headers
.entry(SimpleHeader::CONTENT_LENGTH)
.or_insert_with(|| vec![len.to_string()]);
}