mod controller;
mod listener;
mod session;
mod timer;
mod token;
mod transport;
use std::collections::HashMap;
use std::fmt::{Debug, Display, Formatter};
use std::sync::Arc;
use std::thread::JoinHandle;
use std::time::{Duration, Instant};
use std::{io, thread};
use crossbeam_channel::{Receiver, TryRecvError, unbounded};
use mio::event::{Event, Source};
use mio::{Events, Interest, Poll, Waker};
use thiserror::Error;
use timer::Timer;
use token::WAKER;
use crate::wire;
pub(crate) use self::controller::{ControlMessage, Controller};
pub(crate) use listener::Listener;
pub(crate) use session::{NoiseSession, ProtocolArtifact, Socks5Session};
pub(crate) use token::{Token, Tokens};
pub(crate) use transport::{SessionEvent, Transport};
const SECONDS_IN_AN_HOUR: u64 = 60 * 60;
const WAIT_TIMEOUT: Duration = Duration::from_secs(SECONDS_IN_AN_HOUR);
const LAG_TIMEOUT: Duration = Duration::from_millis(100);
pub trait EventHandler {
type Reaction;
fn interests(&self) -> Option<Interest>;
fn handle(&mut self, event: &Event) -> Vec<Self::Reaction>;
}
pub trait BufferWrite {
fn buffer_write(&mut self, buf: &[u8]);
}
#[derive(Error)]
pub enum Error<L: EventHandler, T: EventHandler> {
#[error("listener {0:?} got disconnected during poll operation")]
ListenerDisconnect(Token, L),
#[error("transport {0:?} got disconnected during poll operation")]
TransportDisconnect(Token, T),
#[error("registration of a resource has failed: {0}")]
Poll(io::Error),
#[error("registration of a resource has failed: {0}")]
Registration(io::Error),
}
impl<L: EventHandler, T: EventHandler> Debug for Error<L, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Display::fmt(self, f)
}
}
pub enum Action<L, T> {
RegisterListener(Token, L),
RegisterTransport(Token, T),
#[allow(dead_code)] UnregisterListener(Token),
UnregisterTransport(Token),
Send(Token, Vec<u8>),
SetTimer(Duration),
}
impl<L: EventHandler, T: EventHandler> Display for Action<L, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Action::RegisterListener(token, _listener) => f
.debug_struct("RegisterListener")
.field("token", token)
.field("listener", &"<omitted>")
.finish(),
Action::RegisterTransport(token, _transport) => f
.debug_struct("RegisterTransport")
.field("token", token)
.field("transport", &"<omitted>")
.finish(),
Action::UnregisterListener(token) => f
.debug_struct("UnregisterListener")
.field("token", token)
.finish(),
Action::UnregisterTransport(token) => f
.debug_struct("UnregisterTransport")
.field("token", token)
.finish(),
Action::Send(token, _data) => f
.debug_struct("Send")
.field("token", token)
.field("data", &"<omitted>")
.finish(),
Action::SetTimer(duration) => f
.debug_struct("SetTimer")
.field("duration", duration)
.finish(),
}
}
}
pub trait ReactionHandler: Send + Iterator<Item = Action<Self::Listener, Self::Transport>> {
type Listener: EventHandler + Source + Send + Debug;
type Transport: EventHandler + Source + Send + Debug + BufferWrite;
fn tick(&mut self);
fn timer_reacted(&mut self);
fn listener_reacted(
&mut self,
token: Token,
reaction: <Self::Listener as EventHandler>::Reaction,
instant: Instant,
);
fn transport_reacted(
&mut self,
token: Token,
reaction: <Self::Transport as EventHandler>::Reaction,
instant: Instant,
);
fn listener_registered(&mut self, token: Token, listener: &Self::Listener);
fn transport_registered(&mut self, token: Token, transport: &Self::Transport);
fn handle_command(&mut self, cmd: wire::Control);
fn handle_error(&mut self, err: Error<Self::Listener, Self::Transport>);
fn handover_listener(&mut self, token: Token, listener: Self::Listener);
fn handover_transport(&mut self, token: Token, transport: Self::Transport);
}
pub struct Reactor {
thread: JoinHandle<()>,
controller: Controller,
}
impl Reactor {
pub fn new<H>(service: H, thread_name: String) -> Result<Self, io::Error>
where
H: 'static + ReactionHandler,
{
let builder = thread::Builder::new().name(thread_name);
let (sender, receiver) = unbounded();
let poll = Poll::new()?;
let controller = Controller::new(sender, Arc::new(Waker::new(poll.registry(), WAKER)?));
log::debug!(target: "reactor-controller", "Initializing reactor thread…");
let thread = builder.spawn(move || {
let runtime = Runtime {
service,
poll,
receiver,
listeners: HashMap::new(),
transports: HashMap::new(),
timeouts: Timer::new(),
};
log::info!(target: "reactor", "Entering reactor event loop");
runtime.run();
})?;
controller.wake()?;
Ok(Self { thread, controller })
}
pub fn controller(&self) -> Controller {
self.controller.clone()
}
pub fn join(self) -> thread::Result<()> {
self.thread.join()
}
}
pub struct Runtime<H: ReactionHandler> {
service: H,
poll: Poll,
receiver: Receiver<ControlMessage>,
listeners: HashMap<Token, H::Listener>,
transports: HashMap<Token, H::Transport>,
timeouts: Timer,
}
impl<H: ReactionHandler> Runtime<H> {
fn register_interests(&mut self) -> io::Result<()> {
let registry = self.poll.registry();
for (id, res) in self.listeners.iter_mut() {
match res.interests() {
None => registry.deregister(res)?,
Some(interests) => registry.reregister(res, *id, interests)?,
};
}
for (id, res) in self.transports.iter_mut() {
match res.interests() {
None => registry.deregister(res)?,
Some(interests) => registry.reregister(res, *id, interests)?,
};
}
Ok(())
}
fn run(mut self) {
loop {
let timeout = self
.timeouts
.next_expiring_from(Instant::now())
.unwrap_or(WAIT_TIMEOUT);
self.register_interests()
.expect("registering interests must work to ensure correct operation");
log::trace!(target: "reactor", "Polling with timeout {timeout:?}");
let mut events = Events::with_capacity(1024);
let res = self.poll.poll(&mut events, Some(timeout));
let tick = Instant::now();
self.service.tick();
if let Err(err) = res {
log::warn!(target: "reactor", "Failure during polling: {err}");
self.service.handle_error(Error::Poll(err));
}
let timers_fired = self.timeouts.remove_expired_by(tick);
if timers_fired > 0 {
log::trace!(target: "reactor", "Timer has fired");
self.service.timer_reacted();
}
if self.handle_events(tick, events) {
loop {
use ControlMessage::*;
use TryRecvError::*;
match self.receiver.try_recv() {
Ok(Command(cmd)) => self.service.handle_command(*cmd),
Ok(Shutdown) => return self.handle_shutdown(),
Err(Empty) => break,
Err(Disconnected) => panic!("control channel disconnected unexpectedly"),
}
}
}
let duration = Instant::now().duration_since(tick);
if duration > LAG_TIMEOUT {
log::debug!(target: "reactor", "Service was busy {:?} which exceeds the timeout of {:?}", duration, LAG_TIMEOUT);
}
self.handle_actions(tick);
}
}
fn handle_events(&mut self, instant: Instant, events: Events) -> bool {
log::trace!(target: "reactor", "Handling events");
let mut awoken = false;
let mut deregistered = Vec::new();
for event in events.into_iter() {
let token = event.token();
if token == WAKER {
log::trace!(target: "reactor", "Awoken by the controller");
awoken = true;
} else if self.listeners.contains_key(&token) {
log::trace!(target: "reactor", token=token.0; "Event from listener with token {}: {:?}", token.0, event);
if !event.is_error() {
let listener = self
.listeners
.get_mut(&token)
.expect("resource disappeared");
listener
.handle(event)
.into_iter()
.for_each(|service_event| {
self.service.listener_reacted(token, service_event, instant);
});
} else {
let listener = self.deregister_listener(token).unwrap_or_else(|| {
panic!("listener with token {} has disappeared", token.0)
});
self.service
.handle_error(Error::ListenerDisconnect(token, listener));
deregistered.push(token);
}
} else if self.transports.contains_key(&token) {
log::trace!(target: "reactor", token=token.0; "Event from transport with token {}: {:?}", token.0, event);
if !event.is_error() {
let transport = self
.transports
.get_mut(&token)
.expect("resource disappeared");
transport
.handle(event)
.into_iter()
.for_each(|service_event| {
self.service
.transport_reacted(token, service_event, instant);
});
} else {
let transport = self.deregister_transport(token).unwrap_or_else(|| {
panic!("transport with token {} has disappeared", token.0)
});
self.service
.handle_error(Error::TransportDisconnect(token, transport));
deregistered.push(token);
}
} else if !deregistered.contains(&token) {
log::debug!(target: "reactor", token=token.0; "Event from unknown token {}: {:?}", token.0, event);
}
}
awoken
}
fn handle_actions(&mut self, instant: Instant) {
while let Some(action) = self.service.next() {
log::trace!(target: "reactor", "Handling action {action} from the service");
if let Err(err) = self.handle_action(action, instant) {
log::warn!(target: "reactor", "Failure: {err}");
self.service.handle_error(err);
}
}
}
fn handle_action(
&mut self,
action: Action<H::Listener, H::Transport>,
instant: Instant,
) -> Result<(), Error<H::Listener, H::Transport>> {
match action {
Action::RegisterListener(token, mut listener) => {
log::trace!(target: "reactor", token=token.0; "Registering listener {:?} with token {}", listener, token.0);
self.poll
.registry()
.register(&mut listener, token, Interest::READABLE)
.map_err(Error::Registration)?;
self.listeners.insert(token, listener);
self.service
.listener_registered(token, &self.listeners[&token]);
}
Action::RegisterTransport(token, mut transport) => {
log::debug!(target: "reactor", token=token.0; "Registering transport");
self.poll
.registry()
.register(&mut transport, token, Interest::READABLE)
.map_err(Error::Registration)?;
self.transports.insert(token, transport);
self.service
.transport_registered(token, &self.transports[&token]);
}
Action::UnregisterListener(token) => {
let Some(listener) = self.deregister_listener(token) else {
return Ok(());
};
log::debug!(target: "reactor", token=token.0; "Handing over listener {listener:?} with token {}", token.0);
self.service.handover_listener(token, listener);
}
Action::UnregisterTransport(token) => {
let Some(transport) = self.deregister_transport(token) else {
return Ok(());
};
log::debug!(target: "reactor", token=token.0; "Handing over transport {transport:?} with token {}", token.0);
self.service.handover_transport(token, transport);
}
Action::Send(token, data) => {
log::trace!(target: "reactor", token=token.0; "Sending {} bytes to {token:?}", data.len());
if let Some(transport) = self.transports.get_mut(&token) {
transport.buffer_write(&data);
} else {
log::debug!(target: "reactor", token=token.0; "No transport with token {token:?} is known!");
}
}
Action::SetTimer(duration) => {
log::trace!(target: "reactor", "Adding timer {duration:?} from now");
self.timeouts.set_timeout(duration, instant);
}
}
Ok(())
}
fn handle_shutdown(self) {
log::info!(target: "reactor", "Shutdown");
}
fn deregister_listener(&mut self, token: Token) -> Option<H::Listener> {
let Some(mut source) = self.listeners.remove(&token) else {
log::debug!(target: "reactor", token=token.0; "Deregistering non-registered listener with token {}", token.0);
return None;
};
if let Err(err) = self.poll.registry().deregister(&mut source) {
log::debug!(target: "reactor", token=token.0; "Failed to deregister listener with token {} from mio: {err}", token.0);
}
Some(source)
}
fn deregister_transport(&mut self, token: Token) -> Option<H::Transport> {
let Some(mut source) = self.transports.remove(&token) else {
log::debug!(target: "reactor", token=token.0; "Deregistering non-registered transport with token {}", token.0);
return None;
};
if let Err(err) = self.poll.registry().deregister(&mut source) {
log::debug!(target: "reactor", token=token.0; "Failed to deregister transport with token {} from mio: {err}", token.0);
}
Some(source)
}
}