Skip to main content

AnyConnection

Enum AnyConnection 

Source
pub enum AnyConnection {
Show 15 variants Draft07(Connection), Draft08(Connection), Draft09(Connection), Draft10(Connection), Draft11(Connection), Draft12(Connection), Draft13(Connection), Draft14(Connection), Draft15(Connection), Draft16(Connection), Draft17(Connection), Draft18(Connection), Draft19(Connection), Draft20(Connection), Draft21(Connection),
}
Expand description

A MoQT client connection of any enabled draft version.

Wraps the draft-specific Connection type. Methods common to all drafts are forwarded; for draft-specific protocol calls, match on the variant.

Variants§

§

Draft07(Connection)

A draft-draft07 connection.

§

Draft08(Connection)

A draft-draft08 connection.

§

Draft09(Connection)

A draft-draft09 connection.

§

Draft10(Connection)

A draft-draft10 connection.

§

Draft11(Connection)

A draft-draft11 connection.

§

Draft12(Connection)

A draft-draft12 connection.

§

Draft13(Connection)

A draft-draft13 connection.

§

Draft14(Connection)

A draft-draft14 connection.

§

Draft15(Connection)

A draft-draft15 connection.

§

Draft16(Connection)

A draft-draft16 connection.

§

Draft17(Connection)

A draft-draft17 connection.

§

Draft18(Connection)

A draft-draft18 connection.

§

Draft19(Connection)

A draft-draft19 connection.

§

Draft20(Connection)

A draft-draft20 connection.

§

Draft21(Connection)

A draft-draft21 connection.

Implementations§

Source§

impl AnyConnection

Source

pub fn draft(&self) -> DraftVersion

Returns the draft version this connection is using.

Source

pub fn server_setup(&self) -> &AnyControlMessage

The SETUP message the server answered the handshake with.

SERVER_SETUP through draft-16, the server’s half of the unified SETUP from draft-17. fields renders its parameters under the negotiated draft’s own names, in the order they arrived — which is what makes one relay’s setup response comparable to another’s.

Source

pub fn server_setup_raw(&self) -> Option<&[u8]>

The framed wire bytes of Self::server_setup, as they arrived.

Source

pub fn set_observer(&mut self, observer: Arc<dyn AnyConnectionObserver>)

Attach an observer. The observer is adapted into the draft-specific observer trait and installed on the inner connection; events are forwarded as AnyClientEvent.

Replaces any previously attached observer.

Source

pub fn clear_observer(&mut self)

Remove any attached observer.

Source

pub fn close(&self, code: u32, reason: &[u8])

Close the connection with the given application error code and reason.

Source§

impl AnyConnection

Source

pub async fn connect( addr: &str, config: AnyClientConfig, ) -> Result<Self, AnyConnectionError>

Connect to a MoQT server using the requested draft. Builds the draft-specific ClientConfig from the provided AnyClientConfig and dispatches to the appropriate Connection::connect.

Source

pub async fn adopt( transport: Transport, config: AnyClientConfig, ) -> Result<Self, AnyConnectionError>

Run the MoQT setup handshake over a transport somebody else established.

connect dials its own socket, which is right for a caller that wants a connection and wrong for one that wants to measure how a peer behaves: choosing the address, the SNI, the ALPN offer or the certificate policy all mean dialling first and adopting after. Every draft module has Connection::adopt for exactly this, and this wrapper is how a caller reaches it without re-implementing the per-draft match over AnyConnection for itself.

config.draft selects the module. Nothing here re-checks it against the ALPN the transport was negotiated with: adopting a transport under a draft the peer did not agree to is a legitimate probe, and refusing it would remove the ability to ask.

Source

pub async fn adopt_offering( transport: Transport, config: AnyClientConfig, versions: Option<Vec<VarInt>>, ) -> Result<Self, AnyConnectionError>

Self::adopt, offering exactly versions in CLIENT_SETUP.

