use crate::handle::{BodySink, Responder};
use crate::request::{OwnedRequest, Request};
use crate::server::Shared;
use crate::service::{Outcome, Service};
use crate::types::{Body, HeaderBuf, Pushed, Response, StaticReply};
use simoom_rt::offload::OffloadPool;
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::sync::Arc;
pub(crate) enum BodyRef<'b> {
Slice(&'b [u8]),
Static(&'static [u8]),
Owned(Vec<u8>),
}
impl BodyRef<'_> {
pub(crate) fn as_bytes(&self) -> &[u8] {
match self {
BodyRef::Slice(s) => s,
BodyRef::Static(s) => s,
BodyRef::Owned(v) => v,
}
}
fn into_body(self) -> Body {
match self {
BodyRef::Slice(s) => Body::Bytes(s.to_vec()),
BodyRef::Static(s) => Body::Static(s),
BodyRef::Owned(v) => Body::Bytes(v),
}
}
}
impl From<Body> for BodyRef<'static> {
fn from(b: Body) -> Self {
match b {
Body::Empty => BodyRef::Static(b""),
Body::Static(s) => BodyRef::Static(s),
Body::Bytes(v) => BodyRef::Owned(v),
}
}
}
pub(crate) trait Target {
fn headers(&mut self) -> &mut HeaderBuf;
fn trailers(&mut self) -> &mut HeaderBuf;
fn respond(&mut self, status: u16, body: BodyRef<'_>, close: bool);
fn fixed(&mut self, r: &StaticReply, close: bool) {
let h = self.headers();
h.clear();
for (n, v) in r.headers.iter() {
h.push(n, v);
}
self.respond(r.status, BodyRef::Slice(r.body.as_bytes()), close);
}
fn defer(&mut self) -> Responder;
fn undefer(&mut self);
fn stream(&mut self, status: u16, close: bool) -> BodySink;
fn pool(&mut self) -> Option<OffloadPool>;
fn shared(&self) -> &Shared;
fn loop_index(&self) -> usize;
#[cfg(feature = "websocket")]
fn websocket(&mut self, protocol: Option<&[u8]>, handler: Box<dyn crate::ws::WebSocketHandler>) -> bool {
let _ = (protocol, handler);
false
}
}
pub struct Reply<'a> {
t: &'a mut dyn Target,
status: u16,
close: bool,
invalid: bool,
trailers: bool,
#[cfg(feature = "compression")]
coding: Option<crate::compress::Neg>,
}
impl std::fmt::Debug for Reply<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Reply").field("status", &self.status).finish()
}
}
impl<'a> Reply<'a> {
pub(crate) fn new(t: &'a mut dyn Target) -> Reply<'a> {
t.headers().clear();
Reply {
t,
status: 200,
close: false,
invalid: false,
trailers: false,
#[cfg(feature = "compression")]
coding: None,
}
}
#[cfg(feature = "compression")]
pub(crate) fn with_coding(mut self, n: crate::compress::Neg) -> Self {
self.coding = Some(n);
self
}
pub fn status(mut self, status: u16) -> Self {
self.status = status;
self
}
pub fn header(mut self, name: impl AsRef<[u8]>, value: impl AsRef<[u8]>) -> Self {
match self.t.headers().push(name.as_ref(), value.as_ref()) {
Pushed::Close => self.close = true,
Pushed::Invalid => self.invalid = true,
_ => {}
}
self
}
pub fn trailer(mut self, name: impl AsRef<[u8]>, value: impl AsRef<[u8]>) -> Self {
self.trailers = true;
if self.t.trailers().push(name.as_ref(), value.as_ref()) == Pushed::Invalid {
self.invalid = true;
}
self
}
fn drop_trailers(&mut self) {
if self.trailers {
self.t.trailers().clear();
}
}
fn ok(&self) -> bool {
!self.invalid && (200..=999).contains(&self.status)
}
fn finish(mut self, body: BodyRef<'_>) -> Outcome {
if self.ok() {
#[cfg(feature = "compression")]
if let Some(n) = &self.coding {
if let Some(c) = crate::compress::apply(n, self.status, self.t.headers(), body.as_bytes()) {
self.t.respond(self.status, BodyRef::Owned(c), self.close);
return Outcome::Done;
}
}
self.t.respond(self.status, body, self.close);
} else {
self.drop_trailers();
self.t.headers().clear();
self.t.respond(500, BodyRef::Static(b"Internal Server Error"), self.close);
}
Outcome::Done
}
pub fn body(self, body: impl Into<Body>) -> Outcome {
self.finish(body.into().into())
}
pub fn body_static(self, body: &'static [u8]) -> Outcome {
self.finish(BodyRef::Static(body))
}
pub fn body_copy(self, body: &[u8]) -> Outcome {
self.finish(BodyRef::Slice(body))
}
pub fn empty(self) -> Outcome {
self.finish(BodyRef::Static(b""))
}
pub fn send(mut self, response: Response) -> Outcome {
let h = self.t.headers();
h.clear();
for (n, v) in response.headers.iter() {
h.push(n, v);
}
self.status = response.status;
self.close |= response.close;
self.invalid = response.invalid;
if !response.trailers.is_empty() {
self.trailers = true;
*self.t.trailers() = response.trailers;
}
self.finish(response.body.into())
}
pub fn fixed(mut self, r: &StaticReply) -> Outcome {
self.drop_trailers();
self.t.fixed(r, self.close);
Outcome::Done
}
pub fn defer(mut self) -> Responder {
self.drop_trailers();
let r = self.t.defer();
#[cfg(feature = "compression")]
let r = r.with_coding(self.coding.take());
r
}
pub fn stream(mut self) -> BodySink {
self.drop_trailers();
let status = if self.ok() { self.status } else { 500 };
#[cfg(feature = "compression")]
if let Some(n) = &self.coding {
if let Some(e) = crate::compress::stream(n, status, self.t.headers()) {
return self.t.stream(status, self.close).with_encoder(e);
}
}
self.t.stream(status, self.close)
}
pub fn sse(self) -> BodySink {
self.header("content-type", "text/event-stream").header("cache-control", "no-cache").stream()
}
pub fn offload<F>(self, req: &mut Request<'_>, f: F) -> Outcome
where
F: FnOnce(&mut Request<'_>, Reply<'_>) -> Outcome + Send + 'static,
{
let owned = req.to_owned_request();
let shared_loop = req.loop_index;
self.submit(move |responder| run_detached(owned, responder, shared_loop, f))
}
pub fn blocking<F>(self, req: &mut Request<'_>, f: F) -> Outcome
where
F: FnOnce(OwnedRequest) -> Response + Send + 'static,
{
let owned = req.to_owned_request();
self.submit(move |responder| {
let r = catch_unwind(AssertUnwindSafe(|| f(owned))).unwrap_or_else(|_| {
let mut r = Response::error(500);
r.close = true;
r
});
responder.send(r);
})
}
#[cold]
#[inline(never)]
pub(crate) fn offload_watched(self, req: &mut Request<'_>, svc: Arc<dyn Service>, watch: Arc<crate::watchdog::Watch>) -> Outcome {
self.offload(req, move |r, reply| {
let t0 = std::time::Instant::now();
let (o, s) = crate::watchdog::measured(|| svc.call(r, reply));
watch.offloaded_call(t0.elapsed().as_nanos() as u64, s);
o
})
}
fn submit(mut self, job: impl FnOnce(Responder) + Send + 'static) -> Outcome {
self.drop_trailers();
let Some(pool) = self.t.pool() else {
job(self.t.defer());
return Outcome::Deferred;
};
let responder = self.t.defer();
#[cfg(feature = "compression")]
let responder = responder.with_coding(self.coding.take());
match pool.try_submit(Box::new(move || job(responder))) {
Ok(()) => Outcome::Deferred,
Err(_) => {
self.t.undefer();
let li = self.t.loop_index();
self.t.shared().counters(li).overloaded.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let h = self.t.headers();
h.clear();
h.push(b"retry-after", b"1");
h.push(b"content-type", b"text/plain");
self.t.respond(503, BodyRef::Static(b"Service Unavailable"), false);
Outcome::Done
}
}
}
#[cfg(feature = "websocket")]
pub fn websocket(self, handler: impl crate::ws::WebSocketHandler) -> Outcome {
self.websocket_with_protocol(None, handler)
}
#[cfg(feature = "websocket")]
pub fn websocket_with_protocol(mut self, protocol: Option<&str>, handler: impl crate::ws::WebSocketHandler) -> Outcome {
self.drop_trailers();
if !self.t.websocket(protocol.map(str::as_bytes), Box::new(handler)) {
self.t.headers().clear();
self.t.respond(400, BodyRef::Static(b"Bad Request"), true);
}
Outcome::Done
}
}
fn run_detached<F>(mut owned: OwnedRequest, responder: Responder, loop_index: usize, f: F)
where
F: FnOnce(&mut Request<'_>, Reply<'_>) -> Outcome,
{
let mut t = Detached { responder: Some(responder), headers: HeaderBuf::default(), trailers: HeaderBuf::default(), loop_index };
let mut req = owned.as_request();
let r = catch_unwind(AssertUnwindSafe(|| {
let _ = f(&mut req, Reply::new(&mut t));
}));
if let Some(responder) = t.responder.take() {
let mut resp = Response::error(500);
resp.close = r.is_err();
responder.send(resp);
}
}
struct Detached {
responder: Option<Responder>,
headers: HeaderBuf,
trailers: HeaderBuf,
loop_index: usize,
}
impl Target for Detached {
fn headers(&mut self) -> &mut HeaderBuf {
&mut self.headers
}
fn trailers(&mut self) -> &mut HeaderBuf {
&mut self.trailers
}
fn respond(&mut self, status: u16, body: BodyRef<'_>, close: bool) {
if let Some(r) = self.responder.take() {
r.send(Response { status, headers: std::mem::take(&mut self.headers), body: body.into_body(), close, invalid: false, trailers: std::mem::take(&mut self.trailers) });
}
}
fn fixed(&mut self, r: &StaticReply, close: bool) {
if let Some(resp) = self.responder.take() {
let mut x = r.to_response();
x.close = close;
resp.send(x);
}
}
fn defer(&mut self) -> Responder {
self.responder.take().expect("a reply completes once")
}
fn undefer(&mut self) {}
fn stream(&mut self, status: u16, close: bool) -> BodySink {
let r = self.responder.take().expect("a reply completes once");
r.stream(Response { status, headers: std::mem::take(&mut self.headers), body: Body::Empty, close, invalid: false, trailers: HeaderBuf::default() })
}
fn pool(&mut self) -> Option<OffloadPool> {
None
}
fn shared(&self) -> &Shared {
unreachable!("detached replies never refuse work")
}
fn loop_index(&self) -> usize {
self.loop_index
}
}