use core::fmt;
use std::time::Duration;
use crate::Finished;
use crate::client::ClientInfo;
use crate::config::{Limits, Profile, ProxySource};
use crate::envelope::Envelope;
use crate::reply::Reply;
use crate::service::{HeadAction, Outcome, WorkClass};
#[cfg(feature = "http1")]
pub(crate) mod http1;
#[cfg(feature = "http1")]
pub use http1::Http1;
pub trait Engine: Send + 'static {
type Head<'a>: Copy
where
Self: 'a;
fn feed(&mut self, bytes: &[u8]) -> usize;
fn feed_eof(&mut self);
fn poll(&mut self) -> Result<EngineEvent<'_>, EngineError>;
fn head(&self) -> Option<Self::Head<'_>>;
fn declared_length(&self) -> Option<u64>;
fn expects_continue(&self) -> bool {
false
}
fn head_len(&self) -> usize {
0
}
fn progress(&self) -> EngineProgress;
fn send_continue(&mut self, out: &mut OutBuf) -> Result<(), EngineError> {
let _ = out;
Ok(())
}
fn refuse(&mut self, refusal: &Refusal, out: &mut OutBuf);
fn close_after_reply(&mut self);
fn keep_alive(&self) -> bool;
fn requests_served(&self) -> u64;
fn remaining_input(&self) -> &[u8];
fn reset(&mut self);
fn shrink_idle(&mut self) -> bool {
false
}
}
#[non_exhaustive]
#[derive(Debug, PartialEq, Eq)]
pub enum EngineEvent<'a> {
Head,
Body(&'a [u8]),
BodyEnd,
NeedInput,
AwaitingReply,
Upgraded,
Closed,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum EngineProgress {
Idle,
Head,
Body,
AwaitingReply,
WritingReply,
Upgraded,
Closed,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum RefusalKind {
Malformed,
TooLarge,
HeadTimeout,
BodyTimeout,
ReplyTimeout,
Busy,
Draining,
Denied,
Ingress,
Service,
Internal,
}
impl RefusalKind {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
RefusalKind::Malformed => "malformed",
RefusalKind::TooLarge => "too_large",
RefusalKind::HeadTimeout => "head_timeout",
RefusalKind::BodyTimeout => "body_timeout",
RefusalKind::ReplyTimeout => "reply_timeout",
RefusalKind::Busy => "busy",
RefusalKind::Draining => "draining",
RefusalKind::Denied => "denied",
RefusalKind::Ingress => "ingress",
RefusalKind::Service => "service",
RefusalKind::Internal => "internal",
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Refusal {
pub kind: RefusalKind,
pub status: u16,
pub retry_after: Option<Duration>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct EngineError {
pub kind: RefusalKind,
pub status: u16,
}
impl fmt::Display for EngineError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"engine refused the request: {} ({})",
self.kind.as_str(),
self.status
)
}
}
impl std::error::Error for EngineError {}
#[derive(Default)]
pub struct OutBuf {
pub(crate) bytes: Vec<u8>,
}
impl OutBuf {
#[must_use]
pub fn new() -> Self {
OutBuf { bytes: Vec::new() }
}
pub fn push(&mut self, bytes: &[u8]) {
self.bytes.extend_from_slice(bytes);
}
#[must_use]
pub fn len(&self) -> usize {
self.bytes.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.bytes.is_empty()
}
#[must_use]
pub fn as_slice(&self) -> &[u8] {
&self.bytes
}
pub(crate) fn clear(&mut self, wipe: bool) {
if wipe {
wipe_bytes(&mut self.bytes);
}
self.bytes.clear();
}
pub(crate) fn shrink(&mut self, retain: usize) {
if self.bytes.capacity() > retain {
self.bytes.shrink_to(retain);
}
}
}
impl fmt::Debug for OutBuf {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("OutBuf")
.field("len", &self.bytes.len())
.finish()
}
}
#[cfg(feature = "http1")]
impl http_net::Output for OutBuf {
fn put(&mut self, bytes: &[u8]) {
self.bytes.extend_from_slice(bytes);
}
fn reserve(&mut self, additional: usize) {
self.bytes.reserve(additional);
}
}
pub(crate) fn wipe_bytes(bytes: &mut [u8]) {
for b in bytes.iter_mut() {
*b = 0;
}
core::hint::black_box(&mut *bytes);
}
pub struct HeadContext<'c> {
pub(crate) client: &'c ClientInfo,
pub(crate) proxy: Option<&'c ProxySource>,
pub(crate) declared_scheme: crate::client::Scheme,
pub(crate) resolved: Option<ClientInfo>,
}
impl HeadContext<'_> {
#[must_use]
pub fn client(&self) -> &ClientInfo {
self.client
}
#[must_use]
pub fn resolved(&self) -> Option<&ClientInfo> {
self.resolved.as_ref()
}
}
pub trait Family: Send + Sync + 'static {
type Engine: Engine;
type Head<'a>;
type Request<'a>;
type Parts: Default + Send + 'static;
fn engine(&self, profile: &Profile, limits: &Limits) -> Self::Engine;
fn head<'a>(
&self,
raw: <Self::Engine as Engine>::Head<'a>,
cx: &HeadContext<'_>,
) -> Result<(Self::Head<'a>, ClientInfo), EngineError>;
fn request<'a>(&self, head: Self::Head<'a>, body: &'a [u8]) -> Self::Request<'a>;
fn new_parts(&self) -> Self::Parts;
fn clear_parts(&self, parts: &mut Self::Parts, wipe: bool);
fn respond(
&self,
engine: &mut Self::Engine,
parts: &mut Self::Parts,
unix_secs: u64,
out: &mut OutBuf,
) -> Result<u16, EngineError>;
}
pub trait Service<F: Family>: Send + Sync {
const WORK: WorkClass = WorkClass::Quick;
fn head(&self, head: &F::Head<'_>, env: &mut Envelope) -> HeadAction {
let _ = (head, env);
HeadAction::Proceed
}
fn call(&self, request: &F::Request<'_>, env: &Envelope, reply: &mut Reply<F>) -> Outcome;
fn done(&self, env: &Envelope, finished: &Finished) {
let _ = (env, finished);
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NoCustom {}
impl Engine for NoCustom {
type Head<'a> = ();
fn feed(&mut self, _bytes: &[u8]) -> usize {
match *self {}
}
fn feed_eof(&mut self) {
match *self {}
}
fn poll(&mut self) -> Result<EngineEvent<'_>, EngineError> {
match *self {}
}
fn head(&self) -> Option<()> {
match *self {}
}
fn declared_length(&self) -> Option<u64> {
match *self {}
}
fn progress(&self) -> EngineProgress {
match *self {}
}
fn refuse(&mut self, _refusal: &Refusal, _out: &mut OutBuf) {
match *self {}
}
fn close_after_reply(&mut self) {
match *self {}
}
fn keep_alive(&self) -> bool {
match *self {}
}
fn requests_served(&self) -> u64 {
match *self {}
}
fn remaining_input(&self) -> &[u8] {
match *self {}
}
fn reset(&mut self) {
match *self {}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_outbuf_clear_and_wipe() {
let mut o = OutBuf::new();
o.push(b"hello world");
assert_eq!(o.as_slice(), b"hello world");
o.clear(true);
assert!(o.is_empty());
}
#[test]
fn test_refusal_kind_names() {
assert_eq!(RefusalKind::Busy.as_str(), "busy");
assert_eq!(
Refusal {
kind: RefusalKind::Malformed,
status: 400,
retry_after: None
}
.status,
400
);
}
}