None offers what config implies. Some replaces the list outright, and takes raw varints rather than DraftVersions because the reason to reach for this is to offer a version no draft assigns — which an enum of drafts cannot name.

Drafts 07-14 only. Drafts 15-21 settle the version by ALPN and put no version list on the wire, so there is nothing there to offer and a Some on one of them is refused rather than quietly ignored: silently sending the ordinary handshake would answer a question that was never asked.

Source

pub fn negotiated_version(&self) -> Option<VarInt>

The version SERVER_SETUP selected, when the draft has a version list to select from.

Not the same question as draft, and the difference is the whole point for a caller enumerating what a relay supports: draft() reports the module the connection is running, which is the one the client chose, while this reports what the server picked out of the versions offered. Offer 11 through 14 and a server that settles on 12 leaves draft() saying 14 and this saying 12.

None for drafts 15-21, which is a fact about those drafts rather than a gap here: they carry no additional_versions, so a connection offers exactly one version and there is nothing for a server to choose between. What a peer supports there is discovered by ALPN instead.

Source

pub async fn recv_and_dispatch(&mut self) -> Result<(), AnyConnectionError>

Read and dispatch one control message on the active draft. Draft-specific control-message return values are discarded because event delivery goes through the attached observer; callers only care about success/failure.

Source

pub async fn recv_response( &mut self, request: &mut AnyRequest, ) -> Result<Option<AnyControlMessage>, AnyConnectionError>

Read the next control message that could answer request, wherever the negotiated draft carries it.

This is the read half of the split AnyRequest describes, and without it the facade can send a request on drafts 17-21 and then has no way to hear the answer: those drafts put every response on the request’s own bidirectional stream, which recv_and_dispatch — a control-stream read — never touches.

§What “could answer” means, and why it is not “does answer”

Which of the two happens depends on the draft, and the difference is the protocol’s, not this method’s:

  • Drafts 07-16, AnyRequest::ControlPlane. Every request shares the one control stream, so this returns the next control message on it, which is the answer only if nothing else was in flight. A peer is free to send MAX_REQUEST_ID or a PUBLISH_NAMESPACE of its own first. Correlate on the request_id the response carries against AnyRequest::request_id, and read again if it is not this one.
  • Draft-16 namespace subscriptions and drafts 17-21. The read is on the request’s own stream, so nothing else can arrive on it. Those drafts’ responses carry no request id at all — the stream is the correlation — which is exactly why the read has to be addressed by the handle rather than by the connection.
§Ok(None)

The peer ended the stream cleanly without a message on it. Reachable today only on a draft-16 namespace stream, whose reader distinguishes a FIN from a message; drafts 17-21 report the same event as an error. A distinct value rather than an error because a peer that answered nothing and closed is a different fact from a read that failed, and a caller that has to tell them apart should not be reading either one out of a message string. Neither phrase carries quotation marks and neither may: they are this crate naming two outcomes, and the marks would hand them to the drafts the line above names.

§Errors

Whatever the underlying read or dispatch produced, flattened to a string like every other error here — a transport failure, a peer reset, or an endpoint refusing a message that does not fit the request’s state. A message the endpoint refuses has still been emitted to any attached observer by the time this returns, so an observer is the way to see what arrived when the return value only says that something did not fit.

A request from a different draft than this connection is refused rather than silently read on the wrong stream.

Source

pub async fn unsubscribe( &mut self, request_id: VarInt, ) -> Result<(), AnyConnectionError>

Send an UNSUBSCRIBE for the given request ID. Drafts 07 through 16.

§Drafts 17 through 20 have no such message

Draft-17 deleted UNSUBSCRIBE, and drafts 18, 19 and 20 keep it deleted: a subscriber ends a subscription by resetting its request stream, which is AnyRequest::cancel, or waits for PUBLISH_DONE. So the error those four return is not a gap to be filled later — there is nothing to wire — and a caller reaching for it on one of them wants cancel on the handle subscribe returned.

Source

