pub trait Plugin: Send {
// Required methods
fn name(&self) -> &str;
fn on_bytes(&mut self, ctx: &mut Ctx<'_>, input: &[u8]) -> Result<()>;
// Provided methods
fn on_eof(&mut self, ctx: &mut Ctx<'_>) -> Result<()> { ... }
fn tick_interval(&self) -> Option<Duration> { ... }
fn on_tick(&mut self, ctx: &mut Ctx<'_>) -> Result<()> { ... }
fn boundaries(&self) -> Boundaries { ... }
fn needs(&self) -> Needs { ... }
}Expand description
One stage of a pipeline. Instances are per-direction and per-connection.
Synchronous on purpose: it is the only shape that maps onto a WASM guest call, and it keeps the per-chunk cost at a function call rather than a future poll. Anything that must await belongs on the effect side, where the host performs it off the critical path.
Required Methods§
Sourcefn on_bytes(&mut self, ctx: &mut Ctx<'_>, input: &[u8]) -> Result<()>
fn on_bytes(&mut self, ctx: &mut Ctx<'_>, input: &[u8]) -> Result<()>
A chunk arrived from upstream. input is the same slice as
Ctx::input; it is passed separately because it is the hot argument.
One call is one unit. Where a stage above declared framing with
Ctx::boundary that is one call per unit rather than one per chunk,
so a stage never has to unpick two of them from a single slice.
Provided Methods§
Sourcefn on_eof(&mut self, ctx: &mut Ctx<'_>) -> Result<()>
fn on_eof(&mut self, ctx: &mut Ctx<'_>) -> Result<()>
Upstream reached EOF. Last chance to emit buffered bytes.
Sourcefn tick_interval(&self) -> Option<Duration>
fn tick_interval(&self) -> Option<Duration>
How often this stage wants on_tick called, or
None (the default) for never.
Read once, at the end of construction, so it must not depend on
anything that changes later. A stage whose interval is configurable
reads its config in PluginFactory::build and answers from that.
The host owns the clock. A guest cannot read one (a WASM module has no way to reach the host’s time) which is why this is a period the stage asks for rather than a timestamp it checks. The cost falls on the relay: one timer per direction per connection for any pipeline containing a ticking stage, so a stage asking for milliseconds is asking every forked connection to wake up that often.
Sourcefn on_tick(&mut self, ctx: &mut Ctx<'_>) -> Result<()>
fn on_tick(&mut self, ctx: &mut Ctx<'_>) -> Result<()>
The stage’s schedule came due.
Called from the same task, and under the same rules, as
on_bytes, it just arrives without any. This is
how a stage does anything that time rather than traffic should drive:
report a measurement, release bytes it has been holding back, emit a
keepalive. Without it a stalled stream and a finished one are
indistinguishable from inside a plugin.
Ctx::input is empty, so there is nothing to pass through; anything
emitted here is emitted with Ctx::forward and continues downstream
through the stages below this one, in the same way
on_eof cascades. Emitting nothing is the common
case and costs nothing.
A stage that emits from here is fabricating a message boundary on a
datagram path (the bytes belong to no datagram the peer sent) so it
should report Boundaries::Fuse from
boundaries. A stage that only observes need
not.
What is emitted here is one unit unless Ctx::boundary says
otherwise, exactly as in on_bytes.
Ticks run for the life of the pipeline and stop at end of stream, so they arrive whether or not anything is moving, which is the point, and is what a keepalive needs. A stage that has nothing to say until the first chunk has arrived is expected to keep that state itself.
Sourcefn boundaries(&self) -> Boundaries
fn boundaries(&self) -> Boundaries
What this stage does to the message boundaries passing through it.
On a byte stream a chunk is an arbitrary slice: a stage may buffer,
split or coalesce freely, and the host is free to do the same. On a
datagram path the chunk is the message: one on_bytes call per
datagram, and whatever it emits is sent as exactly one datagram. A
stage that buffers across calls, or emits two messages’ worth from one,
silently corrupts the protocol unless it says so here.
The four answers, in the order a stage usually wants them:
Preserve: one unit in, one unit out. Every observer, and every codec that rewrites a message in place.Fuse: the units are gone below this stage. Anything that buffers across calls, splits, coalesces, or emits from a tick.Seal: asPreserve, and the boundary is also written into the payload, so it outlives a stage below that fuses.frameand nothing else.Split: the units below are read out of the bytes rather than inherited, so the ones from above do not survive.unframeand nothing else.
Defaults to Fuse because that is the answer that claims nothing,
which is the safe one for a stage that has not thought about it,
including any plugin loaded from outside this binary.
Declaring the truth matters more than declaring safety. block fuses
and says so, and it is still the right stage to reach for when one
datagram per 1400 bytes is exactly what was wanted: the host warns and
relays anyway.
Sourcefn needs(&self) -> Needs
fn needs(&self) -> Needs
What this stage needs of the path it was placed on.
Unlike boundaries, which the host only warns
about, an unmet requirement is a build error: a stage saying this
cannot do its job at all where it was put.
Upstream means every call must carry one whole
message, so boundaries have to arrive from a datagram endpoint or from
an unframe. Downstream means the units this
stage emits have to reach a datagram endpoint or a frame, or what it
wrote cannot be read back. The two are separate because the stages
that want them want opposite ones: a stage that seals a message and
appends a tag makes its own boundaries and needs them to survive
downwards, while the stage that verifies and strips that tag needs
whole messages from above and does not care what happens below it.
Read once, after build, alongside boundaries. Neither is consulted
on the per-chunk path.
Trait Implementations§
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".