use crate::modules::input::Token;
use crate::{
RuntimeError,
constants::{FILE_CHUNK, INLINE_PAYLOAD, STEP_BUDGET},
futures::{
net::step::{Progress, settle, wait_on},
task::{
Nothing, Task,
sealed::{self, Step},
},
tcp::Connection,
unix::UnixConnection,
},
modules::{fd::Fd, int_check::IntCheck, park},
};
use std::{
mem,
sync::{Arc, Mutex, MutexGuard},
};
#[cfg(feature = "tls")]
use crate::futures::tls::TlsConnection;
const _: () = assert!(mem::size_of::<Result<Vec<u8>, RuntimeError>>() <= INLINE_PAYLOAD);
const _: () = assert!(mem::size_of::<Result<usize, RuntimeError>>() <= INLINE_PAYLOAD);
pub(crate) struct Pipe {
fd: Fd,
leftover: Mutex<Vec<u8>>,
}
impl std::fmt::Debug for Pipe {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.debug_tuple("Pipe").field(&self.fd.raw()).finish()
}
}
impl Pipe {
pub(crate) fn new(fd: Fd) -> Self {
Self {
fd,
leftover: Mutex::new(Vec::new()),
}
}
#[inline(always)]
pub(crate) fn fd(&self) -> libc::c_int {
self.fd.raw()
}
pub(crate) fn leftover(&self) -> MutexGuard<'_, Vec<u8>> {
self.leftover
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner())
}
}
#[derive(Debug, Clone)]
pub(crate) enum Source {
Tcp(Connection),
Unix(UnixConnection),
#[cfg(feature = "tls")]
Tls(TlsConnection),
Pipe(Arc<Pipe>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Io {
Moved(usize),
Closed,
#[cfg_attr(not(feature = "tls"), allow(dead_code))]
Truncated,
Wait(i16),
}
impl Source {
#[inline(always)]
fn pipe(&self) -> &Pipe {
match self {
Self::Tcp(conn) => conn.pipe(),
Self::Unix(conn) => conn.pipe(),
Self::Pipe(pipe) => pipe,
#[cfg(feature = "tls")]
Self::Tls(conn) => conn.pipe(),
}
}
fn read(&self, into: &mut Vec<u8>, room: usize) -> Result<Io, RuntimeError> {
match self {
Self::Tcp(_) | Self::Unix(_) => read_raw(self.pipe().fd(), into, room),
Self::Pipe(pipe) => read_pipe(pipe.fd(), into, room),
#[cfg(feature = "tls")]
Self::Tls(conn) => conn.read(into, room),
}
}
fn write(&self, data: &[u8]) -> Result<Io, RuntimeError> {
match self {
Self::Tcp(_) | Self::Unix(_) => write_raw(self.pipe().fd(), data),
Self::Pipe(pipe) => write_pipe(pipe.fd(), data),
#[cfg(feature = "tls")]
Self::Tls(conn) => conn.write(data),
}
}
fn flush(&self) -> Result<Io, RuntimeError> {
match self {
Self::Tcp(_) | Self::Unix(_) | Self::Pipe(_) => Ok(Io::Moved(0)),
#[cfg(feature = "tls")]
Self::Tls(conn) => conn.flush(),
}
}
}
fn read_raw(fd: libc::c_int, into: &mut Vec<u8>, room: usize) -> Result<Io, RuntimeError> {
into.reserve(room);
loop {
let read = unsafe {
libc::recv(
fd,
into.spare_capacity_mut()
.as_mut_ptr()
.cast::<libc::c_void>(),
room,
0,
)
}
.check();
match read {
Ok(0) => return Ok(Io::Closed),
Ok(read) => {
unsafe { into.set_len(into.len() + read as usize) };
return Ok(Io::Moved(read as usize));
}
Err(RuntimeError::CheckError(Some(libc::EINTR))) => {}
Err(RuntimeError::CheckError(Some(libc::EAGAIN))) => {
return Ok(Io::Wait(libc::EVFILT_READ));
}
Err(error) => return Err(error),
}
}
}
fn write_raw(fd: libc::c_int, data: &[u8]) -> Result<Io, RuntimeError> {
loop {
let put =
unsafe { libc::send(fd, data.as_ptr().cast::<libc::c_void>(), data.len(), 0) }.check();
match put {
Ok(put) => return Ok(Io::Moved(put as usize)),
Err(RuntimeError::CheckError(Some(libc::EINTR))) => {}
Err(RuntimeError::CheckError(Some(libc::EAGAIN | libc::ENOBUFS))) => {
return Ok(Io::Wait(libc::EVFILT_WRITE));
}
Err(error) => return Err(error),
}
}
}
#[derive(Debug, Clone)]
#[must_use = "a task does nothing until it is run or spawned"]
pub struct FinishTask {
source: Source,
#[cfg_attr(not(feature = "tls"), allow(dead_code))]
said: Progress<bool>,
}
impl FinishTask {
pub(crate) fn new(source: Source) -> Self {
Self {
source,
said: Progress::default(),
}
}
fn advance(&mut self) -> Result<Step<Result<(), RuntimeError>>, RuntimeError> {
#[cfg(feature = "tls")]
if let Source::Tls(conn) = &self.source {
if !self.said.0 {
conn.say_goodbye();
self.said.0 = true;
}
}
let fd = self.source.pipe().fd();
if let Io::Wait(filter) = self.source.flush()? {
return wait_on(fd, filter);
}
loop {
match unsafe { libc::shutdown(fd, libc::SHUT_WR) }.check() {
Ok(_) => return Ok(Step::Done(Ok(()))),
Err(RuntimeError::CheckError(Some(libc::EINTR))) => {}
Err(RuntimeError::CheckError(Some(libc::ENOTCONN))) => {
return Err(RuntimeError::Closed);
}
Err(error) => return Err(error),
}
}
}
}
fn read_pipe(fd: libc::c_int, into: &mut Vec<u8>, room: usize) -> Result<Io, RuntimeError> {
into.reserve(room);
loop {
let read = unsafe {
libc::read(
fd,
into.spare_capacity_mut()
.as_mut_ptr()
.cast::<libc::c_void>(),
room,
)
}
.check();
match read {
Ok(0) => return Ok(Io::Closed),
Ok(read) => {
unsafe { into.set_len(into.len() + read as usize) };
return Ok(Io::Moved(read as usize));
}
Err(RuntimeError::CheckError(Some(libc::EINTR))) => {}
Err(RuntimeError::CheckError(Some(libc::EAGAIN))) => {
return Ok(Io::Wait(libc::EVFILT_READ));
}
Err(error) => return Err(error),
}
}
}
fn write_pipe(fd: libc::c_int, data: &[u8]) -> Result<Io, RuntimeError> {
loop {
let put =
unsafe { libc::write(fd, data.as_ptr().cast::<libc::c_void>(), data.len()) }.check();
match put {
Ok(put) => return Ok(Io::Moved(put as usize)),
Err(RuntimeError::CheckError(Some(libc::EINTR))) => {}
Err(RuntimeError::CheckError(Some(libc::EAGAIN))) => {
return Ok(Io::Wait(libc::EVFILT_WRITE));
}
Err(error) => return Err(error),
}
}
}
#[derive(Debug, Clone)]
#[must_use = "a task does nothing until it is run or spawned"]
pub struct SendTask {
source: Source,
data: Arc<[u8]>,
sent: Progress<usize>,
}
impl SendTask {
pub(crate) fn new(source: Source, data: Arc<[u8]>) -> Self {
Self {
source,
data,
sent: Progress::default(),
}
}
#[inline(always)]
pub(crate) fn source(&self) -> &Source {
&self.source
}
fn advance(&mut self) -> Result<Step<Result<usize, RuntimeError>>, RuntimeError> {
let fd = self.source.pipe().fd();
let mut moved = 0;
loop {
let sent = self.sent.0;
if sent == self.data.len() {
return match self.source.flush()? {
Io::Wait(filter) => wait_on(fd, filter),
_ => Ok(Step::Done(Ok(sent))),
};
}
if moved >= STEP_BUDGET {
return wait_on(fd, libc::EVFILT_WRITE);
}
let want = (self.data.len() - sent).min(FILE_CHUNK);
match self.source.write(&self.data[sent..sent + want])? {
Io::Moved(put) => {
self.sent.0 += put;
moved += put;
}
Io::Wait(filter) => return wait_on(fd, filter),
Io::Closed | Io::Truncated => {
return Err(RuntimeError::CheckError(Some(libc::EPIPE)));
}
}
}
}
}
#[derive(Debug, Clone)]
enum Want {
Some(usize),
Exact(usize),
Until(Arc<[u8]>, usize),
ToEnd,
}
#[derive(Default)]
struct Reading {
got: Vec<u8>,
started: bool,
searched: usize,
}
#[derive(Debug, Clone)]
#[must_use = "a task does nothing until it is run or spawned"]
pub struct RecvTask {
source: Source,
want: Want,
progress: Progress<Reading>,
}
impl RecvTask {
pub(crate) fn some(source: Source, max: usize) -> Self {
Self::new(source, Want::Some(max))
}
pub(crate) fn exact(source: Source, len: usize) -> Self {
Self::new(source, Want::Exact(len))
}
pub(crate) fn until(source: Source, delimiter: Arc<[u8]>, max: usize) -> Self {
Self::new(source, Want::Until(delimiter, max))
}
pub(crate) fn to_end(source: Source) -> Self {
Self::new(source, Want::ToEnd)
}
fn new(source: Source, want: Want) -> Self {
Self {
source,
want,
progress: Progress::default(),
}
}
fn advance(&mut self) -> Result<Step<Result<Vec<u8>, RuntimeError>>, RuntimeError> {
if !self.progress.0.started {
self.progress.0.started = true;
if let Some(done) = self.take_leftover() {
return Ok(Step::Done(Ok(done)));
}
}
let fd = self.source.pipe().fd();
let mut moved = 0;
loop {
if let Some(done) = self.done()? {
return Ok(Step::Done(Ok(done)));
}
let got = self.progress.0.got.len();
if moved >= STEP_BUDGET {
if matches!(self.want, Want::Some(_)) {
return Ok(Step::Done(Ok(self.take())));
}
return wait_on(fd, libc::EVFILT_READ);
}
let room = match &self.want {
Want::Some(max) => max - got,
Want::Exact(len) => len - got,
Want::Until(_, _) | Want::ToEnd => FILE_CHUNK,
};
match self
.source
.read(&mut self.progress.0.got, room.min(FILE_CHUNK))?
{
Io::Closed => {
return match self.want {
Want::Some(_) | Want::ToEnd => Ok(Step::Done(Ok(self.take()))),
Want::Exact(_) | Want::Until(_, _) => Err(RuntimeError::Closed),
};
}
Io::Truncated => return Err(RuntimeError::Closed),
Io::Moved(read) => moved += read,
Io::Wait(filter) => {
if matches!(self.want, Want::Some(_)) && got > 0 {
return Ok(Step::Done(Ok(self.take())));
}
return wait_on(fd, filter);
}
}
}
}
fn take_leftover(&mut self) -> Option<Vec<u8>> {
if let Want::Some(0) | Want::Exact(0) = self.want {
return Some(Vec::new());
}
let mut leftover = self.source.pipe().leftover();
if leftover.is_empty() {
return None;
}
let limit = match self.want {
Want::Some(max) => max,
Want::Exact(len) => len,
Want::Until(_, _) | Want::ToEnd => usize::MAX,
};
let split = limit.min(leftover.len());
let rest = leftover.split_off(split);
let taken = mem::replace(&mut *leftover, rest);
drop(leftover);
if let Want::Some(_) = self.want {
return Some(taken);
}
self.progress.0.got = taken;
None
}
fn done(&mut self) -> Result<Option<Vec<u8>>, RuntimeError> {
let reading = &mut self.progress.0;
match &self.want {
Want::Some(max) => Ok((reading.got.len() >= *max).then(|| mem::take(&mut reading.got))),
Want::Exact(len) => {
Ok((reading.got.len() >= *len).then(|| mem::take(&mut reading.got)))
}
Want::ToEnd => Ok(None),
Want::Until(delimiter, max) => {
let from = reading
.searched
.saturating_sub(delimiter.len().saturating_sub(1));
let found = match delimiter.is_empty() {
true => Some(0),
false => reading.got[from..]
.windows(delimiter.len())
.position(|window| window == &delimiter[..])
.map(|at| from + at),
};
reading.searched = reading.got.len();
let Some(at) = found else {
return match reading.got.len() >= *max {
true => Err(RuntimeError::TooLong),
false => Ok(None),
};
};
let end = at + delimiter.len();
if end > *max {
return Err(RuntimeError::TooLong);
}
let rest = reading.got.split_off(end);
let line = mem::take(&mut reading.got);
put_front(self.source.pipe(), rest);
Ok(Some(line))
}
}
}
#[inline(always)]
fn take(&mut self) -> Vec<u8> {
mem::take(&mut self.progress.0.got)
}
fn put_back(&mut self) {
let got = self.take();
put_front(self.source.pipe(), got);
}
}
fn put_front(pipe: &Pipe, mut bytes: Vec<u8>) {
if bytes.is_empty() {
return;
}
let mut leftover = pipe.leftover();
bytes.extend_from_slice(&leftover);
*leftover = bytes;
}
impl Drop for RecvTask {
fn drop(&mut self) {
self.put_back();
}
}
fn settle_recv(read: &mut RecvTask) -> Step<Result<Vec<u8>, RuntimeError>> {
match read.advance() {
Ok(step) => step,
Err(error) => {
read.put_back();
Step::Done(Err(error))
}
}
}
impl sealed::Sealed for SendTask {}
impl sealed::Sealed for FinishTask {}
impl Task for FinishTask {
type Output = Result<(), RuntimeError>;
type Input = Nothing;
fn execute(&self, _token: Token, reactor_id: i32, task_id: usize) -> Self::Output {
park::drive(self.clone(), reactor_id, task_id)
}
fn step(&mut self, _token: Token, _reactor_id: i32, _task_id: usize) -> Step<Self::Output> {
settle(self.advance())
}
}
impl sealed::Sealed for RecvTask {}
impl Task for SendTask {
type Output = Result<usize, RuntimeError>;
type Input = Nothing;
fn execute(&self, _token: Token, reactor_id: i32, task_id: usize) -> Self::Output {
park::drive(self.clone(), reactor_id, task_id)
}
fn prepare(&mut self, _token: Token) {
self.sent = Progress::default();
}
fn step(&mut self, _token: Token, _reactor_id: i32, _task_id: usize) -> Step<Self::Output> {
settle(self.advance())
}
}
impl Task for RecvTask {
type Output = Result<Vec<u8>, RuntimeError>;
type Input = Nothing;
fn execute(&self, _token: Token, reactor_id: i32, task_id: usize) -> Self::Output {
park::drive(self.clone(), reactor_id, task_id)
}
fn prepare(&mut self, _token: Token) {
self.put_back();
self.progress = Progress::default();
}
fn step(&mut self, _token: Token, _reactor_id: i32, _task_id: usize) -> Step<Self::Output> {
settle_recv(self)
}
}