pub async fn subscribe( &mut self, namespace: TrackNamespace, track_name: Vec<u8>, subscriber_priority: u8, group_order: GroupOrder, filter_type: FilterType, ) -> Result<AnyRequest, AnyConnectionError>

Send a SUBSCRIBE with the given filter, priority, and group order. Supported on every draft this build carries. Drafts 15 onward carry priority/order/filter as parameters rather than fields; this helper passes an empty parameter list, so on those drafts all three take the protocol default and the three arguments here are ignored.

§The Track Alias, and why it is not an argument

Drafts 07 through 11 carry a Track Alias on SUBSCRIBE and make it the subscriber’s to choose; draft-12 moved the field to SUBSCRIBE_OK and made it the publisher’s. An argument here would therefore do nothing on nine of the drafts, and a fixed value would collide the moment a caller subscribed to a second track.

So the value is read off the endpoint — Connection::next_free_track_alias, the lowest alias no live binding holds — rather than asked of the caller. That table is the same one the endpoint checks before writing, so a caller mixing these calls with a draft’s own Connection::subscribe and aliases of its own is correct by construction rather than by convention, and a genuine duplicate is still refused with EndpointError::TrackAliasInUse before anything reaches the wire.

There is no range to get wrong here: a subscription with no filter is one that starts where the draft says it starts, and nothing is converted. The two filters that name a Start Location — AbsoluteStart and AbsoluteRange — are refused by this call on every draft that takes a Filter Type as an argument, because it has no start location to put beside them; AnyConnection::subscribe_range is the entry point that takes one. A draft-20 caller that wants a Location filter, a fill, or anything else from Section 10.2 reaches draft20::connection::Connection::subscribe through the variant with the parameters it wants.

The returned AnyRequest must be held while the request is live: on drafts 17-21 it owns the bidirectional stream the request went out on and dropping it cancels the subscription. See AnyRequest for how the two kinds of handle differ.

Source

pub async fn subscribe_range( &mut self, namespace: TrackNamespace, track_name: Vec<u8>, subscriber_priority: u8, group_order: GroupOrder, range: SubscribeRange, ) -> Result<AnyRequest, AnyConnectionError>

Send a SUBSCRIBE that names where the subscription starts, and optionally where it stops. Wired on every draft this build carries.

This is the half of SUBSCRIBE AnyConnection::subscribe cannot reach. The two filters that ask a relay for anything it has already carried — AbsoluteStart and AbsoluteRange — both put a Start Location on the wire, and a call taking the Filter Type beside the other arguments has none to give. So subscribe refuses them and this takes a SubscribeRange instead, from which the Filter Type is derived: the message cannot name a filter whose fields it does not carry, because nothing here gets to name one.

Without it there is no way to ask whether a relay holds a cache at all, which is a question about relays and not about ranges.

§What each draft is handed

Four wire shapes for one range, and the conversions are SubscribeRange’s rather than each arm’s:

  • draft-07 — a Start Location and an End Location, whose Object is the last one plus 1 with 0 for the whole Group, exactly as FETCH words it. SubscribeRange::inline_end_location.
  • drafts 08 through 14 — a Start Location and an absolute End Group, the End Object having been deleted in draft-08.
  • drafts 15 and 16 — the same two, moved into the SUBSCRIPTION_FILTER parameter draft-15 introduced.
  • drafts 17 through 19 — the same parameter, with the End Group written as a delta from the start.
  • draft-20LOCATION_FILTER, whose shape comes from its field count rather than from a Filter Type, and whose ranges are inclusive. SubscribeRange::location_filter_draft20, which is also where the one value this facade refuses on one draft is documented.
§What this does not carry

An empty parameter list on every draft that has one beside the filter, and the same priority and group order defaults AnyConnection::subscribe passes: those are fields of SUBSCRIBE on drafts 07 through 14 and parameters on drafts 15 and later, so they are taken here for the drafts that have the fields and ignored by the six that do not — which is the arrangement subscribe already documents.

§Errors

