pub struct EventStream<S> { /* private fields */ }Expand description
A potentially infinite stream of Server-Sent Events (SSE).
An EventStream can be constructed from any Stream of items of type
Event. The stream can be constructed directly via EventStream::from()
or through generator syntax via EventStream!.
§Responder
EventStream is a (potentially infinite) responder. The response
Content-Type is set to EventStream.
The body is unsized, and values are sent as
soon as they are yielded by the internal iterator.
§Heartbeat
A heartbeat comment is injected into the internal stream and sent at a fixed
interval. The comment is discarded by clients and serves only to keep the
connection alive; it does not interfere with application data. The interval
defaults to 30 seconds but can be adjusted with
EventStream::heartbeat().
§Examples
Use EventStream! to yield an infinite series of “ping” SSE messages to
the client, one per second:
use rocket::response::stream::{Event, EventStream};
use rocket::tokio::time::{self, Duration};
#[get("/events")]
fn stream() -> EventStream![] {
EventStream! {
let mut interval = time::interval(Duration::from_secs(1));
loop {
yield Event::data("ping");
interval.tick().await;
}
}
}Yield 9 events: 3 triplets of retry, data, and comment events:
use rocket::response::stream::{Event, EventStream};
use rocket::tokio::time::Duration;
#[get("/events")]
fn events() -> EventStream![] {
EventStream! {
for i in 0..3 {
yield Event::retry(Duration::from_secs(10));
yield Event::data(format!("{}", i)).id("cat").event("bar");
yield Event::comment("silly boy");
}
}
}The syntax of EventStream! as an expression is identical to that of
stream!. For how to gracefully
terminate an otherwise infinite stream, see graceful
shutdown.
§Borrowing
If an EventStream contains a borrow, the extended type syntax
EventStream![Event + '_] must be used:
use rocket::State;
use rocket::response::stream::{Event, EventStream};
#[get("/events")]
fn events(ctxt: &State<bool>) -> EventStream![Event + '_] {
EventStream! {
// By using `ctxt` in the stream, the borrow is moved into it. Thus,
// the stream object contains a borrow, prompting the '_ annotation.
if *ctxt.inner() {
yield Event::data("hi");
}
}
}See stream#borrowing for further
details on borrowing in streams.
§Pitfalls
Server-Sent Events are a rather simple mechanism, though there are some pitfalls to be aware of.
-
Buffering
Protocol restrictions complicate implementing an API that does not buffer. As such, if you are sending lots of data, consider sending the data via multiple data fields (with events to signal start and end). Alternatively, send one event which instructs the client to fetch the data from another endpoint which in-turn streams the data.
-
Raw SSE requires UTF-8 data
Only UTF-8 data can be sent via SSE. If you need to send arbitrary bytes, consider encoding it, for instance, as JSON using
Event::json(). Alternatively, as described before, use SSE as a notifier which alerts the client to fetch the data from elsewhere. -
Raw SSE is Lossy
Data sent via SSE cannot contain new lines
\nor carriage returns\rdue to interference with the line protocol.The protocol allows expressing new lines as multiple messages, however, and Rocket automatically transforms a message of
foo\nbarinto two messages,fooandbar, so that they are reconstructed (automatically) asfoo\nbaron the client-side. For messages that only contain new lines\n, the conversion is lossless.However, the protocol has no mechanism for expressing carriage returns and thus it is not possible to send unencoded carriage returns via SSE. Rocket handles carriage returns like it handles new lines: it splits the data into multiple messages. Thus, a sequence of
\r\nbecomes\nat the client side. A single\rthat is not part of an\r\nsequence also becomes\nat the client side. As a result, the messagefoo\r\nbar\rbazis read asfoo\nbar\nbazat the client-side.To send messages losslessly, they must be encoded first, for instance, by using
Event::json(). -
Clients reconnect ad-infinitum
The SSE standard stipulates: “Clients will reconnect if the connection is closed; a client can be told to stop reconnecting using the HTTP 204 No Content response code.” As a result, clients will typically reconnect exhaustively until either they choose to disconnect or they receive a
204 No Contentresponse.
Implementations§
Source§impl<S: Stream<Item = Event>> EventStream<S>
impl<S: Stream<Item = Event>> EventStream<S>
Sourcepub fn heartbeat<H: Into<Option<Duration>>>(self, heartbeat: H) -> Self
pub fn heartbeat<H: Into<Option<Duration>>>(self, heartbeat: H) -> Self
Sets a “ping” interval for this EventStream to avoid connection
timeouts when no data is being transferred. The default interval is 30
seconds.
The ping is implemented by sending an empty comment to the client every
interval seconds.
§Example
use rocket::response::stream::{Event, EventStream};
use rocket::tokio::time::Duration;
#[get("/events")]
fn events() -> EventStream![] {
// Remove the default heartbeat.
EventStream::from(event_stream).heartbeat(None);
// Set the heartbeat interval to 15 seconds.
EventStream::from(event_stream).heartbeat(Duration::from_secs(15));
// Do the same but for a generated `EventStream`:
let stream = EventStream! {
yield Event::data("hello");
};
stream.heartbeat(Duration::from_secs(15))
}Trait Implementations§
Source§impl<S: Stream<Item = Event>> From<S> for EventStream<S>
impl<S: Stream<Item = Event>> From<S> for EventStream<S>
Source§fn from(stream: S) -> Self
fn from(stream: S) -> Self
Creates an EventStream from a Stream of Events.
Use EventStream::from() to construct an EventStream from an already
existing stream. Otherwise, prefer to use EventStream!.
§Example
use rocket::response::stream::{Event, EventStream};
use rocket::futures::stream;
let raw = stream::iter(vec![Event::data("a"), Event::data("b")]);
let stream = EventStream::from(raw);Auto Trait Implementations§
impl<S> Freeze for EventStream<S>where
S: Freeze,
impl<S> RefUnwindSafe for EventStream<S>where
S: RefUnwindSafe,
impl<S> Send for EventStream<S>where
S: Send,
impl<S> Sync for EventStream<S>where
S: Sync,
impl<S> Unpin for EventStream<S>where
S: Unpin,
impl<S> UnwindSafe for EventStream<S>where
S: UnwindSafe,
Blanket Implementations§
Source§impl<'a, T> AsTaggedExplicit<'a> for Twhere
T: 'a,
impl<'a, T> AsTaggedExplicit<'a> for Twhere
T: 'a,
Source§impl<'a, T> AsTaggedImplicit<'a> for Twhere
T: 'a,
impl<'a, T> AsTaggedImplicit<'a> for Twhere
T: 'a,
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
§impl<T> IntoCollection<T> for T
impl<T> IntoCollection<T> for T
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Paint for Twhere
T: ?Sized,
impl<T> Paint for Twhere
T: ?Sized,
Source§fn fg(&self, value: Color) -> Painted<&T>
fn fg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the foreground set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like red() and
green(), which have the same functionality but are
pithier.
§Example
Set foreground color to white using fg():
use yansi::{Paint, Color};
painted.fg(Color::White);Set foreground color to white using white().
use yansi::Paint;
painted.white();Source§fn bright_black(&self) -> Painted<&T>
fn bright_black(&self) -> Painted<&T>
Source§fn bright_red(&self) -> Painted<&T>
fn bright_red(&self) -> Painted<&T>
Source§fn bright_green(&self) -> Painted<&T>
fn bright_green(&self) -> Painted<&T>
Source§fn bright_yellow(&self) -> Painted<&T>
fn bright_yellow(&self) -> Painted<&T>
Source§fn bright_blue(&self) -> Painted<&T>
fn bright_blue(&self) -> Painted<&T>
Source§fn bright_magenta(&self) -> Painted<&T>
fn bright_magenta(&self) -> Painted<&T>
Source§fn bright_cyan(&self) -> Painted<&T>
fn bright_cyan(&self) -> Painted<&T>
Source§fn bright_white(&self) -> Painted<&T>
fn bright_white(&self) -> Painted<&T>
Source§fn bg(&self, value: Color) -> Painted<&T>
fn bg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the background set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like on_red() and
on_green(), which have the same functionality but
are pithier.
§Example
Set background color to red using fg():
use yansi::{Paint, Color};
painted.bg(Color::Red);Set background color to red using on_red().
use yansi::Paint;
painted.on_red();Source§fn on_primary(&self) -> Painted<&T>
fn on_primary(&self) -> Painted<&T>
Source§fn on_magenta(&self) -> Painted<&T>
fn on_magenta(&self) -> Painted<&T>
Source§fn on_bright_black(&self) -> Painted<&T>
fn on_bright_black(&self) -> Painted<&T>
Source§fn on_bright_red(&self) -> Painted<&T>
fn on_bright_red(&self) -> Painted<&T>
Source§fn on_bright_green(&self) -> Painted<&T>
fn on_bright_green(&self) -> Painted<&T>
Source§fn on_bright_yellow(&self) -> Painted<&T>
fn on_bright_yellow(&self) -> Painted<&T>
Source§fn on_bright_blue(&self) -> Painted<&T>
fn on_bright_blue(&self) -> Painted<&T>
Source§fn on_bright_magenta(&self) -> Painted<&T>
fn on_bright_magenta(&self) -> Painted<&T>
Source§fn on_bright_cyan(&self) -> Painted<&T>
fn on_bright_cyan(&self) -> Painted<&T>
Source§fn on_bright_white(&self) -> Painted<&T>
fn on_bright_white(&self) -> Painted<&T>
Source§fn attr(&self, value: Attribute) -> Painted<&T>
fn attr(&self, value: Attribute) -> Painted<&T>
Enables the styling Attribute value.
This method should be used rarely. Instead, prefer to use
attribute-specific builder methods like bold() and
underline(), which have the same functionality
but are pithier.
§Example
Make text bold using attr():
use yansi::{Paint, Attribute};
painted.attr(Attribute::Bold);Make text bold using using bold().
use yansi::Paint;
painted.bold();Source§fn rapid_blink(&self) -> Painted<&T>
fn rapid_blink(&self) -> Painted<&T>
Source§fn quirk(&self, value: Quirk) -> Painted<&T>
fn quirk(&self, value: Quirk) -> Painted<&T>
Enables the yansi Quirk value.
This method should be used rarely. Instead, prefer to use quirk-specific
builder methods like mask() and
wrap(), which have the same functionality but are
pithier.
§Example
Enable wrapping using .quirk():
use yansi::{Paint, Quirk};
painted.quirk(Quirk::Wrap);Enable wrapping using wrap().
use yansi::Paint;
painted.wrap();Source§fn clear(&self) -> Painted<&T>
👎Deprecated since 1.0.1: renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
fn clear(&self) -> Painted<&T>
resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.Source§fn whenever(&self, value: Condition) -> Painted<&T>
fn whenever(&self, value: Condition) -> Painted<&T>
Conditionally enable styling based on whether the Condition value
applies. Replaces any previous condition.
See the crate level docs for more details.
§Example
Enable styling painted only when both stdout and stderr are TTYs:
use yansi::{Paint, Condition};
painted.red().on_yellow().whenever(Condition::STDOUTERR_ARE_TTY);