use std::{
fmt::{self, Debug},
pin::Pin,
str,
task::Poll,
};
use bytes::{Bytes, BytesMut};
use futures_util::stream::Stream;
use tokio::io::AsyncRead;
use tokio_util::io::ReaderStream;
use tracing::debug;
use crate::{
meta::{EndRequestRec, Header, RequestType, HEADER_LEN},
ClientError, ClientResult,
};
#[derive(Default, Clone)]
#[non_exhaustive]
pub struct Response {
pub stdout: Option<Bytes>,
pub stderr: Option<Bytes>,
}
impl Debug for Response {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("Response")
.field("stdout", &self.stdout.as_deref().map(str::from_utf8))
.field("stderr", &self.stderr.as_deref().map(str::from_utf8))
.finish()
}
}
pub enum Content {
Stdout(Bytes),
Stderr(Bytes),
}
pub struct ResponseStream<S: AsyncRead + Unpin> {
stream: ReaderStream<S>,
id: u16,
eof: bool,
header: Option<Header>,
buf: BytesMut,
}
impl<S: AsyncRead + Unpin> ResponseStream<S> {
#[inline]
pub(crate) fn new(stream: S, id: u16) -> Self {
Self {
stream: ReaderStream::new(stream),
id,
eof: false,
header: None,
buf: BytesMut::new(),
}
}
#[inline]
fn read_header(&mut self) -> Option<Header> {
if self.buf.len() < HEADER_LEN {
return None;
}
let buf = self.buf.split_to(HEADER_LEN);
Some(Header::from(buf))
}
#[inline]
fn read_content(&mut self) -> Option<BytesMut> {
let header = self.header.as_ref().unwrap();
let block_length = header.content_length as usize + header.padding_length as usize;
if self.buf.len() < block_length {
return None;
}
let content = self.buf.split_to(header.content_length as usize);
let _ = self.buf.split_to(header.padding_length as usize);
self.header = None;
Some(content)
}
fn process_message(&mut self) -> Result<Option<Content>, ClientError> {
if self.buf.is_empty() {
return Ok(None);
}
if self.header.is_none() {
match self.read_header() {
Some(header) => self.header = Some(header),
None => return Ok(None),
}
}
let header = self.header.as_ref().unwrap();
match header.r#type.clone() {
RequestType::Stdout => {
if let Some(data) = self.read_content() {
return Ok(Some(Content::Stdout(data.freeze())));
}
}
RequestType::Stderr => {
if let Some(data) = self.read_content() {
return Ok(Some(Content::Stderr(data.freeze())));
}
}
RequestType::EndRequest => {
let header = header.clone();
let Some(data) = self.read_content() else {
return Ok(None);
};
let end = EndRequestRec::new_from_buf(header, data);
debug!(id = self.id, ?end, "Receive from stream.");
self.eof = true;
end.end_request
.protocol_status
.convert_to_client_result(end.end_request.app_status)?;
return Ok(None);
}
r#type => {
self.eof = true;
return Err(ClientError::UnknownRequestType {
request_type: r#type,
});
}
}
Ok(None)
}
}
impl<S> Stream for ResponseStream<S>
where
S: AsyncRead + Unpin,
{
type Item = ClientResult<Content>;
fn poll_next(
mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Option<Self::Item>> {
let mut pending = false;
loop {
match Pin::new(&mut self.stream).poll_next(cx) {
Poll::Ready(Some(Ok(data))) => {
self.buf.extend_from_slice(&data);
match self.process_message() {
Ok(Some(data)) => return Poll::Ready(Some(Ok(data))),
Ok(None) if self.eof => return Poll::Ready(None),
Ok(None) => continue,
Err(err) => return Poll::Ready(Some(Err(err))),
}
}
Poll::Ready(Some(Err(err))) => return Poll::Ready(Some(Err(err.into()))),
Poll::Ready(None) => break,
Poll::Pending => {
pending = true;
break;
}
}
}
match self.process_message() {
Ok(Some(data)) => Poll::Ready(Some(Ok(data))),
Ok(None) if !self.eof && pending => Poll::Pending,
Ok(None) => Poll::Ready(None),
Err(err) => Poll::Ready(Some(Err(err))),
}
}
}