All subscriptions begin in the Idle state. A subscription can be
initiated and moved to the Pending state by either a publisher or a
subscriber. A publisher initiates a subscription to a track by
sending the PUBLISH message. The subscriber either accepts or rejects
the subscription using PUBLISH_OK (Section 9.3) or
PUBLISH_ERROR. A subscriber
initiates a subscription to a track by sending the SUBSCRIBE message.
The publisher either accepts or rejects the subscription using
SUBSCRIBE_OK or SUBSCRIBE_ERROR. Once either of these sequences is
successful, the subscription moves to the Established state and can
be updated by the subscriber using REQUEST_UPDATE. Either endpoint
can terminate an Established subscription, moving it to the
Terminated state. The subscriber terminates a subscription in the
Pending (Subscriber) or Established states by sending STOP_SENDING.
The publisher terminates a subscription in the
Pending (Publisher) or Established states by sending PUBLISH_DONE
and closing the stream.
This diagram shows the subscription state machine:
+--------+
| Idle |
+--------+
| |
SUBSCRIBE | | PUBLISH
(subscriber)| | (publisher)
V V
+--------------+ +--------------+
| Pending | | Pending |
+----| (Subscriber) | | (Publisher) |----+
| +--------------+ +--------------+ |
| | | |
SUBSCRIBE_ERROR | SUBSCRIBE_OK | | PUBLISH_OK | PUBLISH_ERROR
(publisher) | (publisher)| | (subscriber) | (subscriber)
| V V |
| +-------------+ |
| | Established | ------+
| | | | REQUEST_UPDATE
| +-------------+ <-----+
| | | |
+--- STOP_SENDING | | PUBLISH_DONE ----+
| (subscriber)| | (publisher) |
| V V |
| +-------------+ |
+----------->| Terminated | <------------+
+-------------+
A publisher MUST send exactly one SUBSCRIBE_OK or SUBSCRIBE_ERROR in response to a SUBSCRIBE. A subscriber MUST send exactly one PUBLISH_OK (Section 9.3) or PUBLISH_ERROR in response to a PUBLISH. The peer SHOULD close the session with a protocol error if it receives more than one.
Either endpoint can initiate a subscription to a track without exchanging any prior messages other than SETUP. Relays MUST NOT send any PUBLISH messages without knowing the client is interested in and authorized to receive the content. The communication of intent and authorization can be accomplished by the client sending SUBSCRIBE_NAMESPACE, or conveyed in other mechanisms out of band.
An endpoint MAY SUBSCRIBE to a Track it is publishing, though only Relays are required to handle such a SUBSCRIBE. Such self-subscriptions are identical to subscriptions initiated by other endpoints, and all published Objects will be forwarded back to the endpoint, subject to priority and congestion response rules.
An endpoint MAY have multiple concurrent subscriptions to the same Track, each identified by a unique Request ID. A publisher MAY assign the same or different Track Aliases to these subscriptions.
When an Object matches the filters of multiple subscriptions to the same Track, the publisher MUST send the Object once for each matching subscription, even when those subscriptions share the same Track Alias. Because subscriptions can share a Track Alias, the subscriber re-applies each subscription's filter to determine which subscription a received Object belongs to. Subscribers SHOULD avoid overlapping filters across subscriptions to the same Track, as they are responsible for deduplicating any resulting duplicate Objects.
A publisher SHOULD begin sending incomplete objects when available to avoid incurring additional latency.
Publishers MAY start sending Objects on PUBLISH-initiated subscriptions before receiving a PUBLISH_OK response to reduce latency. Doing so can consume unnecessary resources in cases where the Subscriber rejects the subscription with PUBLISH_ERROR or pauses the subscription (see below). It can also result in the Subscriber dropping Objects if its buffering limits are exceeded (see Section 11.2 and Section 11.3.1).
An object published or received in a subgroup or datagram is subscription-delivered.
3.1.1. Pausing Subscriptions
An Established subscription is either paused or not paused. The publisher does
not send Objects on a paused subscription, and does send them when it is not
paused. Control messages, such as PUBLISH_DONE (Section 9.9), are
sent regardless of whether the subscription is paused.
The initiator of the subscription sets the initial state by including the
FORWARD parameter (Section 9.20.18) in PUBLISH or SUBSCRIBE. The
subscriber can pause an Established subscription by sending REQUEST_UPDATE
with FORWARD set to 0, or resume it by sending REQUEST_UPDATE with FORWARD set
to 1.
3.1.2. Subscription State Management
A subscriber keeps subscription state until it cancels the request (see Section 6.4.2.3), or until receipt of a PUBLISH_DONE or REQUEST_ERROR. Note that PUBLISH_DONE does not usually indicate that state can immediately be removed, see Section 9.9.
The Publisher can remove subscription state as soon as it has received STOP_SENDING. It MUST reset any open streams associated with the SUBSCRIBE.
The Publisher can also immediately delete subscription state after sending PUBLISH_DONE, but MUST NOT send it until it has closed all related streams.
A REQUEST_ERROR indicates no objects will be delivered, and both endpoints can immediately remove relevant state. Objects MUST NOT be sent for requests that end with an error.
3.1.3. Track Alias
To optimize wire efficiency, Subgroups and Datagrams refer to a track by a numeric identifier, rather than the Full Track Name. Track Alias is chosen by the publisher and included in SUBSCRIBE_OK (Section 9.7) or PUBLISH (Section 9.8).
The same Track Alias MUST NOT be used by a publisher to refer to two different
Tracks simultaneously in the same session. If a subscriber receives a
PUBLISH or SUBSCRIBE_OK that uses the same Track Alias as a different Track
with an Established subscription, it MUST close the session with error
DUPLICATE_TRACK_ALIAS.
Objects can be sent before the Subscriber knows the Track Alias, requiring buffering Objects with an unknown Track Alias. If a Track Alias is used for two concurrent subscriptions to the same Track, an Object that arrives with the Track Alias could be for either Subscription. Reusing the same Track Alias for concurrent subscriptions to the same Track can lead to missed delivery if objects for the new subscription arrive before the control message establishing the shared Alias. The Subscriber can assume the Track Alias is reused until told otherwise, in order to avoid missing Objects.
To avoid a protocol violation and to ensure the Subscriber knows which Track Objects are from, Publishers SHOULD NOT reuse a Track Alias for different Tracks within a session, unless it is certain the prior Subscription has been completely closed and no Objects are scheduled to be sent or in flight.
Objects can arrive after a subscription has been cancelled. Subscribers SHOULD retain sufficient state to quickly discard these unwanted Objects, rather than treating them as belonging to an unknown Track Alias.
3.1.3.1. Unknown Track Alias
When an endpoint receives a datagram or a new stream with a Track Alias that
is not yet associated with an Established subscription, it MAY drop the
data or buffer it briefly to handle reordering with the control message that
establishes the Track Alias. For streams, the endpoint MAY withhold stream
flow control beyond the stream header until the Track Alias has been
established. To prevent deadlocks, endpoints MUST allocate connection flow
control to control streams before allocating it to any data streams;
otherwise a receiver might wait for a control message containing a Track
Alias to release flow control, while the sender waits for flow control to
send the message.
3.1.4. Largest Object
The Largest Object is the Object with the largest Location
(Section 8.2) in the Track from the perspective of the publisher
processing the message. Largest Object updates when the first byte of an Object
with a Location larger than the previous value is published or received through
a subscription. Largest Object therefore identifies an Object that can still
be arriving.
The Next Object is the Location immediately following Largest Object, which
is {Largest Object.Group, Largest Object.Object + 1}, or {0, 0} if no content
has been delivered yet. The Next Group is the first Location of the Group
following Largest Object, which is {Largest Object.Group + 1, 0}.
Next Object and Next Group are Locations that do not necessarily
refer to an Object that exists.