A range the negotiated draft cannot express, before anything is written. There are three, and each is a genuine difference between the drafts rather than a limitation of this call: SubscribeEnd::ThroughObject on drafts 08 through 19; an end_group below start_group on drafts 17 through 20; and a {0, 0} SubscribeEnd::Open on draft-20, which reads as the live edge there and as the beginning of the track everywhere else.

The returned AnyRequest must be held while the request is live: on drafts 17-21 it owns the bidirectional stream the request went out on and dropping it cancels the subscription.

Source

pub async fn fetch( &mut self, namespace: TrackNamespace, track_name: Vec<u8>, range: FetchRange, ) -> Result<AnyRequest, AnyConnectionError>

Send a standalone FETCH for range. Wired on every draft this build carries.

§What the range means here

FetchEnd::Object is the last Object the fetch covers, and the range holds it. FetchEnd::EntireGroup covers the whole end Group. end_group is absolute. That is the whole contract, and it is stated in FetchEnd as well because it is the one thing about this call a caller can get wrong without being told.

The drafts do not agree, which is why the argument is a FetchRange rather than four numbers. Drafts 07 through 19 carry the start and the end inline in FETCH and all thirteen word the end alike — draft-19 Section 10.12.1: “The end Location, plus 1. A Location.Object value of 0 means the entire group is requested.” The section number moves between drafts and the fields are regrouped into a Location along the way; the sentence does not change, which is why moqtap_codec::types::check_location_range is one shared function rather than thirteen. Draft-20 Section 10.13 deleted both fields and moved the range into the LOCATION_FILTER parameter, whose ranges Section 5.1.2 calls inclusive — the + 1 and the 0-means-whole- group convention are both gone, and neither deletion is in the draft’s own change log. A single end_object: u64 at this boundary would have meant one of those two things and looked like the other.

The conversion is FetchRange::inline_end_object for the first group and FetchRange::location_filter_draft20 for draft-20. The + 1 exists in exactly one place, the first of those, so it cannot reach draft-20 by being ported.

§What this does not carry

An empty parameter list, on every draft that has one — drafts 07 through 11 have no parameter field on FETCH at all. Drafts 07 through 14 carry subscriber priority and group order as fields of FETCH and no later draft does, so those arms send the defaults rather than widening an entry point shared with six drafts that have no such fields. Reach a draft’s own Connection::fetch through the variant for anything past a plain range.

§Errors

A range the negotiated draft cannot express, before anything is written: FetchEnd::Object(u64::MAX) on drafts 14 through 19, and an end_group below start_group on draft-20. See the two conversions for why each is inexpressible rather than merely unusual.

The returned AnyRequest must be held while the request is live: on drafts 17-21 it owns the bidirectional stream the request went out on and dropping it cancels the fetch.

Source

pub async fn fetch_joining( &mut self, joining_request_id: VarInt, start: JoiningStart, ) -> Result<AnyRequest, AnyConnectionError>

Send a Joining FETCH against a subscription this session already holds. Drafts 08 through 19.

AnyConnection::fetch’s other half, and a different question rather than a shorthand for the same one. A standalone FETCH names a track and a range and asks a relay’s store for it. A Joining FETCH names a subscription and asks for the part of that subscription’s track that precedes it, and the publisher fills in the namespace, the name and the end from the subscription itself. So a subscriber that wants “what I am watching, plus the run-up to it” has one request for the pair instead of a subscription and a fetch whose range it had to compute — and, on a live track, could not compute, because the run-up ends wherever the subscription happened to start.

joining_request_id is the Request ID of that subscription, which is AnyRequest::request_id on the handle AnyConnection::subscribe returned. Nothing here checks that it names one: draft-19 Section 10.12.2 puts that check at the publisher — “it MUST respond with a Fetch Error with code Invalid Joining Request ID” — and this side is the subscriber. The subscription must still be live when the FETCH arrives, which is a thing about ordering rather than about this call.

