use std::collections::VecDeque;
use std::time::{Duration, Instant};
use bytes::Bytes;
use moqtap_codec::version::DraftVersion;
use crate::action::{Action, DropMode, StreamAction};
use crate::capability::{classify, ActionKind, CapCtx, Precondition, Refusal, Site, Support};
use crate::egress::{self, Deferral, Pending, PendingQueue, SessionCloser, Terminal};
use crate::event::{Effect, ImpairmentKind, ProxyEvent, SessionId};
use crate::instrument::Recorder;
use crate::observer::ProxyObserver;
use crate::shape::StreamKey;
use crate::types::{DataStreamType, ObjectMeta, ProxySide};
pub(crate) const MAX_APPLICATION_ERROR_CODE: u64 = (1u64 << 62) - 1;
#[derive(Debug)]
pub(crate) enum Target<'a> {
Control {
raw: Bytes,
},
Object {
meta: &'a ObjectMeta,
subgroup_id_mode: Option<u8>,
raw: Bytes,
},
Datagram {
raw: Bytes,
header_len: Option<usize>,
is_status: bool,
},
StreamEnd {
is_control_stream: bool,
},
}
impl Target<'_> {
pub(crate) fn site(&self) -> Site {
match self {
Target::Control { .. } => Site::Control,
Target::Object { .. } => Site::Object,
Target::Datagram { .. } => Site::Datagram,
Target::StreamEnd { .. } => Site::StreamEnd,
}
}
fn raw(&self) -> Option<Bytes> {
match self {
Target::Control { raw } | Target::Object { raw, .. } | Target::Datagram { raw, .. } => {
Some(raw.clone())
}
Target::StreamEnd { .. } => None,
}
}
fn payload_delimited(&self, draft: DraftVersion) -> Option<bool> {
match self {
Target::Object { .. } => Some(true),
Target::Datagram { header_len, is_status, .. } => {
Some(header_len.is_some() && draft != DraftVersion::Draft14 && !*is_status)
}
Target::Control { .. } | Target::StreamEnd { .. } => None,
}
}
fn payload_offset(&self, draft: DraftVersion) -> Option<usize> {
if self.payload_delimited(draft) != Some(true) {
return None;
}
match self {
Target::Object { meta, raw, .. } => {
usize::try_from(meta.payload_len).ok().and_then(|n| raw.len().checked_sub(n))
}
Target::Datagram { header_len, .. } => *header_len,
Target::Control { .. } | Target::StreamEnd { .. } => None,
}
}
fn cap_ctx(&self, draft: DraftVersion, replacement_len: Option<u64>) -> CapCtx {
let mut cx = CapCtx { draft: Some(draft), replacement_len, ..CapCtx::default() };
match self {
Target::Control { .. } => {}
Target::Object { meta, subgroup_id_mode, .. } => {
cx.stream_kind = Some(meta.stream_kind);
cx.index_in_stream = Some(meta.index_in_stream);
cx.subgroup_id_resolved = Some(meta.subgroup_id.is_some());
cx.is_status_object = Some(meta.status.is_some());
cx.payload_len = Some(meta.payload_len);
cx.payload_delimited = Some(true);
cx.subgroup_id_mode = *subgroup_id_mode;
}
Target::Datagram { is_status, .. } => {
cx.is_status_object = Some(*is_status);
cx.payload_delimited = self.payload_delimited(draft);
}
Target::StreamEnd { is_control_stream } => {
cx.is_control_stream = Some(*is_control_stream);
}
}
cx
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum StreamSite {
Open,
Header,
}
impl StreamSite {
pub(crate) fn site(self) -> Site {
match self {
StreamSite::Open => Site::StreamOpen,
StreamSite::Header => Site::StreamHeader,
}
}
}
#[derive(Debug)]
pub(crate) struct Unit<'a> {
pub(crate) target: Target<'a>,
pub(crate) draft: DraftVersion,
pub(crate) arrived_at: Instant,
}
#[derive(Debug)]
pub(crate) struct Queue<'a> {
pub(crate) pending: &'a mut PendingQueue,
pub(crate) deferred: &'a mut DeferredEffects,
}
#[derive(Debug)]
pub(crate) struct Engine<'a> {
pub(crate) queue: Option<Queue<'a>>,
pub(crate) closer: &'a SessionCloser,
}
impl Engine<'_> {
fn queue_is_busy(&self) -> bool {
self.queue.as_ref().is_some_and(|q| !q.pending.is_empty() || q.pending.is_shaped())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Deferred {
pub(crate) action: ActionKind,
pub(crate) effect: Effect,
}
#[derive(Debug, Default)]
pub(crate) struct DeferredEffects {
q: VecDeque<Option<Deferred>>,
}
impl DeferredEffects {
pub(crate) fn new() -> Self {
Self::default()
}
#[allow(dead_code)]
pub(crate) fn len(&self) -> usize {
self.q.len()
}
#[allow(dead_code)]
pub(crate) fn is_empty(&self) -> bool {
self.q.is_empty()
}
pub(crate) fn pop(&mut self) -> Option<Deferred> {
self.q.pop_front().flatten()
}
pub(crate) fn take_all(&mut self) -> Vec<Deferred> {
std::mem::take(&mut self.q).into_iter().flatten().collect()
}
pub(crate) fn clear(&mut self) {
self.q.clear();
}
fn push(&mut self, entry: Option<Deferred>) {
self.q.push_back(entry);
}
}
#[derive(Clone, Copy)]
pub(crate) struct Reporter<'a> {
observer: &'a dyn ProxyObserver,
enabled: bool,
counters: &'a Recorder,
session_id: SessionId,
side: ProxySide,
stream_id: Option<u64>,
}
impl std::fmt::Debug for Reporter<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Reporter")
.field("enabled", &self.enabled)
.field("session_id", &self.session_id)
.field("side", &self.side)
.field("stream_id", &self.stream_id)
.finish_non_exhaustive()
}
}
impl<'a> Reporter<'a> {
pub(crate) fn new(
observer: &'a dyn ProxyObserver,
enabled: bool,
counters: &'a Recorder,
session_id: SessionId,
side: ProxySide,
stream_id: Option<u64>,
) -> Self {
Self { observer, enabled, counters, session_id, side, stream_id }
}
pub(crate) fn applied(&self, site: Site, action: ActionKind, effect: Effect) {
match action {
ActionKind::Delay => self.counters.note_unit_delayed(),
ActionKind::Truncate => self.counters.note_object_truncated(),
_ => {}
}
self.emit(ProxyEvent::ActionApplied {
session_id: self.session_id,
side: self.side,
stream_id: self.stream_id,
site,
action,
effect,
});
}
pub(crate) fn applied_deferred(&self, site: Site, deferred: Deferred) {
self.applied(site, deferred.action, deferred.effect);
}
pub(crate) fn refused(&self, site: Site, action: ActionKind, refusal: Refusal) {
self.counters.note_action_refused();
self.emit(ProxyEvent::ActionRefused {
session_id: self.session_id,
side: self.side,
stream_id: self.stream_id,
site,
action,
refusal,
});
}
pub(crate) fn failed(&self, site: Site, action: ActionKind, error: String) {
self.emit(ProxyEvent::ActionFailed {
session_id: self.session_id,
side: self.side,
site,
action,
error,
});
}
pub(crate) fn impairment(&self, kind: ImpairmentKind) {
let leg = crate::event::impairment_leg(&kind, self.side);
self.emit(ProxyEvent::Impairment {
session_id: self.session_id,
side: self.side,
leg,
kind,
});
}
fn emit(&self, event: ProxyEvent) {
if self.enabled {
self.observer.on_event(&event);
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum Plan {
WriteNow(Bytes),
Nothing,
Terminal,
RejectStream {
code: u64,
},
OpenStreamAfter {
after: Duration,
},
SerializeStreamAfter {
target: StreamKey,
},
CloseSession {
code: u32,
reason: Bytes,
},
}
#[derive(Debug, Clone)]
pub(crate) struct Outcome {
pub(crate) plan: Plan,
pub(crate) result: Result<Effect, Refusal>,
pub(crate) note_elided: bool,
#[allow(dead_code)]
pub(crate) clamped: Option<Deferral>,
#[allow(dead_code)]
pub(crate) entered_backpressure: bool,
}
impl Outcome {
pub(crate) fn is_applied(&self) -> bool {
self.result.is_ok()
}
}
pub(crate) fn execute(
unit: &Unit<'_>,
action: Action,
engine: &mut Engine<'_>,
report: &Reporter<'_>,
) -> Outcome {
let site = unit.target.site();
match plan_action(unit, action, engine, report) {
Ok(applied) => {
report.applied(site, applied.action, applied.effect.clone());
Outcome {
plan: applied.plan,
result: Ok(applied.effect),
note_elided: applied.note_elided,
clamped: applied.clamped,
entered_backpressure: applied.entered_backpressure,
}
}
Err(Refused { action, refusal }) => {
report.refused(site, action, refusal.clone());
let (plan, entered_backpressure) = forward_unchanged(unit, engine, report);
Outcome {
plan,
result: Err(refusal),
note_elided: false,
clamped: None,
entered_backpressure,
}
}
}
}
pub(crate) fn execute_stream(
site: StreamSite,
draft: DraftVersion,
action: StreamAction,
report: &Reporter<'_>,
) -> Outcome {
let published = site.site();
let cx = CapCtx { draft: Some(draft), ..CapCtx::default() };
let (kind, effect) = match action {
StreamAction::Open => (ActionKind::Open, Effect::ForwardedVerbatim),
StreamAction::Reject { code } => (ActionKind::Reject, Effect::StreamRejected { code }),
StreamAction::OpenAfter(_) => (ActionKind::OpenAfter, Effect::ForwardedVerbatim),
StreamAction::SerializeAfter(_) => (ActionKind::SerializeAfter, Effect::ForwardedVerbatim),
};
let refuse = |refusal: Refusal| {
report.refused(published, kind, refusal.clone());
Outcome {
plan: Plan::Nothing,
result: Err(refusal),
note_elided: false,
clamped: None,
entered_backpressure: false,
}
};
if let Err(refusal) = admit(classify(published, kind, &cx)) {
return refuse(refusal);
}
if let StreamAction::Reject { code } = action {
if let Err(refusal) = check_error_code(code) {
return refuse(refusal);
}
}
report.applied(published, kind, effect.clone());
Outcome {
plan: match action {
StreamAction::Open => Plan::Nothing,
StreamAction::Reject { code } => Plan::RejectStream { code },
StreamAction::OpenAfter(after) => Plan::OpenStreamAfter { after },
StreamAction::SerializeAfter(target) => Plan::SerializeStreamAfter { target },
},
result: Ok(effect),
note_elided: false,
clamped: None,
entered_backpressure: false,
}
}
pub(crate) fn shape_elide(unit: &Unit<'_>, report: &Reporter<'_>) -> bool {
debug_assert!(
matches!(unit.target, Target::Object { .. }),
"only a framed object can be tail-dropped: the shaper never sees anything else",
);
match admit_kind(Site::Object, ActionKind::DropElide, unit, None) {
Ok(()) => {
report.counters.note_object_elided();
true
}
Err(Refused { action, refusal }) => {
report.refused(Site::Object, action, refusal);
false
}
}
}
#[derive(Debug, Clone)]
struct Refused {
action: ActionKind,
refusal: Refusal,
}
impl Refused {
fn new(action: ActionKind, refusal: Refusal) -> Self {
Self { action, refusal }
}
}
#[derive(Debug)]
struct AppliedPlan {
action: ActionKind,
effect: Effect,
plan: Plan,
note_elided: bool,
clamped: Option<Deferral>,
entered_backpressure: bool,
}
impl AppliedPlan {
fn simple(action: ActionKind, effect: Effect, plan: Plan) -> Self {
Self {
action,
effect,
plan,
note_elided: false,
clamped: None,
entered_backpressure: false,
}
}
}
#[derive(Debug)]
struct Content {
kind: ActionKind,
payload: Payload,
effect: Effect,
note_elided: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum Payload {
Write(Bytes),
Elide,
Absent,
}
fn plan_action(
unit: &Unit<'_>,
action: Action,
engine: &mut Engine<'_>,
report: &Reporter<'_>,
) -> Result<AppliedPlan, Refused> {
let site = unit.target.site();
match action {
Action::Delay { by, then } => {
admit_kind(site, ActionKind::Delay, unit, None)?;
check_composition(&then)?;
let content = prepare_content(unit, &then, report)?;
let config = queue_config(engine);
let deferral = egress::defer_by(unit.arrived_at, by, &config);
if deferral.was_clamped() {
report.impairment(ImpairmentKind::HoldClamped {
requested: Some(deferral.requested),
applied: deferral.applied,
});
}
let pending = pending_for(&content.payload, deferral.release_at);
let push = push_unit(engine, report, pending, Some(content.deferred()));
Ok(AppliedPlan {
action: ActionKind::Delay,
effect: Effect::Queued { release_at: push.release_at },
plan: Plan::Nothing,
note_elided: content.note_elided,
clamped: Some(deferral),
entered_backpressure: push.entered_backpressure,
})
}
Action::Hold { gate, then } => {
admit_kind(site, ActionKind::Hold, unit, None)?;
check_composition(&then)?;
let content = prepare_content(unit, &then, report)?;
let config = queue_config(engine);
let ceiling = egress::hold_ceiling(unit.arrived_at, &config);
let pending = pending_for(&content.payload, ceiling).with_gate(gate);
let push = push_unit(engine, report, pending, Some(content.deferred()));
Ok(AppliedPlan {
action: ActionKind::Hold,
effect: Effect::Queued { release_at: push.release_at },
plan: Plan::Nothing,
note_elided: content.note_elided,
clamped: None,
entered_backpressure: push.entered_backpressure,
})
}
Action::Truncate { bytes, code } => {
admit_kind(site, ActionKind::Truncate, unit, None)?;
check_error_code(code).map_err(|r| Refused::new(ActionKind::Truncate, r))?;
let raw = unit.target.raw().unwrap_or_default();
let prefix = raw.slice(..bytes.min(raw.len()));
let forwarded = prefix.len();
let push = push_unit(
engine,
report,
Pending::terminal(Terminal::Truncate { prefix, code }),
None,
);
Ok(AppliedPlan {
action: ActionKind::Truncate,
effect: Effect::Truncated {
forwarded,
code,
code_defined: stream_reset_code_defined(unit.draft),
},
plan: Plan::Terminal,
note_elided: false,
clamped: None,
entered_backpressure: push.entered_backpressure,
})
}
Action::ResetStream { code } => {
admit_kind(site, ActionKind::ResetStream, unit, None)?;
check_error_code(code).map_err(|r| Refused::new(ActionKind::ResetStream, r))?;
let push = push_unit(engine, report, Pending::terminal(Terminal::Reset { code }), None);
Ok(AppliedPlan {
action: ActionKind::ResetStream,
effect: Effect::StreamReset {
code,
code_defined: stream_reset_code_defined(unit.draft),
},
plan: Plan::Terminal,
note_elided: false,
clamped: None,
entered_backpressure: push.entered_backpressure,
})
}
Action::CloseSession { code, reason } => {
admit_kind(site, ActionKind::CloseSession, unit, None)?;
if !engine.closer.request(code, reason.clone()) {
return Err(Refused::new(ActionKind::CloseSession, Refusal::SessionAlreadyClosing));
}
Ok(AppliedPlan::simple(
ActionKind::CloseSession,
Effect::SessionClosing { code },
Plan::CloseSession { code, reason },
))
}
content_action => {
let content = prepare_content(unit, &content_action, report)?;
let (plan, entered_backpressure) = commit_now(&content.payload, engine, report);
Ok(AppliedPlan {
action: content.kind,
effect: content.effect,
plan,
note_elided: content.note_elided,
clamped: None,
entered_backpressure,
})
}
}
}
impl Content {
fn deferred(&self) -> Deferred {
Deferred { action: self.kind, effect: self.effect.clone() }
}
}
fn prepare_content(
unit: &Unit<'_>,
action: &Action,
report: &Reporter<'_>,
) -> Result<Content, Refused> {
let site = unit.target.site();
match action {
Action::Pass => {
admit_kind(site, ActionKind::Pass, unit, None)?;
Ok(Content {
kind: ActionKind::Pass,
payload: match unit.target.raw() {
Some(raw) => Payload::Write(raw),
None => Payload::Absent,
},
effect: Effect::ForwardedVerbatim,
note_elided: false,
})
}
Action::Replace(replacement) => {
admit_kind(site, ActionKind::Replace, unit, None)?;
Ok(Content {
kind: ActionKind::Replace,
payload: Payload::Write(replacement.clone()),
effect: Effect::Replaced { bytes: replacement.len() },
note_elided: false,
})
}
Action::ReplacePayload(replacement) => {
let replacement_len = u64::try_from(replacement.len()).ok();
admit_kind(site, ActionKind::ReplacePayload, unit, replacement_len)?;
let raw = unit.target.raw().unwrap_or_default();
let offset = unit.target.payload_offset(unit.draft).unwrap_or(raw.len());
let mut spliced = Vec::with_capacity(offset + replacement.len());
spliced.extend_from_slice(&raw[..offset]);
spliced.extend_from_slice(replacement);
let spliced = Bytes::from(spliced);
Ok(Content {
kind: ActionKind::ReplacePayload,
payload: Payload::Write(spliced.clone()),
effect: Effect::Replaced { bytes: spliced.len() },
note_elided: false,
})
}
Action::Drop(DropMode::Elide) => {
admit_kind(site, ActionKind::DropElide, unit, None)?;
let at_object_site = matches!(unit.target, Target::Object { .. });
let effect = if at_object_site {
report.counters.note_object_elided();
Effect::Elided { renumbered_successor: elide_renumbers_successor(unit) }
} else {
Effect::Dropped
};
Ok(Content {
kind: ActionKind::DropElide,
payload: Payload::Elide,
effect,
note_elided: at_object_site,
})
}
Action::Delay { .. } | Action::Hold { .. } => {
Err(Refused::new(kind_of(action), NESTED_MODIFIER))
}
Action::Truncate { .. } | Action::ResetStream { .. } => {
Err(Refused::new(kind_of(action), WRAPPED_TERMINAL))
}
Action::CloseSession { .. } => Err(Refused::new(kind_of(action), WRAPPED_CLOSE)),
}
}
fn admit_kind(
site: Site,
kind: ActionKind,
unit: &Unit<'_>,
replacement_len: Option<u64>,
) -> Result<(), Refused> {
let cx = unit.target.cap_ctx(unit.draft, replacement_len);
admit(classify(site, kind, &cx)).map_err(|refusal| Refused::new(kind, refusal))
}
fn admit(support: Support) -> Result<(), Refusal> {
match support {
Support::Yes => Ok(()),
Support::No(refusal) => Err(refusal),
Support::Conditional(precondition) => admit_conditional(precondition),
Support::NotAttemptable { refusal, .. } | Support::Unreachable { refusal, .. } => {
Err(refusal)
}
}
}
fn admit_conditional(precondition: Precondition) -> Result<(), Refusal> {
match precondition {
Precondition::WithinMaxDatagramSize => Ok(()),
Precondition::ReplacementLengthEqualsPayload
| Precondition::NotFirstObjectOfImplicitSubgroup
| Precondition::NotAStatusObject
| Precondition::DatagramPayloadDelimited => {
debug_assert!(
false,
"exec supplies every per-unit fact; {precondition:?} means a CapCtx \
was built outside Target::cap_ctx",
);
Ok(())
}
}
}
fn check_composition(inner: &Action) -> Result<(), Refused> {
let refusal = match inner {
Action::Pass | Action::Replace(_) | Action::ReplacePayload(_) | Action::Drop(_) => {
return Ok(())
}
Action::Delay { .. } | Action::Hold { .. } => NESTED_MODIFIER,
Action::Truncate { .. } | Action::ResetStream { .. } => WRAPPED_TERMINAL,
Action::CloseSession { .. } => WRAPPED_CLOSE,
};
Err(Refused::new(kind_of(inner), refusal))
}
fn check_error_code(code: u64) -> Result<(), Refusal> {
if code > MAX_APPLICATION_ERROR_CODE {
Err(Refusal::ErrorCodeOutOfRange { code })
} else {
Ok(())
}
}
const NESTED_MODIFIER: Refusal =
Refusal::WrongComposition { detail: "Delay or Hold wrapping another Delay or Hold" };
const WRAPPED_TERMINAL: Refusal =
Refusal::WrongComposition { detail: "Delay or Hold wrapping Truncate or ResetStream" };
const WRAPPED_CLOSE: Refusal =
Refusal::WrongComposition { detail: "Delay or Hold wrapping CloseSession" };
fn commit_now(payload: &Payload, engine: &mut Engine<'_>, report: &Reporter<'_>) -> (Plan, bool) {
if matches!(payload, Payload::Absent) || !engine.queue_is_busy() {
return (
match payload {
Payload::Write(raw) => Plan::WriteNow(raw.clone()),
Payload::Elide | Payload::Absent => Plan::Nothing,
},
false,
);
}
let push = push_unit(engine, report, pending_for(payload, Instant::now()), None);
(Plan::Nothing, push.entered_backpressure)
}
fn forward_unchanged(
unit: &Unit<'_>,
engine: &mut Engine<'_>,
report: &Reporter<'_>,
) -> (Plan, bool) {
match unit.target.raw() {
Some(raw) => commit_now(&Payload::Write(raw), engine, report),
None => (Plan::Nothing, false),
}
}
fn pending_for(payload: &Payload, release_at: Instant) -> Pending {
match payload {
Payload::Write(raw) => Pending::bytes(raw.clone(), release_at),
Payload::Elide | Payload::Absent => Pending::elided(release_at),
}
}
fn push_unit(
engine: &mut Engine<'_>,
report: &Reporter<'_>,
unit: Pending,
owed: Option<Deferred>,
) -> egress::Push {
let Some(queue) = engine.queue.as_mut() else {
debug_assert!(
false,
"the datagram site has no queue, and Delay/Hold/Truncate/ResetStream \
are refused there — nothing may reach a push",
);
return egress::Push { release_at: Instant::now(), entered_backpressure: false };
};
let push = queue.pending.push(unit);
queue.deferred.push(owed);
if push.entered_backpressure {
if let Some(stream_id) = report.stream_id {
report.impairment(ImpairmentKind::EgressQueueFull { stream_id });
}
}
push
}
pub(crate) fn enqueue_unshown(
pending: &mut PendingQueue,
deferred: &mut DeferredEffects,
raw: Bytes,
report: &Reporter<'_>,
) {
let push = pending.push(Pending::bytes(raw, Instant::now()));
deferred.push(None);
if push.entered_backpressure {
if let Some(stream_id) = report.stream_id {
report.impairment(ImpairmentKind::EgressQueueFull { stream_id });
}
}
}
fn queue_config(engine: &Engine<'_>) -> crate::action::EgressConfig {
const FALLBACK: crate::action::EgressConfig = crate::action::EgressConfig {
max_pending_bytes: 1024 * 1024,
max_hold: std::time::Duration::from_secs(30),
drain_timeout: std::time::Duration::from_millis(100),
};
match engine.queue.as_ref() {
Some(queue) => *queue.pending.config(),
None => FALLBACK,
}
}
const fn stream_reset_code_defined(draft: DraftVersion) -> bool {
!matches!(
draft,
DraftVersion::Draft07
| DraftVersion::Draft08
| DraftVersion::Draft09
| DraftVersion::Draft10
)
}
fn elide_renumbers_successor(unit: &Unit<'_>) -> bool {
let Target::Object { meta, .. } = &unit.target else {
return false;
};
match meta.stream_kind {
DataStreamType::Subgroup => matches!(
unit.draft,
DraftVersion::Draft14
| DraftVersion::Draft15
| DraftVersion::Draft16
| DraftVersion::Draft17
| DraftVersion::Draft18
| DraftVersion::Draft19
),
DataStreamType::Fetch => matches!(
unit.draft,
DraftVersion::Draft15
| DraftVersion::Draft16
| DraftVersion::Draft17
| DraftVersion::Draft18
| DraftVersion::Draft19
),
}
}
const fn kind_of(action: &Action) -> ActionKind {
match action {
Action::Pass => ActionKind::Pass,
Action::Replace(_) => ActionKind::Replace,
Action::ReplacePayload(_) => ActionKind::ReplacePayload,
Action::Delay { .. } => ActionKind::Delay,
Action::Hold { .. } => ActionKind::Hold,
Action::Drop(DropMode::Elide) => ActionKind::DropElide,
Action::Truncate { .. } => ActionKind::Truncate,
Action::ResetStream { .. } => ActionKind::ResetStream,
Action::CloseSession { .. } => ActionKind::CloseSession,
}
}
#[cfg(test)]
mod tests {
use std::sync::{Arc, Mutex};
use std::time::Duration;
use super::*;
use crate::action::{EgressConfig, Gate};
use crate::egress::Item;
use crate::types::Leg;
use tokio_util::sync::CancellationToken;
#[derive(Default)]
struct Recording {
events: Mutex<Vec<ProxyEvent>>,
}
impl ProxyObserver for Recording {
fn on_event(&self, event: &ProxyEvent) {
self.events.lock().unwrap().push(event.clone());
}
}
impl Recording {
fn events(&self) -> Vec<ProxyEvent> {
self.events.lock().unwrap().clone()
}
fn applied(&self) -> Vec<(Site, ActionKind, Effect)> {
self.events()
.into_iter()
.filter_map(|e| match e {
ProxyEvent::ActionApplied { site, action, effect, .. } => {
Some((site, action, effect))
}
_ => None,
})
.collect()
}
fn refused(&self) -> Vec<(Site, ActionKind, Refusal)> {
self.events()
.into_iter()
.filter_map(|e| match e {
ProxyEvent::ActionRefused { site, action, refusal, .. } => {
Some((site, action, refusal))
}
_ => None,
})
.collect()
}
fn impairments(&self) -> Vec<ImpairmentKind> {
self.events()
.into_iter()
.filter_map(|e| match e {
ProxyEvent::Impairment { kind, .. } => Some(kind),
_ => None,
})
.collect()
}
fn attributed_impairments(&self) -> Vec<(Option<Leg>, ImpairmentKind)> {
self.events()
.into_iter()
.filter_map(|e| match e {
ProxyEvent::Impairment { leg, kind, .. } => Some((leg, kind)),
_ => None,
})
.collect()
}
}
struct Harness {
observer: Arc<Recording>,
counters: Arc<Recorder>,
pending: PendingQueue,
deferred: DeferredEffects,
closer: SessionCloser,
cancel: CancellationToken,
side: ProxySide,
}
impl Harness {
fn new() -> Self {
Self::with_config(EgressConfig::default())
}
fn with_config(config: EgressConfig) -> Self {
let counters = Arc::new(Recorder::new());
let cancel = CancellationToken::new();
Self {
observer: Arc::new(Recording::default()),
pending: PendingQueue::new(config, counters.clone()),
deferred: DeferredEffects::new(),
closer: SessionCloser::new(cancel.clone()),
counters,
cancel,
side: ProxySide::ClientToProxy,
}
}
fn reading_from(mut self, side: ProxySide) -> Self {
self.side = side;
self
}
fn report(&self) -> Reporter<'_> {
Reporter::new(
self.observer.as_ref(),
true,
self.counters.as_ref(),
SessionId(1),
self.side,
Some(4),
)
}
fn datagram_report(&self) -> Reporter<'_> {
Reporter::new(
self.observer.as_ref(),
true,
self.counters.as_ref(),
SessionId(1),
self.side,
None,
)
}
fn datagram_engine(&self) -> Engine<'_> {
Engine { queue: None, closer: &self.closer }
}
fn run_unwatched(&mut self, unit: &Unit<'_>, action: Action) -> Outcome {
let report = Reporter::new(
self.observer.as_ref(),
false,
self.counters.as_ref(),
SessionId(1),
self.side,
Some(4),
);
let mut engine = Engine {
queue: Some(Queue { pending: &mut self.pending, deferred: &mut self.deferred }),
closer: &self.closer,
};
execute(unit, action, &mut engine, &report)
}
fn run(&mut self, unit: &Unit<'_>, action: Action) -> Outcome {
let report = Reporter::new(
self.observer.as_ref(),
true,
self.counters.as_ref(),
SessionId(1),
self.side,
Some(4),
);
let mut engine = Engine {
queue: Some(Queue { pending: &mut self.pending, deferred: &mut self.deferred }),
closer: &self.closer,
};
execute(unit, action, &mut engine, &report)
}
}
fn meta(draft: DraftVersion) -> ObjectMeta {
ObjectMeta {
draft,
stream_kind: DataStreamType::Subgroup,
track_alias: Some(7),
group_id: 1,
subgroup_id: Some(0),
object_id: 3,
publisher_priority: Some(128),
index_in_stream: 3,
payload_len: 4,
status: None,
end_of_range: None,
}
}
fn object_bytes() -> Bytes {
Bytes::from_static(&[0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, b'p', b'a', b'y', b'l'])
}
fn object_unit<'a>(m: &'a ObjectMeta, at: Instant) -> Unit<'a> {
Unit {
target: Target::Object { meta: m, subgroup_id_mode: None, raw: object_bytes() },
draft: m.draft,
arrived_at: at,
}
}
fn control_unit<'a>(draft: DraftVersion, at: Instant) -> Unit<'a> {
Unit {
target: Target::Control { raw: Bytes::from_static(b"control-frame") },
draft,
arrived_at: at,
}
}
fn datagram_unit<'a>(draft: DraftVersion, header_len: Option<usize>) -> Unit<'a> {
Unit {
target: Target::Datagram {
raw: Bytes::from_static(&[0x01, 0x02, 0x03, b'p', b'a', b'y', b'l']),
header_len,
is_status: false,
},
draft,
arrived_at: Instant::now(),
}
}
fn stream_end_unit<'a>(draft: DraftVersion, is_control_stream: bool) -> Unit<'a> {
Unit { target: Target::StreamEnd { is_control_stream }, draft, arrived_at: Instant::now() }
}
const ALL_DRAFTS: [DraftVersion; 13] = [
DraftVersion::Draft07,
DraftVersion::Draft08,
DraftVersion::Draft09,
DraftVersion::Draft10,
DraftVersion::Draft11,
DraftVersion::Draft12,
DraftVersion::Draft13,
DraftVersion::Draft14,
DraftVersion::Draft15,
DraftVersion::Draft16,
DraftVersion::Draft17,
DraftVersion::Draft18,
DraftVersion::Draft19,
];
const COMPILED_DRAFTS: &[DraftVersion] = &[
#[cfg(feature = "draft07")]
DraftVersion::Draft07,
#[cfg(feature = "draft08")]
DraftVersion::Draft08,
#[cfg(feature = "draft09")]
DraftVersion::Draft09,
#[cfg(feature = "draft10")]
DraftVersion::Draft10,
#[cfg(feature = "draft11")]
DraftVersion::Draft11,
#[cfg(feature = "draft12")]
DraftVersion::Draft12,
#[cfg(feature = "draft13")]
DraftVersion::Draft13,
#[cfg(feature = "draft14")]
DraftVersion::Draft14,
#[cfg(feature = "draft15")]
DraftVersion::Draft15,
#[cfg(feature = "draft16")]
DraftVersion::Draft16,
#[cfg(feature = "draft17")]
DraftVersion::Draft17,
#[cfg(feature = "draft18")]
DraftVersion::Draft18,
#[cfg(feature = "draft19")]
DraftVersion::Draft19,
];
#[test]
fn an_applied_action_emits_exactly_one_event() {
for &draft in COMPILED_DRAFTS {
let mut h = Harness::new();
let m = meta(draft);
let out = h.run(&object_unit(&m, Instant::now()), Action::Pass);
assert_eq!(out.plan, Plan::WriteNow(object_bytes()), "draft {draft:?}");
assert_eq!(out.result, Ok(Effect::ForwardedVerbatim), "draft {draft:?}");
assert_eq!(h.observer.events().len(), 1, "draft {draft:?}");
assert_eq!(
h.observer.applied(),
vec![(Site::Object, ActionKind::Pass, Effect::ForwardedVerbatim)],
"draft {draft:?}",
);
assert_eq!(h.counters.snapshot().actions_refused, 0, "draft {draft:?}");
}
}
#[test]
fn a_refusal_emits_exactly_one_event_and_bumps_exactly_one_counter() {
for &draft in COMPILED_DRAFTS {
let mut h = Harness::new();
let m = meta(draft);
let out = h.run(
&object_unit(&m, Instant::now()),
Action::Replace(Bytes::from_static(b"nope")),
);
assert_eq!(h.observer.events().len(), 1, "draft {draft:?}");
assert_eq!(h.counters.snapshot().actions_refused, 1, "draft {draft:?}");
assert_eq!(out.plan, Plan::WriteNow(object_bytes()), "draft {draft:?}");
}
}
#[test]
fn the_counter_moves_even_with_no_observer_attached() {
for &draft in COMPILED_DRAFTS {
let counters = Arc::new(Recorder::new());
let observer = Recording::default();
let cancel = CancellationToken::new();
let closer = SessionCloser::new(cancel);
let mut pending = PendingQueue::new(EgressConfig::default(), counters.clone());
let mut deferred = DeferredEffects::new();
let report = Reporter::new(
&observer,
false, counters.as_ref(),
SessionId(1),
ProxySide::ClientToProxy,
Some(1),
);
let mut engine = Engine {
queue: Some(Queue { pending: &mut pending, deferred: &mut deferred }),
closer: &closer,
};
let m = meta(draft);
let out = execute(
&object_unit(&m, Instant::now()),
Action::Replace(Bytes::from_static(b"x")),
&mut engine,
&report,
);
assert_eq!(
out.result,
Err(Refusal::WrongSite { site: Site::Object, action: ActionKind::ReplaceObject }),
"draft {draft:?}",
);
assert!(observer.events().is_empty(), "events are gated");
assert_eq!(counters.snapshot().actions_refused, 1, "counters are not");
}
}
#[test]
fn exactly_one_decision_event_per_unit_and_refusals_forward_the_original() {
let mut refused_cells = 0usize;
let mut applied_cells = 0usize;
for draft in ALL_DRAFTS {
let m = meta(draft);
let object_site_is_reachable = COMPILED_DRAFTS.contains(&draft);
let actions = || {
vec![
Action::Pass,
Action::Replace(Bytes::from_static(b"xxxx")),
Action::ReplacePayload(Bytes::from_static(b"abcd")),
Action::ReplacePayload(Bytes::from_static(b"toolong")),
Action::Drop(DropMode::Elide),
Action::Truncate { bytes: 2, code: 1 },
Action::ResetStream { code: u64::MAX },
Action::CloseSession { code: 1, reason: Bytes::new() },
Action::Pass.delayed(Duration::from_millis(1)),
Action::Pass.held(Gate::new()),
Action::Drop(DropMode::Elide).delayed(Duration::from_millis(1)),
Action::Drop(DropMode::Elide).held(Gate::new()),
Action::ResetStream { code: 1 }.delayed(Duration::from_millis(1)),
Action::CloseSession { code: 1, reason: Bytes::new() }.held(Gate::new()),
]
};
for action in actions() {
let mut cells: Vec<(Unit<'_>, Option<Bytes>)> = vec![
(
control_unit(draft, Instant::now()),
Some(Bytes::from_static(b"control-frame")),
),
(stream_end_unit(draft, false), None),
(stream_end_unit(draft, true), None),
];
if object_site_is_reachable {
cells.push((object_unit(&m, Instant::now()), Some(object_bytes())));
}
for (unit, original) in cells {
let mut h = Harness::new();
let out = h.run(&unit, action.clone());
let what = format!("{draft:?} / {:?} / {action:?}", unit.target.site());
let decisions = h
.observer
.events()
.iter()
.filter(|e| {
matches!(
e,
ProxyEvent::ActionApplied { .. } | ProxyEvent::ActionRefused { .. }
)
})
.count();
assert_eq!(decisions, 1, "[{what}] one decision event per unit");
match &out.result {
Ok(_) => {
applied_cells += 1;
assert!(h.observer.refused().is_empty(), "[{what}]");
assert_eq!(h.counters.snapshot().actions_refused, 0, "[{what}]");
}
Err(_) => {
refused_cells += 1;
assert!(
h.observer.applied().is_empty(),
"[{what}] a refused unit reports no ActionApplied",
);
assert_eq!(h.counters.snapshot().actions_refused, 1, "[{what}]");
let want = match &original {
Some(raw) => Plan::WriteNow(raw.clone()),
None => Plan::Nothing,
};
assert_eq!(
out.plan, want,
"[{what}] a refused unit is forwarded unchanged",
);
assert!(!out.note_elided, "[{what}]");
assert!(out.clamped.is_none(), "[{what}]");
assert!(
h.pending.is_empty(),
"[{what}] a refusal on an empty queue writes inline",
);
assert_eq!(h.counters.snapshot().egress_items_queued, 0, "[{what}]");
}
}
}
let h = Harness::new();
let unit = datagram_unit(draft, Some(3));
let raw = Bytes::from_static(&[0x01, 0x02, 0x03, b'p', b'a', b'y', b'l']);
let report = h.datagram_report();
let mut engine = h.datagram_engine();
let out = execute(&unit, action.clone(), &mut engine, &report);
let what = format!("{draft:?} / Datagram / {action:?}");
let decisions = h
.observer
.events()
.iter()
.filter(|e| {
matches!(
e,
ProxyEvent::ActionApplied { .. } | ProxyEvent::ActionRefused { .. }
)
})
.count();
assert_eq!(decisions, 1, "[{what}] one decision event per unit");
if out.result.is_err() {
refused_cells += 1;
assert!(h.observer.applied().is_empty(), "[{what}]");
assert_eq!(out.plan, Plan::WriteNow(raw), "[{what}] forwarded unchanged");
} else {
applied_cells += 1;
assert!(h.observer.refused().is_empty(), "[{what}]");
}
}
}
assert!(refused_cells > 0 && applied_cells > 0, "the sweep must reach both verdicts");
}
#[test]
fn a_refused_unit_queues_behind_a_delayed_one_rather_than_overtaking_it() {
const DELAY: Duration = Duration::from_millis(60);
for &draft in COMPILED_DRAFTS {
let mut h = Harness::new();
let m = meta(draft);
let at = Instant::now();
h.run(&object_unit(&m, at), Action::Pass.delayed(DELAY));
let out = h.run(&object_unit(&m, at), Action::Replace(Bytes::from_static(b"no")));
assert!(out.result.is_err(), "draft {draft:?}");
assert_eq!(out.plan, Plan::Nothing, "not written inline: the queue is busy");
assert_eq!(h.pending.len(), 2, "draft {draft:?}");
assert_eq!(h.deferred.len(), 2, "one ledger entry per pushed unit, refusals included");
assert!(
h.pending.pop_next_due(Instant::now()).is_none(),
"the delayed head still blocks the refused unit behind it",
);
let far = Instant::now() + Duration::from_secs(3600);
let head = h.pending.pop_next_due(far).expect("the delayed Pass");
let behind = h.pending.pop_next_due(far).expect("the refused unit, behind it");
assert_eq!(*head.item(), Item::Write(object_bytes()));
assert_eq!(
*behind.item(),
Item::Write(object_bytes()),
"the refused unit's own bytes, not the replacement",
);
assert!(behind.expected_at() >= head.expected_at(), "and not expected ahead of it");
assert_eq!(
h.deferred.take_all(),
vec![Deferred { action: ActionKind::Pass, effect: Effect::ForwardedVerbatim }],
"the refused unit owes no release event",
);
}
}
#[test]
fn delay_then_replace_reports_queued_now_and_the_inner_effect_at_release() {
let Some(draft) = a_compiled_draft() else { return };
let mut h = Harness::new();
let at = Instant::now();
let out = h.run(
&control_unit(draft, at),
Action::Replace(Bytes::from_static(b"1234")).delayed(Duration::from_millis(50)),
);
assert_eq!(out.plan, Plan::Nothing);
let Ok(Effect::Queued { release_at }) = out.result else {
panic!("expected Queued, got {:?}", out.result)
};
assert!(release_at >= at + Duration::from_millis(50));
assert_eq!(h.observer.applied().len(), 1);
assert_eq!(h.observer.applied()[0].1, ActionKind::Delay);
assert_eq!(h.deferred.len(), 1);
assert_eq!(h.pending.len(), 1);
let owed = h.deferred.pop().expect("the delay owes a release event");
assert_eq!(
owed,
Deferred { action: ActionKind::Replace, effect: Effect::Replaced { bytes: 4 } },
);
h.report().applied_deferred(Site::Control, owed);
let applied = h.observer.applied();
assert_eq!(applied.len(), 2, "a deferred action reports twice");
assert_eq!(applied[1].1, ActionKind::Replace);
assert_eq!(applied[1].2, Effect::Replaced { bytes: 4 });
}
#[test]
fn a_delay_wrapping_a_refused_inner_action_is_refused_with_the_inners_reason() {
for &draft in COMPILED_DRAFTS {
let mut h = Harness::new();
let m = meta(draft);
let out = h.run(
&object_unit(&m, Instant::now()),
Action::Replace(Bytes::from_static(b"1234")).delayed(Duration::from_millis(50)),
);
assert_eq!(
out.result,
Err(Refusal::WrongSite { site: Site::Object, action: ActionKind::ReplaceObject }),
"draft {draft:?}",
);
assert_eq!(
h.observer.refused()[0].1,
ActionKind::Replace,
"the event names the inner action, which is the informative one",
);
assert!(h.pending.is_empty(), "the site check happens before the queue");
}
}
#[test]
fn a_direct_action_queued_only_for_ordering_owes_nothing_at_release() {
for &draft in COMPILED_DRAFTS {
let mut h = Harness::new();
let m = meta(draft);
let at = Instant::now();
h.run(&object_unit(&m, at), Action::Pass.delayed(Duration::from_millis(80)));
let out = h.run(&object_unit(&m, at), Action::Pass);
assert_eq!(out.plan, Plan::Nothing, "a busy queue swallows the write");
assert_eq!(out.result, Ok(Effect::ForwardedVerbatim), "draft {draft:?}");
assert_eq!(h.pending.len(), 2, "draft {draft:?}");
assert_eq!(h.deferred.len(), 2, "one ledger entry per pushed unit");
assert!(h.deferred.pop().is_some(), "the delayed unit owes one");
assert!(h.deferred.pop().is_none(), "the ordering-only unit owes none");
assert_eq!(h.observer.applied().len(), 2, "two decisions, two events");
}
}
#[test]
fn hold_reports_queued_at_the_ceiling_and_owes_the_inner_effect() {
for &draft in COMPILED_DRAFTS {
let mut h = Harness::new();
let m = meta(draft);
let at = Instant::now();
let gate = Gate::new();
let out = h.run(&object_unit(&m, at), Action::Pass.held(gate.clone()));
let Ok(Effect::Queued { release_at }) = out.result else {
panic!("[{draft:?}] expected Queued, got {:?}", out.result)
};
assert!(release_at >= at + Duration::from_secs(30) - Duration::from_millis(1));
assert_eq!(out.clamped, None, "a Hold has no requested duration to clamp");
assert_eq!(
h.deferred.pop(),
Some(Deferred { action: ActionKind::Pass, effect: Effect::ForwardedVerbatim }),
);
assert!(!gate.is_released());
}
}
#[test]
fn a_delay_wrapping_a_terminal_is_refused_before_it_is_queued() {
for &draft in COMPILED_DRAFTS {
let mut h = Harness::new();
let m = meta(draft);
let out = h.run(
&object_unit(&m, Instant::now()),
Action::ResetStream { code: 2 }.delayed(Duration::from_millis(10)),
);
assert_eq!(out.result, Err(WRAPPED_TERMINAL), "draft {draft:?}");
assert_eq!(
h.observer.refused(),
vec![(Site::Object, ActionKind::ResetStream, WRAPPED_TERMINAL)],
);
assert!(h.pending.is_empty(), "nothing reached the deque");
assert!(h.deferred.is_empty());
assert_eq!(h.counters.snapshot().egress_items_queued, 0);
assert_eq!(out.plan, Plan::WriteNow(object_bytes()));
}
}
#[test]
fn every_illegal_composition_names_which_one_it_was() {
let cases = [
(Action::Pass.delayed(Duration::from_millis(1)), NESTED_MODIFIER),
(Action::Pass.held(Gate::new()), NESTED_MODIFIER),
(Action::Truncate { bytes: 1, code: 0 }, WRAPPED_TERMINAL),
(Action::ResetStream { code: 0 }, WRAPPED_TERMINAL),
(Action::CloseSession { code: 1, reason: Bytes::new() }, WRAPPED_CLOSE),
];
for &draft in COMPILED_DRAFTS {
for (inner, expected) in cases.clone() {
let mut h = Harness::new();
let m = meta(draft);
let out = h.run(
&object_unit(&m, Instant::now()),
inner.clone().delayed(Duration::from_millis(1)),
);
assert_eq!(out.result, Err(expected.clone()), "draft {draft:?} / inner {inner:?}");
assert!(h.pending.is_empty());
}
}
}
#[test]
fn the_composition_rule_admits_exactly_the_four_content_actions() {
for inner in [
Action::Pass,
Action::Replace(Bytes::from_static(b"xxxx")),
Action::ReplacePayload(Bytes::from_static(b"abcd")),
Action::Drop(DropMode::Elide),
] {
assert!(
check_composition(&inner).is_ok(),
"{inner:?} is a content action and must be legal inside Delay/Hold",
);
}
for (inner, expected) in [
(Action::Pass.delayed(Duration::ZERO), NESTED_MODIFIER),
(Action::Pass.held(Gate::new()), NESTED_MODIFIER),
(Action::Truncate { bytes: 1, code: 0 }, WRAPPED_TERMINAL),
(Action::ResetStream { code: 0 }, WRAPPED_TERMINAL),
(Action::CloseSession { code: 0, reason: Bytes::new() }, WRAPPED_CLOSE),
] {
let Err(Refused { action, refusal }) = check_composition(&inner) else {
panic!("{inner:?} is not a content action and must be refused")
};
assert_eq!(refusal, expected, "inner {inner:?}");
assert_eq!(action, kind_of(&inner), "the event names what was wrapped");
}
}
#[test]
fn a_delayed_drop_is_admitted_and_blocks_the_stream_behind_it() {
const DELAY: Duration = Duration::from_millis(80);
for &draft in COMPILED_DRAFTS {
let mut h = Harness::new();
let m = meta(draft);
let at = Instant::now();
let renumbered = elide_renumbers_successor(&object_unit(&m, at));
let out = h.run(&object_unit(&m, at), Action::Drop(DropMode::Elide).delayed(DELAY));
let Ok(Effect::Queued { release_at }) = out.result else {
panic!("[{draft:?}] a delayed Drop is admitted, not refused; got {:?}", out.result)
};
assert!(release_at >= at + DELAY);
assert!(out.note_elided, "the framer's cursor still moves at the decision");
assert_eq!(h.counters.snapshot().actions_refused, 0);
assert!(h.observer.refused().is_empty(), "nothing was refused");
assert_eq!(
h.observer.applied(),
vec![(Site::Object, ActionKind::Delay, Effect::Queued { release_at })],
"step 1 names the modifier",
);
let behind = h.run(&object_unit(&m, at), Action::Pass);
assert_eq!(behind.plan, Plan::Nothing, "the drop's slot is still holding the queue");
assert_eq!(behind.result, Ok(Effect::ForwardedVerbatim));
assert_eq!(h.deferred.len(), h.pending.len());
assert_eq!(
h.deferred.take_all(),
vec![Deferred {
action: ActionKind::DropElide,
effect: Effect::Elided { renumbered_successor: renumbered },
}],
"step 2 names the inner action; the ordering-only Pass owes nothing",
);
assert!(
h.pending.pop_next_due(Instant::now()).is_none(),
"a delayed drop head-of-line-blocks the undelayed unit behind it",
);
let far = Instant::now() + Duration::from_secs(3600);
let dropped = h.pending.pop_next_due(far).expect("the drop holds a slot");
let passed = h.pending.pop_next_due(far).expect("the Pass is queued behind it");
assert_eq!(*dropped.item(), Item::Elided, "the drop writes nothing...");
assert!(dropped.due_at() >= at + DELAY, "...and not until its delay is up");
assert_eq!(*passed.item(), Item::Write(object_bytes()));
assert!(
passed.expected_at() >= at + DELAY,
"the queue expects to write the unit behind the drop no earlier than the drop: \
{:?} is earlier than {:?}",
passed.expected_at(),
at + DELAY,
);
}
}
#[test]
fn a_held_drop_is_admitted_and_stays_undue_until_its_gate_is_released() {
for &draft in COMPILED_DRAFTS {
let mut h = Harness::new();
let m = meta(draft);
let at = Instant::now();
let renumbered = elide_renumbers_successor(&object_unit(&m, at));
let gate = Gate::new();
let out = h.run(&object_unit(&m, at), Action::Drop(DropMode::Elide).held(gate.clone()));
assert!(
matches!(out.result, Ok(Effect::Queued { .. })),
"[{draft:?}] a held Drop is admitted, not refused; got {:?}",
out.result,
);
assert_eq!(h.counters.snapshot().actions_refused, 0);
assert!(
h.pending.pop_next_due(Instant::now()).is_none(),
"nothing is due while the gate holds",
);
gate.release();
let released =
h.pending.pop_next_due(Instant::now()).expect("a released gate makes it due");
assert_eq!(*released.item(), Item::Elided);
assert_eq!(
h.deferred.take_all(),
vec![Deferred {
action: ActionKind::DropElide,
effect: Effect::Elided { renumbered_successor: renumbered },
}],
);
}
}
#[test]
fn the_site_verdict_wins_over_the_composition_verdict() {
let h = Harness::new();
let unit = datagram_unit(DraftVersion::Draft11, Some(3));
let report = h.datagram_report();
let mut engine = h.datagram_engine();
let out = execute(
&unit,
Action::ResetStream { code: 1 }.delayed(Duration::from_millis(5)),
&mut engine,
&report,
);
assert_eq!(
out.result,
Err(Refusal::WrongSite { site: Site::Datagram, action: ActionKind::Delay }),
);
}
#[test]
fn replace_at_the_object_site_propagates_classifys_replaceobject_refusal() {
for &draft in COMPILED_DRAFTS {
let mut h = Harness::new();
let m = meta(draft);
let out =
h.run(&object_unit(&m, Instant::now()), Action::Replace(Bytes::from_static(b"x")));
assert_eq!(
out.result,
Err(Refusal::WrongSite { site: Site::Object, action: ActionKind::ReplaceObject }),
"draft {draft:?}",
);
assert_eq!(h.observer.refused()[0].1, ActionKind::Replace);
let published = crate::capability::Capabilities::for_draft(draft)
.supports(Site::Object, ActionKind::ReplaceObject);
assert_eq!(published, Support::No(out.result.unwrap_err()), "draft {draft:?}");
}
}
#[test]
fn every_refusal_this_module_emits_is_classifys_or_one_of_its_own_three() {
let mut seen: Vec<Refusal> = Vec::new();
for draft in ALL_DRAFTS {
let m = meta(draft);
let actions = || {
vec![
Action::Pass,
Action::Replace(Bytes::from_static(b"xxxx")),
Action::ReplacePayload(Bytes::from_static(b"abcd")),
Action::ReplacePayload(Bytes::from_static(b"toolong")),
Action::Drop(DropMode::Elide),
Action::Truncate { bytes: 2, code: 1 },
Action::Truncate { bytes: 2, code: u64::MAX },
Action::ResetStream { code: 1 },
Action::ResetStream { code: u64::MAX },
Action::CloseSession { code: 1, reason: Bytes::new() },
Action::Pass.delayed(Duration::from_millis(1)),
Action::Pass.held(Gate::new()),
Action::ResetStream { code: 1 }.delayed(Duration::from_millis(1)),
]
};
for action in actions() {
let mut units = vec![
control_unit(draft, Instant::now()),
stream_end_unit(draft, false),
stream_end_unit(draft, true),
];
if COMPILED_DRAFTS.contains(&draft) {
units.push(object_unit(&m, Instant::now()));
}
for unit in units {
let mut h = Harness::new();
let out = h.run(&unit, action.clone());
if let Err(refusal) = out.result {
assert_eq!(h.counters.snapshot().actions_refused, 1);
seen.push(refusal);
} else {
assert_eq!(h.counters.snapshot().actions_refused, 0);
}
}
let h = Harness::new();
let unit = datagram_unit(draft, Some(3));
let report = h.datagram_report();
let mut engine = h.datagram_engine();
let out = execute(&unit, action.clone(), &mut engine, &report);
if let Err(refusal) = out.result {
seen.push(refusal);
}
}
}
assert!(!seen.is_empty(), "the sweep must actually refuse things");
for refusal in &seen {
assert!(
!matches!(refusal, Refusal::StreamNotFramed { .. }),
"table-only refusal {refusal:?} escaped into an ActionRefused",
);
}
assert!(seen.iter().any(|r| matches!(r, Refusal::WrongComposition { .. })));
assert!(seen.iter().any(|r| matches!(r, Refusal::ErrorCodeOutOfRange { .. })));
}
#[test]
fn a_second_close_is_refused_as_session_already_closing() {
for &draft in COMPILED_DRAFTS {
let mut h = Harness::new();
let m = meta(draft);
let first = h.run(
&object_unit(&m, Instant::now()),
Action::CloseSession { code: 3, reason: Bytes::from_static(b"bye") },
);
assert_eq!(first.result, Ok(Effect::SessionClosing { code: 3 }), "draft {draft:?}");
assert_eq!(
first.plan,
Plan::CloseSession { code: 3, reason: Bytes::from_static(b"bye") },
);
let second = h.run(
&object_unit(&m, Instant::now()),
Action::CloseSession { code: 1, reason: Bytes::new() },
);
assert_eq!(second.result, Err(Refusal::SessionAlreadyClosing), "draft {draft:?}");
assert_eq!(
h.closer.close_args(),
(3, Bytes::from_static(b"bye")),
"the first request wins; the second does not overwrite the reason",
);
assert!(h.cancel.is_cancelled(), "SessionCloser::request cancels as it records");
assert_eq!(second.plan, Plan::WriteNow(object_bytes()));
}
}
#[test]
fn an_out_of_range_code_is_refused_before_anything_is_queued() {
for &draft in COMPILED_DRAFTS {
for action in [
Action::ResetStream { code: 1 << 62 },
Action::Truncate { bytes: 1, code: u64::MAX },
] {
let mut h = Harness::new();
let m = meta(draft);
let out = h.run(&object_unit(&m, Instant::now()), action.clone());
let Err(Refusal::ErrorCodeOutOfRange { code }) = out.result else {
panic!(
"[{draft:?}] expected ErrorCodeOutOfRange for {action:?}, got {:?}",
out.result,
)
};
assert!(code > MAX_APPLICATION_ERROR_CODE);
assert!(h.pending.is_empty(), "nothing was queued");
}
}
let h = Harness::new();
let out = execute_stream(
StreamSite::Open,
DraftVersion::Draft11,
StreamAction::Reject { code: u64::MAX },
&h.report(),
);
assert_eq!(out.result, Err(Refusal::ErrorCodeOutOfRange { code: u64::MAX }));
assert_eq!(out.plan, Plan::Nothing);
}
#[test]
fn replace_payload_splices_at_the_trailing_field() {
for &draft in COMPILED_DRAFTS {
let mut h = Harness::new();
let m = meta(draft);
let out = h.run(
&object_unit(&m, Instant::now()),
Action::ReplacePayload(Bytes::from_static(b"WXYZ")),
);
assert_eq!(out.result, Ok(Effect::Replaced { bytes: 10 }), "draft {draft:?}");
let Plan::WriteNow(bytes) = out.plan else {
panic!("[{draft:?}] expected an inline write")
};
assert_eq!(&bytes[..6], &object_bytes()[..6], "framing is untouched");
assert_eq!(&bytes[6..], b"WXYZ");
}
}
#[test]
fn replace_payload_with_a_different_length_is_refused_with_the_lengths() {
for &draft in COMPILED_DRAFTS {
let mut h = Harness::new();
let m = meta(draft);
let out = h.run(
&object_unit(&m, Instant::now()),
Action::ReplacePayload(Bytes::from_static(b"WXYZ!")),
);
assert_eq!(
out.result,
Err(Refusal::LengthChanged { from: 4, to: 5 }),
"draft {draft:?}",
);
assert_eq!(out.plan, Plan::WriteNow(object_bytes()), "forwarded unchanged");
}
}
#[cfg(feature = "draft11")]
#[test]
fn eliding_the_first_object_of_an_implicit_subgroup_is_refused() {
let mut h = Harness::new();
let mut m = meta(DraftVersion::Draft11);
m.index_in_stream = 0;
m.subgroup_id = None;
let out = h.run(&object_unit(&m, Instant::now()), Action::Drop(DropMode::Elide));
assert_eq!(out.result, Err(Refusal::WouldRedefineSubgroupId));
assert!(!out.note_elided, "a refused elide must not move the cursor");
assert_eq!(h.counters.snapshot().objects_elided, 0);
}
#[cfg(feature = "draft19")]
#[test]
fn a_reserved_subgroup_id_mode_is_its_own_refusal() {
let mut h = Harness::new();
let mut m = meta(DraftVersion::Draft19);
m.index_in_stream = 0;
m.subgroup_id = None;
let unit = Unit {
target: Target::Object { meta: &m, subgroup_id_mode: Some(3), raw: object_bytes() },
draft: DraftVersion::Draft19,
arrived_at: Instant::now(),
};
let out = h.run(&unit, Action::Drop(DropMode::Elide));
assert_eq!(out.result, Err(Refusal::ReservedHeaderMode { mode: 3 }));
}
#[test]
fn a_shaper_tail_drop_takes_the_elide_guards_and_reports_only_refusals() {
for &draft in COMPILED_DRAFTS {
let h = Harness::new();
let m = meta(draft);
assert!(
shape_elide(&object_unit(&m, Instant::now()), &h.report()),
"draft {draft:?}: an ordinary object elides"
);
assert_eq!(h.counters.snapshot().objects_elided, 1, "draft {draft:?}");
assert_eq!(h.counters.snapshot().actions_refused, 0, "draft {draft:?}");
assert!(h.observer.applied().is_empty(), "no hook asked, so nothing was applied");
let h = Harness::new();
let mut m = meta(draft);
m.status = Some(3);
m.payload_len = 0;
assert!(
!shape_elide(&object_unit(&m, Instant::now()), &h.report()),
"draft {draft:?}: a status object may not be elided, so the shaper \
must admit the unit instead"
);
assert_eq!(h.counters.snapshot().objects_elided, 0, "draft {draft:?}");
assert_eq!(
h.observer.refused(),
vec![(Site::Object, ActionKind::DropElide, Refusal::WouldDestroyStatusObject)],
"draft {draft:?}: the refusal is reported, once",
);
assert_eq!(h.counters.snapshot().actions_refused, 1, "draft {draft:?}");
}
}
#[test]
fn eliding_a_status_object_is_refused() {
for &draft in COMPILED_DRAFTS {
let mut h = Harness::new();
let mut m = meta(draft);
m.status = Some(3);
m.payload_len = 0;
let out = h.run(&object_unit(&m, Instant::now()), Action::Drop(DropMode::Elide));
assert_eq!(out.result, Err(Refusal::WouldDestroyStatusObject), "draft {draft:?}");
}
}
#[test]
fn an_admitted_elide_writes_nothing_counts_one_and_owes_a_cursor_move() {
for &draft in COMPILED_DRAFTS {
let mut h = Harness::new();
let m = meta(draft);
let at = Instant::now();
let renumbered = elide_renumbers_successor(&object_unit(&m, at));
let out = h.run(&object_unit(&m, at), Action::Drop(DropMode::Elide));
assert_eq!(out.plan, Plan::Nothing, "draft {draft:?}");
assert_eq!(
out.result,
Ok(Effect::Elided { renumbered_successor: renumbered }),
"draft {draft:?}",
);
assert!(out.note_elided);
assert_eq!(h.counters.snapshot().objects_elided, 1);
}
}
#[test]
fn a_deferred_unit_is_counted_and_a_forwarded_one_is_not() {
for &draft in COMPILED_DRAFTS {
let m = meta(draft);
let mut h = Harness::new();
let out = h.run(
&object_unit(&m, Instant::now()),
Action::Delay { by: Duration::from_millis(5), then: Box::new(Action::Pass) },
);
assert!(matches!(out.result, Ok(Effect::Queued { .. })), "draft {draft:?}");
let counted = h.counters.snapshot();
assert_eq!(
counted.units_delayed, 1,
"draft {draft:?}: the engine took the unit off the wire and queued it \
for a later release, and the figure for what it deferred did not move",
);
assert_eq!(counted.actions_refused, 0, "draft {draft:?}");
assert_eq!(
counted.objects_truncated, 0,
"draft {draft:?}: a deferral is not a truncation, and one figure \
answering for both would be indistinguishable from either",
);
let mut h = Harness::new();
h.run(&object_unit(&m, Instant::now()), Action::Pass);
assert_eq!(
h.counters.snapshot().units_delayed,
0,
"draft {draft:?}: a unit that went straight out was never deferred",
);
}
}
#[test]
fn a_deferred_control_frame_moves_the_same_figure() {
for &draft in COMPILED_DRAFTS {
let mut h = Harness::new();
let out = h.run(
&control_unit(draft, Instant::now()),
Action::Delay { by: Duration::from_millis(5), then: Box::new(Action::Pass) },
);
assert!(matches!(out.result, Ok(Effect::Queued { .. })), "draft {draft:?}");
assert_eq!(
h.counters.snapshot().units_delayed,
1,
"draft {draft:?}: a control frame is a unit, and it was deferred",
);
}
}
#[test]
fn a_truncation_counts_when_it_is_applied_and_never_when_it_is_refused() {
for &draft in COMPILED_DRAFTS {
let m = meta(draft);
let mut h = Harness::new();
let out =
h.run(&object_unit(&m, Instant::now()), Action::Truncate { bytes: 3, code: 0x2 });
assert!(matches!(out.result, Ok(Effect::Truncated { .. })), "draft {draft:?}");
assert_eq!(
h.counters.snapshot().objects_truncated,
1,
"draft {draft:?}: the object went out cut short to its first three \
bytes and nothing counted it",
);
let mut h = Harness::new();
let out = h.run(
&object_unit(&m, Instant::now()),
Action::Truncate { bytes: 3, code: MAX_APPLICATION_ERROR_CODE + 1 },
);
assert!(out.result.is_err(), "draft {draft:?}");
let counted = h.counters.snapshot();
assert_eq!(
counted.objects_truncated, 0,
"draft {draft:?}: an out-of-range code refuses the truncation, and \
a refused action cut nothing short",
);
assert_eq!(counted.actions_refused, 1, "draft {draft:?}");
let mut h = Harness::new();
let out = h.run(
&control_unit(draft, Instant::now()),
Action::Truncate { bytes: 3, code: 0x2 },
);
assert_eq!(out.result, Err(Refusal::ControlStreamResetIllegal), "draft {draft:?}");
assert_eq!(
h.counters.snapshot().objects_truncated,
0,
"draft {draft:?}: the object name holds because the control site \
refuses the action outright",
);
}
}
#[test]
fn a_session_with_nobody_watching_still_counts_what_it_applied() {
for &draft in COMPILED_DRAFTS {
let m = meta(draft);
let mut h = Harness::new();
h.run_unwatched(
&object_unit(&m, Instant::now()),
Action::Delay { by: Duration::from_millis(5), then: Box::new(Action::Pass) },
);
let mut h2 = Harness::new();
h2.run_unwatched(
&object_unit(&m, Instant::now()),
Action::Truncate { bytes: 3, code: 0x2 },
);
assert!(
h.observer.events().is_empty() && h2.observer.events().is_empty(),
"draft {draft:?}: nothing was emitted, which is what makes the \
counters below a measurement rather than a restatement",
);
assert_eq!(
h.counters.snapshot().units_delayed,
1,
"draft {draft:?}: a deferral on a session nobody attached to is still \
a deferral, and this figure is the only thing that says so",
);
assert_eq!(
h2.counters.snapshot().objects_truncated,
1,
"draft {draft:?}: and so is a truncation",
);
}
}
#[cfg(feature = "draft14")]
#[test]
fn a_deferred_elide_still_moves_the_cursor_at_the_decision() {
let mut h = Harness::new();
let m = meta(DraftVersion::Draft14);
let out = h.run(
&object_unit(&m, Instant::now()),
Action::Drop(DropMode::Elide).delayed(Duration::from_millis(20)),
);
assert!(
out.note_elided,
"the framer's cursor is positional; note_elided cannot wait for the release",
);
assert_eq!(
h.deferred.pop(),
Some(Deferred {
action: ActionKind::DropElide,
effect: Effect::Elided { renumbered_successor: true },
}),
);
}
#[test]
fn elide_renumbering_names_the_drafts_that_owe_a_fixup() {
for draft in ALL_DRAFTS {
for stream_kind in [DataStreamType::Subgroup, DataStreamType::Fetch] {
let mut m = meta(draft);
m.stream_kind = stream_kind;
let unit = object_unit(&m, Instant::now());
let expected = match stream_kind {
DataStreamType::Subgroup => draft.number() >= 14,
DataStreamType::Fetch => draft.number() >= 15,
};
assert_eq!(
elide_renumbers_successor(&unit),
expected,
"draft {draft:?} / {stream_kind:?}",
);
}
}
assert!(!elide_renumbers_successor(&control_unit(DraftVersion::Draft19, Instant::now())));
}
#[test]
fn truncate_queues_a_prefix_then_a_reset_and_reports_the_prefix_length() {
for &draft in COMPILED_DRAFTS {
let mut h = Harness::new();
let m = meta(draft);
let out =
h.run(&object_unit(&m, Instant::now()), Action::Truncate { bytes: 3, code: 0x2 });
assert_eq!(out.plan, Plan::Terminal, "draft {draft:?}");
assert_eq!(
out.result,
Ok(Effect::Truncated {
forwarded: 3,
code: 0x2,
code_defined: stream_reset_code_defined(draft),
}),
"draft {draft:?}",
);
assert_eq!(h.pending.len(), 1);
assert!(h.pending.head_release().is_some());
}
}
#[test]
fn truncate_past_the_end_of_the_unit_forwards_the_whole_unit() {
for &draft in COMPILED_DRAFTS {
let mut h = Harness::new();
let m = meta(draft);
let out =
h.run(&object_unit(&m, Instant::now()), Action::Truncate { bytes: 9_999, code: 0 });
assert_eq!(
out.result,
Ok(Effect::Truncated {
forwarded: object_bytes().len(),
code: 0,
code_defined: stream_reset_code_defined(draft),
}),
"draft {draft:?}",
);
}
}
#[test]
fn drafts_07_to_10_report_the_reset_code_as_undefined() {
for &draft in COMPILED_DRAFTS {
let expected = !matches!(
draft,
DraftVersion::Draft07
| DraftVersion::Draft08
| DraftVersion::Draft09
| DraftVersion::Draft10
);
let mut h = Harness::new();
let m = meta(draft);
let out = h.run(&object_unit(&m, Instant::now()), Action::ResetStream { code: 5 });
assert_eq!(
out.result,
Ok(Effect::StreamReset { code: 5, code_defined: expected }),
"draft {draft:?}",
);
}
}
fn a_compiled_draft() -> Option<DraftVersion> {
COMPILED_DRAFTS.first().copied()
}
#[test]
fn the_control_site_replaces_the_whole_frame_and_drops_it_whole() {
let Some(draft) = a_compiled_draft() else { return };
let mut h = Harness::new();
let unit = control_unit(draft, Instant::now());
let out = h.run(&unit, Action::Replace(Bytes::from_static(b"other")));
assert_eq!(out.plan, Plan::WriteNow(Bytes::from_static(b"other")));
assert_eq!(out.result, Ok(Effect::Replaced { bytes: 5 }));
let mut h = Harness::new();
let out = h.run(&control_unit(draft, Instant::now()), Action::Drop(DropMode::Elide));
assert_eq!(out.result, Ok(Effect::Dropped), "no object slot to renumber");
assert!(!out.note_elided);
assert_eq!(h.counters.snapshot().objects_elided, 0);
}
#[test]
fn resetting_a_control_stream_is_refused_on_every_draft() {
for &draft in COMPILED_DRAFTS {
for action in [Action::ResetStream { code: 1 }, Action::Truncate { bytes: 1, code: 1 }]
{
let mut h = Harness::new();
let out = h.run(&control_unit(draft, Instant::now()), action.clone());
assert_eq!(
out.result,
Err(Refusal::ControlStreamResetIllegal),
"draft {draft:?} / {action:?}",
);
assert!(h.pending.is_empty());
}
}
}
#[test]
fn drafts_17_to_19_execute_a_control_action_like_every_other_draft() {
for draft in [DraftVersion::Draft17, DraftVersion::Draft18, DraftVersion::Draft19]
.into_iter()
.filter(|d| COMPILED_DRAFTS.contains(d))
{
let mut h = Harness::new();
let out = h.run(
&control_unit(draft, Instant::now()),
Action::Replace(Bytes::from_static(b"z")),
);
assert_eq!(out.result, Ok(Effect::Replaced { bytes: 1 }), "draft {draft:?}");
}
}
#[test]
fn a_datagram_replace_is_admitted_and_its_failure_is_the_callers_to_report() {
let h = Harness::new();
let unit = datagram_unit(DraftVersion::Draft11, Some(3));
let report = h.datagram_report();
let mut engine = h.datagram_engine();
let out =
execute(&unit, Action::Replace(Bytes::from_static(b"bigger")), &mut engine, &report);
assert_eq!(out.result, Ok(Effect::Replaced { bytes: 6 }));
assert_eq!(out.plan, Plan::WriteNow(Bytes::from_static(b"bigger")));
report.failed(Site::Datagram, ActionKind::Replace, "too large".to_owned());
let failed: Vec<_> = h
.observer
.events()
.into_iter()
.filter(|e| matches!(e, ProxyEvent::ActionFailed { .. }))
.collect();
assert_eq!(failed.len(), 1);
assert_eq!(h.counters.snapshot().actions_refused, 0);
}
#[test]
fn a_datagram_payload_splice_needs_a_real_boundary() {
let h = Harness::new();
let unit = datagram_unit(DraftVersion::Draft11, Some(3));
let report = h.datagram_report();
let mut engine = h.datagram_engine();
let out = execute(
&unit,
Action::ReplacePayload(Bytes::from_static(b"NEWP")),
&mut engine,
&report,
);
let Plan::WriteNow(bytes) = out.plan else { panic!("expected a datagram write") };
assert_eq!(&bytes[..3], &[0x01, 0x02, 0x03]);
assert_eq!(&bytes[3..], b"NEWP");
let cases = [
(DraftVersion::Draft14, Some(3), false, "draft-14 header decode consumes the payload"),
(DraftVersion::Draft11, None, false, "datagram header did not decode"),
(DraftVersion::Draft11, Some(3), true, "status datagram has no payload"),
];
for (draft, header_len, is_status, detail) in cases {
let h = Harness::new();
let unit = Unit {
target: Target::Datagram {
raw: Bytes::from_static(&[0x01, 0x02, 0x03, b'p']),
header_len,
is_status,
},
draft,
arrived_at: Instant::now(),
};
let report = h.datagram_report();
let mut engine = h.datagram_engine();
let out = execute(
&unit,
Action::ReplacePayload(Bytes::from_static(b"N")),
&mut engine,
&report,
);
assert_eq!(
out.result,
Err(Refusal::PayloadNotDelimited { detail }),
"draft {draft:?} / header_len {header_len:?} / status {is_status}",
);
assert_eq!(out.plan, Plan::WriteNow(Bytes::from_static(&[0x01, 0x02, 0x03, b'p'])),);
}
}
#[test]
fn timing_and_terminals_are_refused_at_the_datagram_site() {
for action in [
Action::Pass.delayed(Duration::from_millis(1)),
Action::Pass.held(Gate::new()),
Action::Truncate { bytes: 1, code: 1 },
Action::ResetStream { code: 1 },
] {
let h = Harness::new();
let unit = datagram_unit(DraftVersion::Draft11, Some(3));
let report = h.datagram_report();
let mut engine = h.datagram_engine();
let out = execute(&unit, action.clone(), &mut engine, &report);
assert!(
matches!(out.result, Err(Refusal::WrongSite { site: Site::Datagram, .. })),
"{action:?} -> {:?}",
out.result,
);
}
}
#[test]
fn close_session_is_honoured_at_both_stream_end_columns() {
for is_control_stream in [false, true] {
let mut h = Harness::new();
let out = h.run(
&stream_end_unit(DraftVersion::Draft11, is_control_stream),
Action::CloseSession { code: 3, reason: Bytes::from_static(b"x") },
);
assert_eq!(
out.result,
Ok(Effect::SessionClosing { code: 3 }),
"control={is_control_stream}",
);
}
}
#[test]
fn reset_at_stream_end_is_a_data_stream_capability_only() {
let mut h = Harness::new();
let out =
h.run(&stream_end_unit(DraftVersion::Draft11, false), Action::ResetStream { code: 4 });
assert_eq!(out.plan, Plan::Terminal);
assert_eq!(out.result, Ok(Effect::StreamReset { code: 4, code_defined: true }),);
let mut h = Harness::new();
let out =
h.run(&stream_end_unit(DraftVersion::Draft11, true), Action::ResetStream { code: 4 });
assert_eq!(out.result, Err(Refusal::ControlStreamResetIllegal));
assert_eq!(out.plan, Plan::Nothing, "a stream end carries no unit to forward");
}
#[test]
fn everything_else_at_stream_end_is_wrong_site_except_the_two_reset_shapes() {
for is_control_stream in [false, true] {
for action in [
Action::Replace(Bytes::from_static(b"x")),
Action::ReplacePayload(Bytes::from_static(b"x")),
Action::Pass.delayed(Duration::from_millis(1)),
Action::Pass.held(Gate::new()),
Action::Drop(DropMode::Elide),
] {
let mut h = Harness::new();
let out = h.run(
&stream_end_unit(DraftVersion::Draft11, is_control_stream),
action.clone(),
);
assert!(
matches!(out.result, Err(Refusal::WrongSite { site: Site::StreamEnd, .. })),
"control={is_control_stream} {action:?} -> {:?}",
out.result,
);
}
let mut h = Harness::new();
let out = h.run(
&stream_end_unit(DraftVersion::Draft11, is_control_stream),
Action::Truncate { bytes: 1, code: 1 },
);
let expected = if is_control_stream {
Refusal::ControlStreamResetIllegal
} else {
Refusal::WrongSite { site: Site::StreamEnd, action: ActionKind::Truncate }
};
assert_eq!(out.result, Err(expected));
}
}
#[test]
fn pass_at_stream_end_changes_nothing() {
let mut h = Harness::new();
let out = h.run(&stream_end_unit(DraftVersion::Draft11, true), Action::Pass);
assert_eq!(out.plan, Plan::Nothing);
assert_eq!(out.result, Ok(Effect::ForwardedVerbatim));
assert_eq!(h.observer.applied().len(), 1);
}
#[test]
fn stream_open_and_reject_each_report_once() {
for site in [StreamSite::Open, StreamSite::Header] {
let h = Harness::new();
let out = execute_stream(site, DraftVersion::Draft11, StreamAction::Open, &h.report());
assert_eq!(out.plan, Plan::Nothing);
assert_eq!(out.result, Ok(Effect::ForwardedVerbatim));
assert_eq!(h.observer.applied().len(), 1);
assert_eq!(h.observer.applied()[0].0, site.site());
let h = Harness::new();
let out = execute_stream(
site,
DraftVersion::Draft11,
StreamAction::Reject { code: 9 },
&h.report(),
);
assert_eq!(out.plan, Plan::RejectStream { code: 9 });
assert_eq!(out.result, Ok(Effect::StreamRejected { code: 9 }));
assert_eq!(h.observer.applied().len(), 1);
}
}
#[test]
fn no_fact_precondition_survives_execution() {
let mut saw_conditional = 0usize;
for draft in ALL_DRAFTS {
for stream_kind in [DataStreamType::Subgroup, DataStreamType::Fetch] {
for index_in_stream in [0u64, 3] {
for subgroup_id in [None, Some(0u64)] {
for status in [None, Some(3u64)] {
for mode in [None, Some(0u8), Some(1), Some(3)] {
let mut m = meta(draft);
m.stream_kind = stream_kind;
m.index_in_stream = index_in_stream;
m.subgroup_id = subgroup_id;
m.status = status;
let unit = Unit {
target: Target::Object {
meta: &m,
subgroup_id_mode: mode,
raw: object_bytes(),
},
draft,
arrived_at: Instant::now(),
};
for kind in [
ActionKind::Pass,
ActionKind::ReplacePayload,
ActionKind::DropElide,
ActionKind::Truncate,
ActionKind::ResetStream,
ActionKind::CloseSession,
] {
let replacement_len =
(kind == ActionKind::ReplacePayload).then_some(4);
let cx = unit.target.cap_ctx(draft, replacement_len);
if let Support::Conditional(p) =
classify(Site::Object, kind, &cx)
{
saw_conditional += 1;
assert!(
matches!(p, Precondition::WithinMaxDatagramSize),
"unsupplied fact {p:?} at Object/{kind:?} \
draft {draft:?}",
);
}
}
}
}
}
}
}
for is_status in [false, true] {
for header_len in [None, Some(3usize)] {
let unit = Unit {
target: Target::Datagram {
raw: Bytes::from_static(&[1, 2, 3, 4]),
header_len,
is_status,
},
draft,
arrived_at: Instant::now(),
};
for kind in [
ActionKind::Pass,
ActionKind::Replace,
ActionKind::ReplacePayload,
ActionKind::DropElide,
ActionKind::CloseSession,
] {
let cx = unit.target.cap_ctx(draft, Some(1));
if let Support::Conditional(p) = classify(Site::Datagram, kind, &cx) {
saw_conditional += 1;
assert!(
matches!(p, Precondition::WithinMaxDatagramSize),
"unsupplied fact {p:?} at Datagram/{kind:?} draft {draft:?}",
);
}
}
}
}
}
assert!(
saw_conditional > 0,
"the sweep must reach the environmental preconditions, or it proves nothing",
);
}
#[test]
fn the_ledger_stays_the_same_length_as_the_queue() {
for &draft in COMPILED_DRAFTS {
let mut h = Harness::new();
let m = meta(draft);
let at = Instant::now();
let actions = [
Action::Pass.delayed(Duration::from_millis(30)),
Action::Pass,
Action::Drop(DropMode::Elide),
Action::ReplacePayload(Bytes::from_static(b"abcd")),
Action::Replace(Bytes::from_static(b"refused")),
Action::ResetStream { code: 1 },
];
for action in actions {
h.run(&object_unit(&m, at), action);
assert_eq!(
h.deferred.len(),
h.pending.len(),
"[{draft:?}] one ledger entry per pushed unit, always",
);
}
assert!(h.pending.len() >= 5, "draft {draft:?}");
}
}
#[test]
fn backpressure_is_reported_once_per_stream() {
for &draft in COMPILED_DRAFTS {
let mut h = Harness::with_config(EgressConfig {
max_pending_bytes: 16,
max_hold: Duration::from_secs(30),
..EgressConfig::default()
});
let m = meta(draft);
let at = Instant::now();
h.run(&object_unit(&m, at), Action::Pass.delayed(Duration::from_secs(5)));
let mut transitions = 0;
for _ in 0..6 {
let out = h.run(&object_unit(&m, at), Action::Pass);
if out.entered_backpressure {
transitions += 1;
}
}
assert_eq!(transitions, 1, "[{draft:?}] the transition is reported once");
assert_eq!(
h.observer
.impairments()
.iter()
.filter(|k| matches!(k, ImpairmentKind::EgressQueueFull { .. }))
.count(),
1,
);
}
}
#[test]
fn a_delay_beyond_max_hold_is_clamped_and_reported_once() {
for &draft in COMPILED_DRAFTS {
let mut h = Harness::with_config(EgressConfig {
max_pending_bytes: 1 << 20,
max_hold: Duration::from_millis(50),
..EgressConfig::default()
});
let m = meta(draft);
let at = Instant::now();
let out = h.run(&object_unit(&m, at), Action::Pass.delayed(Duration::from_secs(9)));
let clamped = out.clamped.expect("a clamp happened");
assert!(clamped.was_clamped(), "draft {draft:?}");
assert_eq!(clamped.requested, Duration::from_secs(9));
assert_eq!(clamped.applied, Duration::from_millis(50));
assert_eq!(
h.observer.impairments(),
vec![ImpairmentKind::HoldClamped {
requested: Some(Duration::from_secs(9)),
applied: Duration::from_millis(50),
}],
);
let mut h = Harness::new();
let out = h.run(&object_unit(&m, at), Action::Pass.delayed(Duration::from_millis(5)));
assert_eq!(out.clamped.map(|d| d.was_clamped()), Some(false), "draft {draft:?}");
assert!(h.observer.impairments().is_empty());
}
}
#[test]
fn the_ledger_forgets_what_a_failed_drain_could_not_write() {
for &draft in COMPILED_DRAFTS {
let mut h = Harness::new();
let m = meta(draft);
let at = Instant::now();
h.run(
&object_unit(&m, at),
Action::ReplacePayload(Bytes::from_static(b"abcd"))
.delayed(Duration::from_millis(10)),
);
assert_eq!(h.deferred.len(), 1, "draft {draft:?}");
h.deferred.clear();
assert!(h.deferred.is_empty(), "no Replaced is reported for bytes never written");
assert!(h.deferred.take_all().is_empty());
}
}
#[test]
fn take_all_yields_only_the_entries_that_owe_an_event() {
for &draft in COMPILED_DRAFTS {
let mut h = Harness::new();
let m = meta(draft);
let at = Instant::now();
h.run(&object_unit(&m, at), Action::Pass.delayed(Duration::from_millis(30)));
h.run(&object_unit(&m, at), Action::Pass);
h.run(
&object_unit(&m, at),
Action::ReplacePayload(Bytes::from_static(b"wxyz"))
.delayed(Duration::from_millis(40)),
);
assert_eq!(h.deferred.len(), 3, "draft {draft:?}");
let owed = h.deferred.take_all();
assert_eq!(owed.len(), 2, "the ordering-only unit owes nothing");
assert_eq!(owed[0].action, ActionKind::Pass);
assert_eq!(owed[1].effect, Effect::Replaced { bytes: 10 });
assert!(h.deferred.is_empty());
}
}
#[test]
fn kind_of_agrees_with_the_published_attempt_mapping() {
assert_eq!(kind_of(&Action::Pass), ActionKind::Pass);
assert_eq!(kind_of(&Action::Replace(Bytes::new())), ActionKind::Replace);
assert_eq!(kind_of(&Action::ReplacePayload(Bytes::new())), ActionKind::ReplacePayload,);
assert_eq!(kind_of(&Action::Pass.delayed(Duration::ZERO)), ActionKind::Delay);
assert_eq!(kind_of(&Action::Pass.held(Gate::new())), ActionKind::Hold);
assert_eq!(kind_of(&Action::Drop(DropMode::Elide)), ActionKind::DropElide);
assert_eq!(kind_of(&Action::Truncate { bytes: 0, code: 0 }), ActionKind::Truncate,);
assert_eq!(kind_of(&Action::ResetStream { code: 0 }), ActionKind::ResetStream);
assert_eq!(
kind_of(&Action::CloseSession { code: 0, reason: Bytes::new() }),
ActionKind::CloseSession,
);
}
#[test]
fn a_refused_action_reports_its_refusal_and_no_impairment() {
for &draft in COMPILED_DRAFTS {
let clamping = EgressConfig {
max_pending_bytes: 1 << 20,
max_hold: Duration::from_millis(50),
..EgressConfig::default()
};
let m = meta(draft);
let at = Instant::now();
let mut h = Harness::with_config(clamping);
let out = h.run(
&object_unit(&m, at),
Action::ReplacePayload(Bytes::from_static(b"toolong"))
.delayed(Duration::from_secs(9)),
);
assert!(!out.is_applied(), "[{draft:?}] the inner action is refused");
assert_eq!(
h.observer.impairments(),
vec![],
"[{draft:?}] a refused action changed nothing, so it impaired nothing",
);
assert_eq!(
h.observer.refused().len(),
1,
"[{draft:?}] and the refusal itself is still reported",
);
let mut h = Harness::with_config(clamping);
let out = h.run(&object_unit(&m, at), Action::Pass.delayed(Duration::from_secs(9)));
assert!(out.is_applied(), "[{draft:?}] a plain delay is admitted");
assert_eq!(
h.observer.impairments(),
vec![ImpairmentKind::HoldClamped {
requested: Some(Duration::from_secs(9)),
applied: Duration::from_millis(50),
}],
);
}
}
#[test]
fn one_action_owes_two_impairments_in_the_order_it_earned_them() {
for &draft in COMPILED_DRAFTS {
let mut h = Harness::with_config(EgressConfig {
max_pending_bytes: 4,
max_hold: Duration::from_millis(50),
..EgressConfig::default()
});
let m = meta(draft);
let out = h.run(
&object_unit(&m, Instant::now()),
Action::Pass.delayed(Duration::from_secs(9)),
);
assert!(out.is_applied(), "[{draft:?}] the delay is admitted");
assert_eq!(
h.observer.attributed_impairments(),
vec![
(
Some(Leg::Upstream),
ImpairmentKind::HoldClamped {
requested: Some(Duration::from_secs(9)),
applied: Duration::from_millis(50),
},
),
(Some(Leg::Upstream), ImpairmentKind::EgressQueueFull { stream_id: 4 }),
],
"[{draft:?}] the clamp is decided before the push and both belong to the \
connection being written to",
);
}
}
#[test]
fn the_leg_turns_with_the_pipe_and_is_absent_where_there_is_none() {
let draft = COMPILED_DRAFTS[0];
let m = meta(draft);
let clamping = EgressConfig {
max_pending_bytes: 1 << 20,
max_hold: Duration::from_millis(50),
..EgressConfig::default()
};
for (side, expected) in
[(ProxySide::ClientToProxy, Leg::Upstream), (ProxySide::RelayToProxy, Leg::Client)]
{
let mut h = Harness::with_config(clamping).reading_from(side);
h.run(&object_unit(&m, Instant::now()), Action::Pass.delayed(Duration::from_secs(9)));
assert_eq!(
h.observer.attributed_impairments(),
vec![(
Some(expected),
ImpairmentKind::HoldClamped {
requested: Some(Duration::from_secs(9)),
applied: Duration::from_millis(50),
},
)],
"a clamp on the pipe reading from {side:?} holds bytes off the {expected:?} leg",
);
}
for side in [ProxySide::ClientToProxy, ProxySide::RelayToProxy] {
let h = Harness::new().reading_from(side);
h.report().impairment(ImpairmentKind::CoarseReleaseTimer {
backend: crate::instrument::TimerBackend::Condvar,
detail: None,
});
let attributed = h.observer.attributed_impairments();
assert_eq!(attributed.len(), 1);
assert_eq!(
attributed[0].0, None,
"the release wheel belongs to the process, not to a connection, so the pipe \
reading from {side:?} must not name one either",
);
}
}
#[test]
fn one_pipe_reports_the_near_leg_the_far_leg_and_neither_in_order() {
let draft = COMPILED_DRAFTS[0];
let h = Harness::new();
let report = h.report();
report.impairment(ImpairmentKind::FramerBypass {
stream_id: 4,
draft,
reason: crate::types::BypassReason::DecodeError,
});
report.impairment(ImpairmentKind::EgressQueueFull { stream_id: 4 });
report.impairment(ImpairmentKind::CoarseReleaseTimer {
backend: crate::instrument::TimerBackend::Condvar,
detail: None,
});
assert_eq!(
h.observer.attributed_impairments(),
vec![
(
Some(Leg::Client),
ImpairmentKind::FramerBypass {
stream_id: 4,
draft,
reason: crate::types::BypassReason::DecodeError,
},
),
(Some(Leg::Upstream), ImpairmentKind::EgressQueueFull { stream_id: 4 }),
(
None,
ImpairmentKind::CoarseReleaseTimer {
backend: crate::instrument::TimerBackend::Condvar,
detail: None,
},
),
],
"one pipe, three answers: what arrived, what could not be written, and neither",
);
}
}