use crate::types::{HeaderBuf, Response};
use simoom_rt::ConnHandle;
use std::future::Future;
use std::io;
use std::pin::Pin;
use std::sync::{Arc, Condvar, Mutex, PoisonError};
use std::task::{Context, Poll, Waker};
#[derive(Clone, Debug)]
pub(crate) enum Route {
Conn(ConnHandle),
#[cfg(feature = "http3")]
Ep(simoom_rt::EndpointHandle),
}
pub(crate) struct Envelope {
#[cfg_attr(not(feature = "http3"), allow(dead_code))]
pub(crate) conn: u64,
pub(crate) token: u64,
pub(crate) msg: Msg,
}
pub(crate) enum Msg {
Reply(Response),
Stream(Response, Arc<SinkShared>),
Chunk(Vec<u8>),
End { abort: bool },
EndTrailers(HeaderBuf),
Dropped,
BodyResume,
#[cfg(feature = "websocket")]
Ws(crate::ws::WsCmd),
}
impl Route {
pub(crate) fn send(&self, conn: u64, token: u64, msg: Msg) -> bool {
let env = Box::new(Envelope { conn, token, msg });
match self {
Route::Conn(h) => h.send(env),
#[cfg(feature = "http3")]
Route::Ep(h) => h.send(env),
}
}
}
#[derive(Default)]
pub(crate) struct Cancel {
flag: std::sync::atomic::AtomicBool,
hook: Mutex<Option<Box<dyn FnOnce() + Send>>>,
}
impl std::fmt::Debug for Cancel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Cancel").field("cancelled", &self.is_set()).finish()
}
}
impl Cancel {
pub(crate) fn is_set(&self) -> bool {
self.flag.load(std::sync::atomic::Ordering::Acquire)
}
pub(crate) fn cancel(&self) {
if self.flag.swap(true, std::sync::atomic::Ordering::AcqRel) {
return;
}
let hook = self.hook.lock().unwrap_or_else(|e| e.into_inner()).take();
if let Some(f) = hook {
f();
}
}
}
#[derive(Debug)]
pub struct Responder {
route: Route,
conn: u64,
token: u64,
limit: usize,
armed: bool,
cancel: Arc<Cancel>,
#[cfg(feature = "compression")]
coding: Option<crate::compress::Neg>,
}
impl Responder {
pub(crate) fn new(route: Route, conn: u64, token: u64, limit: usize, cancel: Arc<Cancel>) -> Responder {
Responder {
route,
conn,
token,
limit,
armed: true,
cancel,
#[cfg(feature = "compression")]
coding: None,
}
}
#[cfg(feature = "compression")]
pub(crate) fn with_coding(mut self, n: Option<crate::compress::Neg>) -> Self {
if n.is_some() {
self.coding = n;
}
self
}
pub fn is_cancelled(&self) -> bool {
self.cancel.is_set()
}
pub fn on_cancel(&self, f: impl FnOnce() + Send + 'static) {
let mut g = self.cancel.hook.lock().unwrap_or_else(|e| e.into_inner());
if self.cancel.is_set() {
drop(g);
f();
return;
}
*g = Some(Box::new(f));
drop(g);
if self.cancel.is_set() {
let f = self.cancel.hook.lock().unwrap_or_else(|e| e.into_inner()).take();
if let Some(f) = f {
f();
}
}
}
pub fn send(mut self, response: Response) -> bool {
self.armed = false;
#[cfg(feature = "compression")]
let response = {
let mut response = response;
if let Some(n) = self.coding.take() {
crate::compress::apply_response(&n, &mut response);
}
response
};
self.route.send(self.conn, self.token, Msg::Reply(response))
}
pub fn stream(mut self, head: Response) -> BodySink {
self.armed = false;
let shared = Arc::new(SinkShared::new(self.limit));
#[cfg(feature = "compression")]
let (head, enc) = {
let mut head = head;
let enc = match self.coding.take() {
Some(n) if !head.invalid => crate::compress::stream(&n, head.status, &mut head.headers),
_ => None,
};
(head, enc)
};
self.route.send(self.conn, self.token, Msg::Stream(head, shared.clone()));
let sink = BodySink::new(self.route.clone(), self.conn, self.token, shared);
#[cfg(feature = "compression")]
let sink = match enc {
Some(e) => sink.with_encoder(e),
None => sink,
};
sink
}
}
impl Drop for Responder {
fn drop(&mut self) {
if self.armed {
self.route.send(self.conn, self.token, Msg::Dropped);
}
}
}
#[derive(Debug, Default)]
struct SinkState {
pending: usize,
closed: bool,
waker: Option<Waker>,
}
#[derive(Debug)]
pub(crate) struct SinkShared {
st: Mutex<SinkState>,
cv: Condvar,
limit: usize,
}
impl SinkShared {
pub(crate) fn new(limit: usize) -> SinkShared {
SinkShared { st: Mutex::new(SinkState::default()), cv: Condvar::new(), limit: limit.max(1) }
}
pub(crate) fn credit(&self, n: usize) {
if n == 0 {
return;
}
let mut st = self.st.lock().unwrap_or_else(PoisonError::into_inner);
st.pending = st.pending.saturating_sub(n);
let w = st.waker.take();
drop(st);
self.cv.notify_all();
if let Some(w) = w {
w.wake();
}
}
pub(crate) fn close(&self) {
let mut st = self.st.lock().unwrap_or_else(PoisonError::into_inner);
st.closed = true;
let w = st.waker.take();
drop(st);
self.cv.notify_all();
if let Some(w) = w {
w.wake();
}
}
}
fn broken() -> io::Error {
io::Error::new(io::ErrorKind::BrokenPipe, "the connection is gone")
}
#[derive(Debug)]
pub struct BodySink {
route: Route,
conn: u64,
token: u64,
shared: Arc<SinkShared>,
done: bool,
#[cfg(feature = "compression")]
enc: Option<Box<simoom_compress::Encoder>>,
}
impl BodySink {
pub(crate) fn new(route: Route, conn: u64, token: u64, shared: Arc<SinkShared>) -> BodySink {
BodySink {
route,
conn,
token,
shared,
done: false,
#[cfg(feature = "compression")]
enc: None,
}
}
#[cfg(feature = "compression")]
pub(crate) fn with_encoder(mut self, e: Box<simoom_compress::Encoder>) -> Self {
self.enc = Some(e);
self
}
fn push(&mut self, data: &[u8]) -> io::Result<()> {
if data.is_empty() {
return Ok(());
}
if !self.route.send(self.conn, self.token, Msg::Chunk(data.to_vec())) {
return Err(broken());
}
Ok(())
}
#[inline]
fn coded<'d>(&mut self, data: &'d [u8]) -> std::borrow::Cow<'d, [u8]> {
#[cfg(feature = "compression")]
if let Some(e) = &mut self.enc {
if !data.is_empty() {
return std::borrow::Cow::Owned(crate::compress::stream_chunk(e, data));
}
}
std::borrow::Cow::Borrowed(data)
}
fn end_coding(&mut self) {
#[cfg(feature = "compression")]
if let Some(mut e) = self.enc.take() {
let tail = crate::compress::stream_end(&mut e);
let _ = self.push(&tail);
}
}
pub fn write(&mut self, data: &[u8]) -> io::Result<()> {
let data = self.coded(data);
{
let mut st = self.shared.st.lock().unwrap_or_else(PoisonError::into_inner);
while st.pending >= self.shared.limit && !st.closed {
st = self.shared.cv.wait(st).unwrap_or_else(PoisonError::into_inner);
}
if st.closed {
return Err(broken());
}
st.pending += data.len();
}
self.push(&data)
}
pub fn try_write(&mut self, data: &[u8]) -> io::Result<usize> {
{
let mut st = self.shared.st.lock().unwrap_or_else(PoisonError::into_inner);
if st.closed {
return Err(broken());
}
if st.pending >= self.shared.limit {
return Err(io::ErrorKind::WouldBlock.into());
}
#[cfg(feature = "compression")]
if self.enc.is_some() {
drop(st);
let coded = self.coded(data);
self.shared.st.lock().unwrap_or_else(PoisonError::into_inner).pending += coded.len();
self.push(&coded)?;
return Ok(data.len());
}
st.pending += data.len();
}
self.push(data)?;
Ok(data.len())
}
pub fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
let mut st = self.shared.st.lock().unwrap_or_else(PoisonError::into_inner);
if st.closed {
return Poll::Ready(Err(broken()));
}
if st.pending < self.shared.limit {
return Poll::Ready(Ok(()));
}
st.waker = Some(cx.waker().clone());
Poll::Pending
}
pub fn ready(&mut self) -> Ready<'_> {
Ready { sink: self }
}
pub fn send_event(&mut self, data: &str) -> io::Result<()> {
let mut b = Vec::with_capacity(data.len() + 16);
for line in data.split('\n') {
b.extend_from_slice(b"data: ");
b.extend_from_slice(line.as_bytes());
b.push(b'\n');
}
b.push(b'\n');
self.write(&b)
}
pub fn is_closed(&self) -> bool {
self.shared.st.lock().unwrap_or_else(PoisonError::into_inner).closed
}
pub fn finish(mut self) {
self.done = true;
self.end_coding();
self.route.send(self.conn, self.token, Msg::End { abort: false });
}
pub fn finish_with_trailers(mut self, trailers: &[(&str, &str)]) {
self.done = true;
self.end_coding();
let mut t = HeaderBuf::default();
for (n, v) in trailers {
t.push(n.as_bytes(), v.as_bytes());
}
self.route.send(self.conn, self.token, Msg::EndTrailers(t));
}
pub fn abort(mut self) {
self.done = true;
self.route.send(self.conn, self.token, Msg::End { abort: true });
}
}
impl io::Write for BodySink {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
BodySink::write(self, buf)?;
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl Drop for BodySink {
fn drop(&mut self) {
if !self.done {
self.end_coding();
self.route.send(self.conn, self.token, Msg::End { abort: false });
}
}
}
#[derive(Debug)]
pub struct Ready<'a> {
sink: &'a mut BodySink,
}
impl Future for Ready<'_> {
type Output = io::Result<()>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.sink.poll_ready(cx)
}
}
#[derive(Debug, Default)]
struct SrcState {
buf: Vec<u8>,
pos: usize,
done: bool,
error: Option<&'static str>,
waker: Option<Waker>,
loop_paused: bool,
abandoned: bool,
trailers: Vec<(Vec<u8>, Vec<u8>)>,
}
#[derive(Debug)]
pub(crate) struct SrcShared {
st: Mutex<SrcState>,
cv: Condvar,
cap: usize,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) enum Pushed {
More,
Full,
Abandoned,
}
impl SrcShared {
pub(crate) fn new(cap: usize, prefill: Vec<u8>) -> SrcShared {
SrcShared { st: Mutex::new(SrcState { buf: prefill, ..SrcState::default() }), cv: Condvar::new(), cap: cap.max(1024) }
}
fn wake(&self, st: std::sync::MutexGuard<'_, SrcState>) {
let mut st = st;
let w = st.waker.take();
drop(st);
self.cv.notify_all();
if let Some(w) = w {
w.wake();
}
}
pub(crate) fn push(&self, data: &[u8]) -> Pushed {
let mut st = self.st.lock().unwrap_or_else(PoisonError::into_inner);
if st.abandoned {
return Pushed::Abandoned;
}
if st.pos > 0 && st.pos == st.buf.len() {
st.buf.clear();
st.pos = 0;
}
st.buf.extend_from_slice(data);
let full = st.buf.len() - st.pos >= self.cap;
if full {
st.loop_paused = true;
}
self.wake(st);
if full {
Pushed::Full
} else {
Pushed::More
}
}
#[cfg(feature = "http2")]
pub(crate) fn finish(&self) {
self.finish_with(Vec::new());
}
pub(crate) fn finish_with(&self, trailers: Vec<(Vec<u8>, Vec<u8>)>) {
let mut st = self.st.lock().unwrap_or_else(PoisonError::into_inner);
st.done = true;
st.trailers = trailers;
self.wake(st);
}
pub(crate) fn fail(&self, why: &'static str) {
let mut st = self.st.lock().unwrap_or_else(PoisonError::into_inner);
if !st.done {
st.error = Some(why);
}
self.wake(st);
}
}
#[derive(Debug)]
pub struct BodySource {
shared: Arc<SrcShared>,
route: Route,
conn: u64,
token: u64,
}
impl BodySource {
pub(crate) fn new(shared: Arc<SrcShared>, route: Route, conn: u64, token: u64) -> BodySource {
BodySource { shared, route, conn, token }
}
fn take(&mut self, st: &mut SrcState, buf: &mut [u8]) -> usize {
let n = buf.len().min(st.buf.len() - st.pos);
buf[..n].copy_from_slice(&st.buf[st.pos..st.pos + n]);
st.pos += n;
if st.pos == st.buf.len() {
st.buf.clear();
st.pos = 0;
}
if st.loop_paused && st.buf.len() - st.pos < self.shared.cap / 2 {
st.loop_paused = false;
self.route.send(self.conn, self.token, Msg::BodyResume);
}
n
}
fn step(&mut self, st: &mut SrcState, buf: &mut [u8]) -> Option<io::Result<usize>> {
if st.pos < st.buf.len() {
return Some(Ok(self.take(st, buf)));
}
if let Some(e) = st.error {
return Some(Err(io::Error::new(io::ErrorKind::UnexpectedEof, e)));
}
if st.done || buf.is_empty() {
return Some(Ok(0));
}
None
}
pub fn try_read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let shared = self.shared.clone();
let mut st = shared.st.lock().unwrap_or_else(PoisonError::into_inner);
self.step(&mut st, buf).unwrap_or_else(|| Err(io::ErrorKind::WouldBlock.into()))
}
pub fn poll_read(&mut self, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<io::Result<usize>> {
let shared = self.shared.clone();
let mut st = shared.st.lock().unwrap_or_else(PoisonError::into_inner);
match self.step(&mut st, buf) {
Some(r) => Poll::Ready(r),
None => {
st.waker = Some(cx.waker().clone());
Poll::Pending
}
}
}
pub fn is_complete(&self) -> bool {
self.shared.st.lock().unwrap_or_else(PoisonError::into_inner).done
}
pub fn trailers(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
let st = self.shared.st.lock().unwrap_or_else(PoisonError::into_inner);
if st.done {
st.trailers.clone()
} else {
Vec::new()
}
}
}
impl io::Read for BodySource {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let shared = self.shared.clone();
let mut st = shared.st.lock().unwrap_or_else(PoisonError::into_inner);
loop {
if let Some(r) = self.step(&mut st, buf) {
return r;
}
st = shared.cv.wait(st).unwrap_or_else(PoisonError::into_inner);
}
}
}
impl Drop for BodySource {
fn drop(&mut self) {
let mut st = self.shared.st.lock().unwrap_or_else(PoisonError::into_inner);
st.abandoned = true;
st.buf = Vec::new();
st.pos = 0;
if st.loop_paused {
st.loop_paused = false;
drop(st);
self.route.send(self.conn, self.token, Msg::BodyResume);
}
}
}