1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
//! The **one** axum adapter that turns a [`media_plane::egress::ServedEgress`]
//! resolution into a real HTTP response (plan step 5b) — every output/route
//! that resolves against a [`crate::route::RouteHandle`] goes through the two
//! functions here (`resolve_blocking` + `into_response`), rather than each
//! hand-rolling its own blocking-reload wait loop and status-code mapping.
//!
//! `ServedEgress` is sans-IO by design (`media_plane::egress`'s own module
//! doc): no `axum`, no `tokio`, no HTTP type appears in it. This module is
//! the one place that bridges it to axum — [`crate::output::llhls`]'s
//! playlist route, the shared init/segment/part resource route
//! ([`crate::origin::resource`]), and [`crate::output::dash`]/
//! [`crate::output::ll_dash`]'s manifest routes all call the same two
//! functions below rather than each reimplementing the wait loop or the
//! `EgressResponse` -> `Response` mapping. If a future output route is
//! tempted to hand-roll HTTP handling instead of calling these, that
//! reintroduces exactly the duplication this design exists to delete.
use Arc;
use ;
use StatusCode;
use ;
use Timestamp;
use Trunk;
use ;
use crateProgramServing;
/// Upper bound on how long [`resolve_blocking`] parks a caller waiting for a
/// [`ServedEgress::resolve`] to stop answering [`EgressResponse::Await`] — no
/// call site may wait longer than this.
pub const BLOCKING_RELOAD_TIMEOUT: Duration = from_secs;
/// Resolves `route`'s single-program-route [`ProgramServing`] bundle
/// ([`crate::route::SPTS_PROGRAM_ID`]) against its registry, handling the
/// three-way [`crate::route::ProgramResolution`] the same way at every
/// migrated egress call site (issue #805 tasks 3/4/6): `Found` hands back the
/// whole bundle (`Trunk` + `HlsOrigin` + `DashState`, resolved together from
/// one registry read, so a caller never risks reading one program's `Trunk`
/// against a different program's `HlsOrigin`); `NotYetAnnounced` (the route
/// is connected but no program has appeared yet — ingest may still be
/// dialing/handshaking) is a `503 Service Unavailable` "not ready", **not** a
/// `404` — collapsing the two would make an ordinary route mid-connect
/// indistinguishable from a client hitting a route that will never exist,
/// exactly the failure mode [`crate::route::ProgramResolution`]'s own doc
/// exists to prevent; `NotFound` (a genuinely different, permanent absence)
/// is a real `404`.
///
/// Every caller (`crate::output::llhls::media_playlist`,
/// `crate::output::dash::manifest`, `crate::output::ll_dash::manifest`,
/// `crate::origin::resource::dynamic_file`/`fetch_part`) matches this
/// `Result` once, up front, before touching `resolve_blocking`/`ll_hls()` at
/// all — so neither of those needs its own "no program yet" branch.
pub
/// Fallback poll interval when [`Trunk::listen`] has no free waiter slot
/// (`media_plane::trunk::TrunkConfig::part_capacity` already saturated by
/// other parked requests) — bounded by the caller's own `timeout` regardless,
/// so a burst of blocking requests beyond the slot cap degrades to polling
/// rather than either busy-spinning or leaking an unbounded wait.
const NO_SLOT_BACKOFF: Duration = from_millis;
/// Resolve `request` against `egress`, holding a blocking-reload-style wait
/// (RFC 8216bis §6.2.5.2) up to `timeout` while [`ServedEgress::resolve`]
/// answers [`EgressResponse::Await`] — the caller-driven wait loop
/// `hls_runtime::server`'s own module doc sketches, generalised to any
/// `ServedEgress` implementation (LL-HLS's [`hls_runtime::server::HlsOrigin`],
/// or this crate's own DASH/LL-DASH manifest origins).
///
/// A `Trunk::listen()` wake-up is registered **before** re-checking
/// `resolve` (never after) — the same ordering [`Trunk::listen`]'s own docs
/// require, so a publish racing between the first `Await` and the
/// registration is never missed. The fast path (the overwhelmingly common
/// case: a request that resolves immediately, e.g. every DASH/LL-DASH
/// manifest call, which never answers `Await` at all) never touches
/// `Trunk::listen` — only a request that has genuinely seen `Await` twice in
/// a row (once before, once after registering) commits to an actual wait,
/// via `on_enter_wait` (invoked at most once, the first time this call
/// commits to parking) — callers that want an observability gauge around the
/// wait (e.g. `crate::origin::resource::BlockingRequestGuard`) construct it
/// there; callers that never genuinely wait (DASH/LL-DASH) pass a no-op.
///
/// Bounded twice over: by `timeout` (an absolute deadline, composed via
/// [`AwaitPolicy`] so `resolve` itself stops answering `Await` once it
/// passes — see [`EgressResponse::pending`]'s own doc), and by
/// [`Trunk::listen`]'s own waiter-slot cap (a burst of requests beyond it
/// falls back to a capped poll rather than parking unboundedly) — this is
/// the concrete mechanism satisfying "bound anything that accumulates,
/// including parked `Await` requests".
pub async
/// Turn one [`EgressResponse`] into an HTTP [`Response`] — the one place in
/// this crate that does so. `render_ready` supplies the content-type/body
/// for [`EgressResponse::Ready`] (protocol-specific: LL-HLS's `HlsBody`'s
/// two shapes, or a plain MPD `String`); `not_found_status` is the one
/// per-route status choice this adapter still leaves to its caller — a
/// resource byte-range that will never exist is a `404` (gone forever), but
/// a DASH/LL-DASH manifest that cannot yet be described at all (issue #776:
/// no track with a derivable codec string, not just "no segment has closed
/// yet") is a `503` (may become available once a usable track lands) — see
/// each call site.
pub