start is JoiningStart, and it carries the Fetch Type as well as the number for the reason that type documents.

§The two drafts at the ends, and what each of them deleted

Draft-07 has no Joining Fetch at all. Its FETCH has no Fetch Type field, so there is no bit in the message that could ask for one; the field and the second Fetch Type both arrive in draft-08.

Draft-20 deleted the whole mechanism — Section 10.13 removed the Fetch Type field, both payload structures and the Fetch Type registry together, and promoted the namespace and the name to fields of FETCH itself. There is no joining form to fall back to and no parameter that restores one, so this refuses on draft-20 rather than sending something adjacent. This is the entry point that makes “one suite run per draft” concrete: a relay speaking both 14 and 20 answers this question on one of them and cannot be asked it on the other, and a probe that tested only the newest draft would never learn that the relay implements it.

Drafts 08 through 10 carry only the relative form, so JoiningStart::Group is refused there — see that variant.

§What this does not carry

The same three things AnyConnection::fetch leaves out, for the same reasons: an empty parameter list on every draft that has one (drafts 08 through 11 have no parameter field on FETCH), and the subscriber priority and group order as defaults on drafts 08 through 14, which are the only drafts carrying them as fields of FETCH.

The returned AnyRequest must be held while the fetch is live: on drafts 17 through 19 it owns the bidirectional stream the request went out on and dropping it cancels the fetch.

Source

pub async fn track_status( &mut self, namespace: TrackNamespace, track_name: Vec<u8>, ) -> Result<AnyRequest, AnyConnectionError>

Send a TRACK_STATUS query for the given track. Supported on drafts 11 through 20. From draft-15 on, passes an empty parameter list.

§Which message this sends, and why the name moved

Up to draft-12 the query is TRACK_STATUS_REQUEST and track_status is the response to it; draft-13 renamed the request to TRACK_STATUS and gave the answer its own TRACK_STATUS_OK. So this method reaches Connection::track_status_request on drafts 11 and 12 and Connection::track_status from draft-13 on — the same question under two names, not the same name for two things. Calling the same-named method on a draft-11 Connection would send a reply to a question nobody asked, which is why the naming test an_unsent_request_is_not_named_from_the_drafts_table exists.

§Drafts 07 through 10

Not supported here, and not for want of a match arm. Their TRACK_STATUS_REQUEST carries no Request IDConnection:: track_status_request returns () on those drafts, because the answer is matched by track namespace and name rather than by an identifier. AnyRequest is a handle to a request the endpoint numbered, so there is nothing for this method to return, and fabricating an ID would put a number in AnyRequest::request_id that was never on the wire. Send the query through the variant’s own Connection and read the reply with AnyConnection::recv_response against any other outstanding request, or match it by name off recv_and_dispatch.

The returned AnyRequest must be held until the answer arrives: on drafts 17-21 it owns the bidirectional stream the query went out on and dropping it cancels the query.

Source

pub async fn subscribe_namespace( &mut self, namespace_prefix: TrackNamespace, ) -> Result<AnyRequest, AnyConnectionError>

Send a SUBSCRIBE_NAMESPACE (or SUBSCRIBE_ANNOUNCES on drafts 11–12). Supported on drafts 11 through 20. Drafts 16 and 17 pass default subscribe options; every draft from 12 on passes an empty parameter list.

From draft-18 this is the renumbered SUBSCRIBE_NAMESPACE (0x50), which asks for NAMESPACE and NAMESPACE_DONE only. SUBSCRIBE_TRACKS, the other half of the draft-18 split, has no entry point here — reach a draft’s own Connection::subscribe_tracks through the variant.

The returned AnyRequest must be held while the request is live: on drafts 17-21 it owns the bidirectional stream the request went out on and dropping it cancels the namespace subscription.

Source

pub async fn publish_namespace( &mut self, namespace: TrackNamespace, ) -> Result<AnyRequest, AnyConnectionError>

