pub struct EventTail<T> { /* private fields */ }Expand description
A shared, wakeable, replayable source of stream events.
What a session publishes into and what a response body reads from. It is
Send + Sync and holds no borrow of anything, which is the property that
lets a body stream outlive the request task that built it — see the module
docs on why the stream must not be the thing holding the engine.
Cloning is cheap and shares: every clone reads and writes the same ring, so a router can keep one in its state and hand a reference to each response.
§Example
use core::num::NonZeroUsize;
use serde_json::{Value, json};
use protocol_droid::adapters::primary::transport::http::EventTail;
let capacity = NonZeroUsize::new(4).ok_or("capacity")?;
let tail = EventTail::<Value>::new(capacity);
for index in 0..3 {
tail.push(json!({ "index": index }));
}
// A client that has seen all three is current and gets no frames.
assert!(tail.plan(Some("3")).resume().pending().is_empty());Implementations§
Source§impl<T> EventTail<T>
impl<T> EventTail<T>
Sourcepub fn new(capacity: NonZeroUsize) -> Self
pub fn new(capacity: NonZeroUsize) -> Self
A tail over a ring retaining capacity events.
RECOMMENDED_CAPACITY is the
right value for a caller that has not measured its payloads; pass a real
count for one that has.
Source§impl<T> EventTail<T>where
T: FramePayload,
impl<T> EventTail<T>where
T: FramePayload,
Sourcepub fn push(&self, payload: T) -> EventId
pub fn push(&self, payload: T) -> EventId
Publishes an event and wakes every stream parked on this tail.
The returned id is the value a client quotes to resume after this event, so a publisher that discards it has made the event unresumable: the next reconnect is told about a gap the client never knew it had.
Sourcepub fn plan(&self, header: Option<&str>) -> Planned<T>
pub fn plan(&self, header: Option<&str>) -> Planned<T>
Decides what a client that sent header gets, atomically with the
watermark it was decided against.
The pairing with the cursor is the point. Resume names the frames to
write; the stream needs to know where to continue from. Reading those
from two separate lock acquisitions would open the window the module docs
describe, so Planned carries both and there is no way to obtain one
without the other.
header is the raw Last-Event-ID value, or None for a client that has
never seen this stream. A header that is present but unreadable is a
refusal, decided before the ring is consulted.
Source§impl<T> EventTail<T>where
T: FramePayload + Send + 'static,
The response-building half of EventTail, separated by its bound.
impl<T> EventTail<T>where
T: FramePayload + Send + 'static,
The response-building half of EventTail, separated by its bound.
The bound is T: Send + 'static because the body it produces has to be sent
between threads and outlive the call; push and
plan do not carry it, so a caller that only publishes or only
inspects never has to satisfy it.
Sourcepub fn respond(
&self,
header: Option<&str>,
keep_alive: Duration,
) -> Result<Response, StreamError>
pub fn respond( &self, header: Option<&str>, keep_alive: Duration, ) -> Result<Response, StreamError>
Plans and answers in one call, for a router with nothing to do in between.
§Errors
StreamError::Response if the response headers cannot be assembled.
Trait Implementations§
Source§impl EventSource for EventTail<Value>
A tail is its own event source, which is the shape T14’s server uses: one
session, one tail. A server keeping more state per session holds the tail as
a field and implements this for the session itself.
impl EventSource for EventTail<Value>
A tail is its own event source, which is the shape T14’s server uses: one session, one tail. A server keeping more state per session holds the tail as a field and implements this for the session itself.