use crate::modules::input::{Token, token};
use crate::{
RuntimeError,
constants::INLINE_PAYLOAD,
futures::{
net::{
address::{Target, family, from_raw, local_of, peer_of, to_raw},
exchange::{self, Stage},
socket::{Options, begin_connect, configure, finished_connecting, open, set_flag},
step::{Progress, settle, wait_on},
},
task::{
Nothing, Task,
sealed::{self, Step},
},
tcp::connection::{Connection, Listener},
},
modules::{fd::Fd, int_check::IntCheck, park},
};
use std::{mem, net::SocketAddr, sync::Arc, time::Duration};
const _: () = assert!(mem::size_of::<Result<Connection, RuntimeError>>() <= INLINE_PAYLOAD);
const _: () = assert!(mem::size_of::<Result<Listener, RuntimeError>>() <= INLINE_PAYLOAD);
const _: () =
assert!(mem::size_of::<Result<(Connection, SocketAddr), RuntimeError>>() <= INLINE_PAYLOAD);
#[derive(Debug, Clone)]
#[must_use = "a task does nothing until it is run or spawned"]
pub struct ConnectTask {
target: Target,
options: Options,
progress: Progress<Connecting>,
}
#[derive(Default)]
struct Connecting {
left: Option<Vec<SocketAddr>>,
trying: Option<(Fd, SocketAddr)>,
failure: Option<RuntimeError>,
}
enum Started {
Connected(Fd),
Waiting(Fd),
}
impl ConnectTask {
pub(crate) fn new(target: Target) -> Self {
Self {
target,
options: Options::default(),
progress: Progress::default(),
}
}
pub fn nodelay(mut self, nodelay: bool) -> Self {
self.options.nodelay = nodelay;
self
}
pub fn keepalive(mut self, idle: Duration) -> Self {
self.options.keepalive = Some(idle);
self
}
#[cfg_attr(not(feature = "tls"), allow(dead_code))]
pub(crate) fn with_options(mut self, options: Options) -> Self {
self.options = options;
self
}
fn advance(&mut self) -> Result<Step<Result<Connection, RuntimeError>>, RuntimeError> {
let state = &mut self.progress.0;
loop {
if let Some((fd, addr)) = state.trying.take() {
match finished_connecting(fd.raw()) {
Ok(true) => return Ok(Step::Done(connected(fd, addr))),
Ok(false) => {
let step = wait_on(fd.raw(), libc::EVFILT_WRITE)?;
state.trying = Some((fd, addr));
return Ok(step);
}
Err(error) => state.failure = Some(error),
}
continue;
}
if state.left.is_none() {
let mut found = self.target.resolve()?;
found.reverse();
state.left = Some(found);
}
let Some(addr) = state.left.as_mut().and_then(Vec::pop) else {
return Err(state.failure.take().unwrap_or(RuntimeError::BadAddress));
};
match start_connect(&addr, &self.options) {
Ok(Started::Connected(fd)) => return Ok(Step::Done(connected(fd, addr))),
Ok(Started::Waiting(fd)) => state.trying = Some((fd, addr)),
Err(error) => state.failure = Some(error),
}
}
}
}
fn start_connect(addr: &SocketAddr, options: &Options) -> Result<Started, RuntimeError> {
let fd = open(family(addr), libc::SOCK_STREAM)?;
options.apply(fd.raw(), addr.is_ipv6())?;
let (raw, len) = to_raw(addr);
let at_once = begin_connect(
fd.raw(),
(&raw as *const libc::sockaddr_storage).cast::<libc::sockaddr>(),
len,
)?;
match at_once {
true => Ok(Started::Connected(fd)),
false => Ok(Started::Waiting(fd)),
}
}
fn connected(fd: Fd, peer: SocketAddr) -> Result<Connection, RuntimeError> {
let local = local_of(fd.raw())?;
Ok(Connection::new(fd, local, peer))
}
#[derive(Debug, Clone)]
#[must_use = "a task does nothing until it is run or spawned"]
pub struct ListenTask {
target: Target,
options: Options,
}
impl ListenTask {
pub(crate) fn new(target: Target) -> Self {
Self {
target,
options: Options::default(),
}
}
pub fn backlog(mut self, backlog: u32) -> Self {
self.options.backlog = Some(backlog);
self
}
pub fn reuse_port(mut self, reuse: bool) -> Self {
self.options.reuse_port = reuse;
self
}
pub fn v6_only(mut self, only: bool) -> Self {
self.options.v6_only = only;
self
}
#[cfg_attr(not(feature = "tls"), allow(dead_code))]
pub(crate) fn with_options(mut self, options: Options) -> Self {
self.options = options;
self
}
#[cfg_attr(not(feature = "tls"), allow(dead_code))]
pub(crate) fn options(&self) -> Options {
self.options
}
fn listen(&self) -> Result<Listener, RuntimeError> {
let found = self.target.resolve()?;
let mut failure = RuntimeError::BadAddress;
for addr in found {
match bind_listen(&addr, &self.options) {
Ok(listener) => return Ok(listener),
Err(error) => failure = error,
}
}
Err(failure)
}
}
fn bind_listen(addr: &SocketAddr, options: &Options) -> Result<Listener, RuntimeError> {
let fd = open(family(addr), libc::SOCK_STREAM)?;
options.apply(fd.raw(), addr.is_ipv6())?;
set_flag(fd.raw(), libc::SO_REUSEADDR)?;
let (raw, len) = to_raw(addr);
unsafe {
libc::bind(
fd.raw(),
(&raw as *const libc::sockaddr_storage).cast::<libc::sockaddr>(),
len,
)
}
.check()?;
unsafe { libc::listen(fd.raw(), options.backlog()) }.check()?;
let local = local_of(fd.raw())?;
Ok(Listener::new(fd, local))
}
#[derive(Debug, Clone)]
#[must_use = "a task does nothing until it is run or spawned"]
pub struct AcceptTask {
listener: Listener,
}
impl AcceptTask {
pub(crate) fn new(listener: Listener) -> Self {
Self { listener }
}
fn advance(
&mut self,
) -> Result<Step<Result<(Connection, SocketAddr), RuntimeError>>, RuntimeError> {
let fd = self.listener.fd();
loop {
let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() };
let mut len = mem::size_of::<libc::sockaddr_storage>() as libc::socklen_t;
let accepted = unsafe {
libc::accept(
fd,
(&mut storage as *mut libc::sockaddr_storage).cast::<libc::sockaddr>(),
&mut len,
)
}
.check();
match accepted {
Ok(raw) => return Ok(Step::Done(adopt(Fd::new(raw), &storage))),
Err(RuntimeError::CheckError(Some(libc::EINTR | libc::ECONNABORTED))) => {}
Err(RuntimeError::CheckError(Some(libc::EAGAIN))) => {
return wait_on(fd, libc::EVFILT_READ);
}
Err(error) => return Err(error),
}
}
}
}
fn adopt(
fd: Fd,
storage: &libc::sockaddr_storage,
) -> Result<(Connection, SocketAddr), RuntimeError> {
configure(fd.raw())?;
let peer = match from_raw(storage) {
Some(peer) => peer,
None => peer_of(fd.raw())?,
};
let local = local_of(fd.raw())?;
Ok((Connection::new(fd, local, peer), peer))
}
#[derive(Debug, Clone)]
#[must_use = "a task does nothing until it is run or spawned"]
pub struct RequestTask {
connect: ConnectTask,
data: Arc<[u8]>,
stage: Progress<Stage>,
}
impl RequestTask {
pub(crate) fn new(target: Target, data: Arc<[u8]>) -> Self {
Self {
connect: ConnectTask::new(target),
data,
stage: Progress::default(),
}
}
fn advance(&mut self, reactor_id: i32, task_id: usize) -> Step<Result<Vec<u8>, RuntimeError>> {
exchange::advance(
&mut self.connect,
&mut self.stage.0,
&self.data,
reactor_id,
task_id,
)
}
}
impl sealed::Sealed for ConnectTask {}
impl sealed::Sealed for ListenTask {}
impl sealed::Sealed for AcceptTask {}
impl sealed::Sealed for RequestTask {}
impl Task for ConnectTask {
type Output = Result<Connection, 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.progress = Progress::default();
}
fn blocking(&self, _token: Token) -> bool {
self.target.needs_lookup()
}
fn step(&mut self, _token: Token, _reactor_id: i32, _task_id: usize) -> Step<Self::Output> {
settle(self.advance())
}
}
impl Task for ListenTask {
type Output = Result<Listener, RuntimeError>;
type Input = Nothing;
fn execute(&self, _token: Token, _reactor_id: i32, _task_id: usize) -> Self::Output {
self.listen()
}
fn blocking(&self, _token: Token) -> bool {
self.target.needs_lookup()
}
}
impl Task for AcceptTask {
type Output = Result<(Connection, SocketAddr), 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 Task for RequestTask {
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.connect.progress = Progress::default();
self.stage = Progress::default();
}
fn blocking(&self, _token: Token) -> bool {
self.connect.blocking(token())
}
fn step(&mut self, _token: Token, reactor_id: i32, task_id: usize) -> Step<Self::Output> {
self.advance(reactor_id, task_id)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::futures::net::address::sealed::Sealed;
use crate::modules::input::token;
#[test]
fn only_a_name_lookup_blocks() {
assert!(!ConnectTask::new("127.0.0.1:80".target()).blocking(token()));
assert!(ConnectTask::new("localhost:80".target()).blocking(token()));
assert!(!ListenTask::new("127.0.0.1:0".target()).blocking(token()));
assert!(ListenTask::new("localhost:0".target()).blocking(token()));
assert!(!RequestTask::new("[::1]:80".target(), Arc::from(&b""[..])).blocking(token()));
assert!(RequestTask::new("localhost:80".target(), Arc::from(&b""[..])).blocking(token()));
}
}