Offer a namespace to the peer: PUBLISH_NAMESPACE, or ANNOUNCE on the drafts that called it that. Supported on drafts 11 through 20. From draft-12 on, passes an empty parameter list.

This is the first entry point here that asks the peer to hold state rather than to answer a question and forget it. A relay that accepts it records this session as the publisher for that namespace and will route matching subscriptions back down this connection, so a caller that announces owes the peer either a withdrawal — see AnyConnection::publish_namespace_done — or a closed session.

§One request under two names

Drafts 07 through 13 call it ANNOUNCE; draft-14 renamed it PUBLISH_NAMESPACE and every later draft keeps that name. The rename is the whole of the difference — the same namespace goes out and the same acceptance comes back — so this is one method rather than two, on the same reasoning as AnyConnection::track_status, where the request changed names in the other direction.

§Drafts 07 through 10

Not supported here, and for the reason drafts 07 through 10 have no AnyConnection::track_status either: their ANNOUNCE carries no Request ID. Connection::announce returns () on those four drafts because ANNOUNCE_OK is matched by track namespace rather than by an identifier, so there is nothing for this method to hand back and a fabricated ID would put a number in AnyRequest::request_id that was never on the wire. Draft-11 is where the request ID arrives, and where this method starts. Send the announcement through the variant’s own Connection if an older draft is the target.

The returned AnyRequest must be held while the announcement is live: on drafts 17-21 it owns the bidirectional stream the request went out on and dropping it withdraws the namespace, which on those drafts is the only way to withdraw one.

Source

pub async fn publish_namespace_done( &mut self, request: &AnyRequest, namespace: TrackNamespace, ) -> Result<(), AnyConnectionError>

Withdraw a namespace this session announced: PUBLISH_NAMESPACE_DONE, or UNANNOUNCE on the drafts that called it that. Drafts 11 through 16.

§Why this takes both the handle and the namespace

Because the drafts disagree about which of the two identifies the announcement being withdrawn, and they disagree twice:

  • Drafts 11 through 15 name the namespace. UNANNOUNCE and, from draft-14, PUBLISH_NAMESPACE_DONE carry the tuple itself.
  • Draft-16 names the request ID, having moved the whole message onto the identifier the announcement was allocated.
  • Drafts 17 through 20 name neither, because there is no message: the announcement lives exactly as long as its bidirectional stream, so withdrawing one is AnyRequest::cancel or simply dropping the handle. Those four return an error here rather than silently doing nothing, on the same terms as AnyConnection::unsubscribe — it is not a gap waiting to be wired, there is nothing to send.

A signature taking only the namespace would be wrong on draft-16 and one taking only the handle would be wrong on the five drafts before it. Taking both keeps the caller from having to know which era it is in, which is the entire point of this facade.

request is borrowed rather than consumed: on drafts 11 through 16 it carries no stream, so the caller keeps a handle that is still good for AnyRequest::request_id afterwards.

Source

pub async fn recv_inbound(&mut self) -> Result<AnyArrival, AnyConnectionError>

Wait for the peer to send something, and say whether it is a request this facade can answer. Supported on every draft this build carries.

This is the direction AnyConnection::recv_response does not cover: a relay that accepted a namespace from AnyConnection::publish_namespace will send a SUBSCRIBE down this connection when somebody asks for a track under it, and that message answers nothing this side asked for.

§What “wait” means on either side of draft-17

Two different waits for one question. Drafts 07 through 16 read the shared control stream, so every message the peer sends — its requests and its answers to this side’s — arrives through here and is returned, tagged. From draft-17 this waits on a new bidirectional stream, so only the peer’s own requests arrive and an answer to something this side asked is read with AnyConnection::recv_response instead.

That is why AnyArrival::Other exists rather than a filter. On the older drafts a reader that dropped what it was not looking for would swallow a SUBSCRIBE_OK somebody was waiting on; see that variant for what it costs on the newer ones.

Every message is dispatched into the endpoint before it is returned, so the session’s state is correct whether the caller inspects it or not.

Source

pub async fn accept_subscribe( &mut self, request: &mut AnyInboundRequest, track_alias: VarInt, ) -> Result<(), AnyConnectionError>

Answer a peer’s SUBSCRIBE with SUBSCRIBE_OK. Supported on every draft this build carries.

request is the handle from AnyArrival::Subscribe. What the unified shape cannot express is defaulted: no expiry, ascending group order, and no parameters, properties or track extensions — every draft’s own Connection::subscribe_ok is reachable through the variant for a responder that wants any of them.

§The Track Alias, and why it is an argument here

The opposite of AnyConnection::subscribe, and not by inconsistency. Sending a SUBSCRIBE, the alias is bookkeeping the caller has no opinion about — any free one names the track locally — so it is read off the endpoint. Answering one, the alias is what this side’s objects will carry on the wire, and Sections 9.8 and 9.13 require one alias to name one track: two subscriptions to the same track must be answered with the same alias, and a fresh one per subscription would be wrong. The endpoint cannot know which track a caller considers this to be, so the choice is the caller’s and cannot be inferred.

Drafts 07 through 11 ignore it. Those drafts put the Track Alias on SUBSCRIBE and make it the subscriber’s, so their SUBSCRIBE_OK has no such field and the value that counts already arrived — it is on the message in AnyArrival::Subscribe. The argument is accepted and dropped there rather than the signature splitting in two.

§Drafts 07 through 10 have an arm here and no way to be reached

Not a gap in this call. A request is legal only below the ceiling its recipient granted, and a ceiling nobody granted is zero, which forbids every request. Drafts 11 and later let this side grant one in CLIENT_SETUP, as Setup Parameter 0x02; drafts 07 through 10 grant it with the MAX_SUBSCRIBE_ID message, type 0x15, and no Connection in this crate has an entry point for sending one. So a peer’s SUBSCRIBE on those four drafts is refused for exceeding a ceiling of zero long before it reaches here.

The arms stay because they are right and become reachable the day that message can be sent, without being touched.

Source

pub async fn open_subgroup( &self, track_alias: u64, group_id: u64, subgroup_id: u64, publisher_priority: u8, ) -> Result<AnySubgroupWriter, AnyConnectionError>

Open a subgroup stream and frame it, ready for objects.

The first thing a relay can be asked that is not a question about control messages. Answering SUBSCRIBE is not delivering a track, and until something puts an object on the wire there is no way to tell the two apart from outside.

§The header this writes, and the four shapes it takes

An explicit Subgroup ID, no extension block where a draft can say so, and a publisher priority on the wire — the plainest conforming subgroup stream each draft can carry. The struct that says so is different four times over:

  • Drafts 07 through 10 put the stream type outside the header entirely, so the header is four fields and nothing selects a layout.
  • Drafts 11 through 13 name a StreamType variant per layout, and SubgroupExplicit is the one that carries an ID and no extensions.
  • Draft-14 folds the type into the header as a flag word, and the Subgroup ID becomes an Option that must agree with it — a type saying a field follows, with no field, is refused.
  • Drafts 15 through 20 replace the flags with a type byte: 0x14 is the subgroup base 0x10, plus 0x04 for an explicit Subgroup ID, with the extension bit 0x01 clear, the end-of-group bit 0x08 clear, and the no-priority bit 0x20 clear. Drafts 15 and 16 read those two ID bits as a pair of table columns and drafts 17 through 20 call them a SUBGROUP_ID_MODE field, which is a difference in wording and not in bytes.
§Why the extension block is not an argument

It is not a property of any object, so there is nothing an object-level caller could be asked. The header settles it for the whole stream, and an object that disagreed with its header would misframe every object after it — the missing length is read out of the next field along. A caller that needs to put extensions on a stream needs to say so when the stream opens, which is a second entry point rather than an argument here.

Three drafts do not offer the choice at all, and in opposite directions. Draft-07 has no extension block anywhere, so its streams carry none because there is none to carry. Drafts 08 through 10 have one on every object and no header field that could say otherwise: their subgroup header is four values with no type byte, so every object writes a length whether it has anything to put after it or not. On those three moqtap_codec::dispatch::AnySubgroupHeader::carries_extension_block answers without consulting the header, and a caller asking for a stream without one is asking for a stream the draft does not define.

Nothing checks that track_alias names a track this session agreed to publish, because a probe measuring a relay may want to send objects for one it did not.

Source

pub async fn accept_subgroup( &self, ) -> Result<(AnySubgroupHeader, AnySubgroupReader), AnyConnectionError>

Accept the next subgroup stream the peer opens, and read its header.

The header comes back beside the reader because it is the only place several things are said: which track the objects belong to (by Track Alias), which group, and — through carries_extension_block — whether the objects on this stream write an extension block at all. A reader that did not know the last of those could not frame a single object.

Reading the header is not separable from accepting the stream, and the per-draft connections do both in one call for a reason beyond convenience: the header settles the track’s forwarding preference and binds the stream to the endpoint’s object bookkeeping for that alias, so a stream accepted without its header read would be a stream whose objects nothing is measuring.

This waits on the next unidirectional stream, whatever it carries. A draft that sends something else on one — and every draft does, for fetches — will fail to parse a subgroup header out of it, which is the same wall AnyConnection::accept_fetch hits from the other side. Nothing here reads the stream type first and branches: a caller that could receive either has to know which it is expecting, because the header decides how every object after it is framed.

Source

pub async fn accept_fetch( &self, group_order: GroupOrder, ) -> Result<(AnyFetchHeader, AnyFetchReader), AnyConnectionError>

Accept the next fetch stream the peer opens, and read its FETCH_HEADER.

AnyConnection::accept_subgroup’s twin, and it waits on the same queue: the next unidirectional stream, whatever it carries. A subgroup stream arriving here fails to parse as a FETCH_HEADER, and a fetch stream arriving there fails the other way. A caller reading both kinds on one session has to know which is next, and this facade will not guess for it — see accept_subgroup for why guessing is the wrong shape.

The header comes back because it names the request: everything on the stream after it answers the FETCH whose Request ID it carries, and a caller with more than one fetch in flight has no other way to tell the streams apart.

§Why this takes a Group Order and accept_subgroup takes nothing

Because on four drafts the reader cannot be started without it, and starting it wrong is silent. Drafts 18 through 21 encode an object’s Group ID as a delta, and draft-18 Section 11.4.4.1 makes that delta count upward under Ascending and downward under Descending. A reader started in the wrong direction still parses every frame and reports Group IDs that walk the wrong way — the same trap Connection::accept_fill_stream documents on draft-20, where the endpoint happens to know the answer and this facade does not.

Here nothing on this side holds it: the order is on the FETCH_OK, which is a control message a data stream never sees, and which the caller has already read. fetch_group_order takes it off that message so the lookup is written once rather than per caller.

On the other drafts the argument is inert, and it is an argument rather than an Option because a caller that has a FETCH_OK in hand can always answer it, and one that cannot has not read the answer yet.

Source

pub async fn subscribe_update( &mut self, subscription_request_id: VarInt, start_location: Location, end_group: VarInt, subscriber_priority: u8, forward: Forward, ) -> Result<(), AnyConnectionError>

Send a SUBSCRIBE_UPDATE for an active subscription. Draft-14 only.

§Why no later draft is wired, and why that is not this call’s to fix

Draft-15 renamed the message REQUEST_UPDATE and rebuilt it, and from draft-17 it travels on the request’s own bidirectional stream rather than on a control stream — so an update needs the AnyRequest the original request returned, which this signature does not take and cannot be given without becoming a different call. Draft-20 goes further and has no start_location / end_group pair at all: Section 10.9 carries the new range as a LOCATION_FILTER parameter, on the same inclusive terms FetchRange describes. Reach a draft’s own Connection::send_on_request_stream through the variant.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more